Split into category packages; module github.com/ZacharyZhang-NY/MujicaUI
This commit is contained in:
1822 files changed
+24568
-21314
No files matched your search
+26
-22
@@ -7,9 +7,13 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/icons"
|
||||
"mujicaui/theme"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/feedback"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/layout"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/navigation"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||
)
|
||||
|
||||
// event is one line of the gallery's event log.
|
||||
@@ -65,16 +69,16 @@ func (g *gallery) logFor(id string) func(string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (g *gallery) settings() mujica.Settings {
|
||||
s := mujica.Settings{Mode: mujica.Mode(g.mode), Density: densities[g.density], Locale: mujica.EnUS}
|
||||
func (g *gallery) settings() core.Settings {
|
||||
s := core.Settings{Mode: core.Mode(g.mode), Density: densities[g.density], Locale: core.EnUS}
|
||||
if g.zh {
|
||||
s.Locale = mujica.ZhCN
|
||||
s.Locale = core.ZhCN
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (g *gallery) view(c *ui.Context) {
|
||||
mujica.Use(c, g.settings())
|
||||
core.Use(c, g.settings())
|
||||
w, _ := c.Size()
|
||||
if narrow := w < 900; narrow != g.autoFold {
|
||||
g.autoFold, g.folded = narrow, narrow
|
||||
@@ -84,7 +88,7 @@ func (g *gallery) view(c *ui.Context) {
|
||||
scroll = &ui.ScrollState{}
|
||||
g.scrolls[g.selected] = scroll
|
||||
}
|
||||
mujica.AppShell(c, &g.folded, mujica.AppShellOptions{
|
||||
layout.AppShell(c, &g.folded, layout.AppShellOptions{
|
||||
TitleBar: func() { g.titleBar(c) },
|
||||
Sidebar: func() { g.sidebar(c) },
|
||||
Content: func() { g.content(c) },
|
||||
@@ -92,33 +96,33 @@ func (g *gallery) view(c *ui.Context) {
|
||||
ContentScroll: scroll,
|
||||
})
|
||||
g.commandPalette(c)
|
||||
mujica.Toaster(c)
|
||||
feedback.Toaster(c)
|
||||
}
|
||||
|
||||
func (g *gallery) titleBar(c *ui.Context) {
|
||||
mujica.TitleBar(c, "MujicaUI", mujica.TitleBarOptions{Trailing: func() {
|
||||
layout.TitleBar(c, "MujicaUI", layout.TitleBarOptions{Trailing: func() {
|
||||
ui.Row(c).Gap(8).AlignItems(ui.Center).Children(func() {
|
||||
mode := g.mode
|
||||
if mujica.SegmentedControl(c, &g.mode, []mujica.Segment{
|
||||
if navigation.SegmentedControl(c, &g.mode, []navigation.Segment{
|
||||
{Label: g.t("System", "跟随系统"), Icon: icons.Must("settings"), HideLabel: true},
|
||||
{Label: g.t("Light", "浅色"), Icon: icons.Must("sun"), HideLabel: true},
|
||||
{Label: g.t("Dark", "深色"), Icon: icons.Must("moon"), HideLabel: true},
|
||||
}, mujica.SegmentedControlOptions{Label: g.t("Theme", "主题")}).Changed() && mode != g.mode {
|
||||
g.logFor("shell")("theme " + mujica.Mode(g.mode).String())
|
||||
}, navigation.SegmentedControlOptions{Label: g.t("Theme", "主题")}).Changed() && mode != g.mode {
|
||||
g.logFor("shell")("theme " + core.Mode(g.mode).String())
|
||||
g.overture.switches++
|
||||
}
|
||||
density := g.density
|
||||
if mujica.SegmentedControl(c, &g.density, []mujica.Segment{
|
||||
if navigation.SegmentedControl(c, &g.density, []navigation.Segment{
|
||||
{Label: g.t("Compact", "紧凑")}, {Label: g.t("Default", "默认")}, {Label: g.t("Comfortable", "宽松")},
|
||||
}, mujica.SegmentedControlOptions{Label: g.t("Density", "密度")}).Changed() && density != g.density {
|
||||
}, navigation.SegmentedControlOptions{Label: g.t("Density", "密度")}).Changed() && density != g.density {
|
||||
g.logFor("shell")("density " + densities[g.density].String())
|
||||
g.overture.switches++
|
||||
}
|
||||
if mujica.Button(c, g.t("中文", "English"), mujica.ButtonOptions{Variant: mujica.Ghost}).Clicked() {
|
||||
if input.Button(c, g.t("中文", "English"), input.ButtonOptions{Variant: input.Ghost}).Clicked() {
|
||||
g.zh = !g.zh
|
||||
g.logFor("shell")(fmt.Sprintf("locale %s", g.settings().Locale))
|
||||
}
|
||||
if mujica.IconButton(c, icons.Must("command"), g.t("Command palette", "命令面板"), mujica.IconButtonOptions{Variant: mujica.Ghost}).Clicked() {
|
||||
if input.IconButton(c, icons.Must("command"), g.t("Command palette", "命令面板"), input.IconButtonOptions{Variant: input.Ghost}).Clicked() {
|
||||
g.palette = true
|
||||
}
|
||||
})
|
||||
@@ -126,16 +130,16 @@ func (g *gallery) titleBar(c *ui.Context) {
|
||||
}
|
||||
|
||||
func (g *gallery) statusBar(c *ui.Context) {
|
||||
left := []mujica.StatusItem{
|
||||
left := []layout.StatusItem{
|
||||
{ID: "count", Text: fmt.Sprintf(g.t("%d components", "%d 个组件"), len(g.pages)), Icon: icons.Must("layout-grid")},
|
||||
{ID: "look", Text: fmt.Sprintf("%s · %s", mujica.Mode(g.mode), densities[g.density]), Secondary: true},
|
||||
{ID: "look", Text: fmt.Sprintf("%s · %s", core.Mode(g.mode), densities[g.density]), Secondary: true},
|
||||
}
|
||||
var right []mujica.StatusItem
|
||||
var right []layout.StatusItem
|
||||
if n := len(g.events); n > 0 {
|
||||
e := g.events[n-1]
|
||||
right = append(right, mujica.StatusItem{ID: "last", Text: e.page + " · " + e.text, Icon: icons.Must("bell"), Secondary: true})
|
||||
right = append(right, layout.StatusItem{ID: "last", Text: e.page + " · " + e.text, Icon: icons.Must("bell"), Secondary: true})
|
||||
}
|
||||
mujica.StatusBar(c, mujica.StatusBarOptions{Left: left, Right: right})
|
||||
layout.StatusBar(c, layout.StatusBarOptions{Left: left, Right: right})
|
||||
}
|
||||
|
||||
func (g *gallery) content(c *ui.Context) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package main
|
||||
|
||||
import "github.com/egoist/mygo/ui"
|
||||
import (
|
||||
"github.com/egoist/mygo/ui"
|
||||
)
|
||||
|
||||
// page is one component's gallery entry.
|
||||
type page struct {
|
||||
|
||||
@@ -11,9 +11,15 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/icons"
|
||||
"mujicaui/theme"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/feedback"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/layout"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/navigation"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/overlay"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||
)
|
||||
|
||||
const filesID = "c-files"
|
||||
@@ -30,8 +36,8 @@ type filesDemo struct {
|
||||
next int
|
||||
version int
|
||||
query string
|
||||
table mujica.DataTableState[int]
|
||||
dropped []mujica.DroppedFile
|
||||
table data.DataTableState[int]
|
||||
dropped []input.DroppedFile
|
||||
picked []string
|
||||
renaming bool
|
||||
renameID int
|
||||
@@ -68,14 +74,14 @@ func fileSize(n int64) string {
|
||||
}
|
||||
}
|
||||
|
||||
func (g *gallery) fileColumns(c *ui.Context) []mujica.DataColumn[fileEntry] {
|
||||
return []mujica.DataColumn[fileEntry]{
|
||||
func (g *gallery) fileColumns(c *ui.Context) []data.DataColumn[fileEntry] {
|
||||
return []data.DataColumn[fileEntry]{
|
||||
{ID: "name", Title: g.t("Name", "名称"), MinWidth: 200, Sortable: true,
|
||||
Text: func(f fileEntry) string { return f.Name },
|
||||
Compare: func(a, b fileEntry) int { return cmp.Compare(a.Name, b.Name) },
|
||||
Cell: func(f fileEntry) {
|
||||
ui.Row(c).Gap(8).AlignItems(ui.Center).MinWidth(0).Children(func() {
|
||||
ui.Icon(c, icons.Must("file")).Size(16, 16).TextColor(mujica.Tokens(c).TextMuted)
|
||||
ui.Icon(c, icons.Must("file")).Size(16, 16).TextColor(core.Tokens(c).TextMuted)
|
||||
ui.Text(c, f.Name).SingleLine()
|
||||
})
|
||||
}},
|
||||
@@ -121,39 +127,39 @@ func (s *filesDemo) add(path string, size int64, at time.Time) bool {
|
||||
|
||||
func (g *gallery) filesView(c *ui.Context, log func(string)) {
|
||||
s := &g.cFiles
|
||||
k := mujica.Tokens(c)
|
||||
k := core.Tokens(c)
|
||||
cols := g.fileColumns(c)
|
||||
targets := s.targets()
|
||||
ui.Row(c).FillWidth().Gap(12).AlignItems(ui.Center).Children(func() {
|
||||
mujica.Toolbar(c, mujica.ToolbarOptions{Label: g.t("File actions", "文件操作")}, func() {
|
||||
g.fileActions(c, log, targets, func(m *mujica.PopupMenu) { g.fileMenu(m, log, targets) })
|
||||
navigation.Toolbar(c, navigation.ToolbarOptions{Label: g.t("File actions", "文件操作")}, func() {
|
||||
g.fileActions(c, log, targets, func(m *overlay.PopupMenu) { g.fileMenu(m, log, targets) })
|
||||
}).Grow(1)
|
||||
before := s.query
|
||||
mujica.SearchInput(c, &s.query, mujica.SearchInputOptions{Placeholder: g.t("Filter files", "筛选文件")}).Element.Width(240)
|
||||
input.SearchInput(c, &s.query, input.SearchInputOptions{Placeholder: g.t("Filter files", "筛选文件")}).Element.Width(240)
|
||||
if s.query != before {
|
||||
log(fmt.Sprintf("filter %q", s.query))
|
||||
}
|
||||
})
|
||||
s.table.Filter = s.query
|
||||
shown := len(mujica.DataTableRows(s.entries, cols, s.table.Sort, s.query))
|
||||
shown := len(data.DataTableRows(s.entries, cols, s.table.Sort, s.query))
|
||||
switch {
|
||||
case len(s.entries) == 0:
|
||||
if mujica.EmptyState(c, mujica.EmptyStateOptions{Kind: mujica.EmptyNoData, Title: g.t("No files", "没有文件"),
|
||||
if feedback.EmptyState(c, feedback.EmptyStateOptions{Kind: feedback.EmptyNoData, Title: g.t("No files", "没有文件"),
|
||||
Description: g.t("Drop files below or choose some to add them.", "将文件拖到下方,或选取文件以添加。"),
|
||||
Action: g.t("Restore samples", "恢复示例")}).Action {
|
||||
g.restoreFiles(log)
|
||||
}
|
||||
case shown == 0:
|
||||
if mujica.EmptyState(c, mujica.EmptyStateOptions{Kind: mujica.EmptyNoResults, Query: s.query, Action: g.t("Clear filter", "清除筛选")}).Action {
|
||||
if feedback.EmptyState(c, feedback.EmptyStateOptions{Kind: feedback.EmptyNoResults, Query: s.query, Action: g.t("Clear filter", "清除筛选")}).Action {
|
||||
s.query = ""
|
||||
log("filter cleared")
|
||||
}
|
||||
default:
|
||||
v := mujica.DataTable(c, &s.table, s.entries, mujica.DataTableOptions[fileEntry, int]{
|
||||
v := data.DataTable(c, &s.table, s.entries, data.DataTableOptions[fileEntry, int]{
|
||||
Columns: cols, Key: func(f fileEntry) int { return f.ID }, Multiple: true, Version: s.version,
|
||||
})
|
||||
v.Element.FillWidth().Height(430)
|
||||
mujica.ContextMenu(c, v.Element, mujica.ContextMenuOptions{}, func(m *mujica.PopupMenu) { g.fileMenu(m, log, targets) })
|
||||
overlay.ContextMenu(c, v.Element, overlay.ContextMenuOptions{}, func(m *overlay.PopupMenu) { g.fileMenu(m, log, targets) })
|
||||
if v.Changed() {
|
||||
log(fmt.Sprintf("selection %d checked", s.table.Selection.Len()))
|
||||
}
|
||||
@@ -168,12 +174,12 @@ func (g *gallery) filesView(c *ui.Context, log func(string)) {
|
||||
for _, f := range s.entries {
|
||||
size += f.Size
|
||||
}
|
||||
mujica.StatusBar(c, mujica.StatusBarOptions{
|
||||
Left: []mujica.StatusItem{
|
||||
layout.StatusBar(c, layout.StatusBarOptions{
|
||||
Left: []layout.StatusItem{
|
||||
{ID: "count", Icon: icons.Must("folder"), Text: fmt.Sprintf(g.t("%d files · %s", "%d 个文件 · %s"), len(s.entries), fileSize(size))},
|
||||
{ID: "shown", Text: fmt.Sprintf(g.t("%d shown", "显示 %d 个"), shown), Secondary: true},
|
||||
},
|
||||
Right: []mujica.StatusItem{{ID: "selected", Text: fmt.Sprintf(g.t("%d selected", "已选 %d 个"), s.table.Selection.Len())}},
|
||||
Right: []layout.StatusItem{{ID: "selected", Text: fmt.Sprintf(g.t("%d selected", "已选 %d 个"), s.table.Selection.Len())}},
|
||||
}).Element.Radius(theme.CardRadius).Border(theme.BorderWidth, k.Border)
|
||||
g.fileAdders(c, log)
|
||||
g.renameDialog(c, log)
|
||||
@@ -181,39 +187,39 @@ func (g *gallery) filesView(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
// fileActions fills the toolbar; menu fills the More dropdown, shared with the context menu.
|
||||
func (g *gallery) fileActions(c *ui.Context, log func(string), targets []int, menu func(m *mujica.PopupMenu)) {
|
||||
mujica.ToolbarGroup(c, func() {
|
||||
if mujica.ToolbarButton(c, g.t("Rename", "重命名"), mujica.ToolbarButtonOptions{Icon: icons.Must("file-text"), ShowLabel: true, Disabled: len(targets) != 1}).Clicked() {
|
||||
func (g *gallery) fileActions(c *ui.Context, log func(string), targets []int, menu func(m *overlay.PopupMenu)) {
|
||||
navigation.ToolbarGroup(c, func() {
|
||||
if navigation.ToolbarButton(c, g.t("Rename", "重命名"), navigation.ToolbarButtonOptions{Icon: icons.Must("file-text"), ShowLabel: true, Disabled: len(targets) != 1}).Clicked() {
|
||||
g.startRename(log, targets[0])
|
||||
}
|
||||
if mujica.ToolbarButton(c, g.t("Delete", "删除"), mujica.ToolbarButtonOptions{Icon: icons.Must("trash-2"), ShowLabel: true, Disabled: len(targets) == 0}).Clicked() {
|
||||
if navigation.ToolbarButton(c, g.t("Delete", "删除"), navigation.ToolbarButtonOptions{Icon: icons.Must("trash-2"), ShowLabel: true, Disabled: len(targets) == 0}).Clicked() {
|
||||
g.startDelete(log, targets)
|
||||
}
|
||||
})
|
||||
mujica.ToolbarSeparator(c)
|
||||
mujica.DropdownMenu(c, g.t("Actions", "操作"), mujica.DropdownMenuOptions{Icon: icons.Must("ellipsis")}, menu)
|
||||
navigation.ToolbarSeparator(c)
|
||||
overlay.DropdownMenu(c, g.t("Actions", "操作"), overlay.DropdownMenuOptions{Icon: icons.Must("ellipsis")}, menu)
|
||||
}
|
||||
|
||||
// fileMenu holds every file action; the dropdown and the context menu share it.
|
||||
func (g *gallery) fileMenu(m *mujica.PopupMenu, log func(string), targets []int) {
|
||||
func (g *gallery) fileMenu(m *overlay.PopupMenu, log func(string), targets []int) {
|
||||
s := &g.cFiles
|
||||
if m.Item(g.t("Rename…", "重命名…"), mujica.PopupMenuItemOptions{Disabled: len(targets) != 1}) {
|
||||
if m.Item(g.t("Rename…", "重命名…"), overlay.PopupMenuItemOptions{Disabled: len(targets) != 1}) {
|
||||
g.startRename(log, targets[0])
|
||||
}
|
||||
if m.Item(g.t("Delete…", "删除…"), mujica.PopupMenuItemOptions{Disabled: len(targets) == 0}) {
|
||||
if m.Item(g.t("Delete…", "删除…"), overlay.PopupMenuItemOptions{Disabled: len(targets) == 0}) {
|
||||
g.startDelete(log, targets)
|
||||
}
|
||||
if m.Item(g.t("Select all", "全选"), mujica.PopupMenuItemOptions{Disabled: len(s.entries) == 0}) {
|
||||
if m.Item(g.t("Select all", "全选"), overlay.PopupMenuItemOptions{Disabled: len(s.entries) == 0}) {
|
||||
for _, f := range s.entries {
|
||||
s.table.Selection.Add(f.ID)
|
||||
}
|
||||
log(fmt.Sprintf("selected all %d", len(s.entries)))
|
||||
}
|
||||
if m.Item(g.t("Clear selection", "清除选择"), mujica.PopupMenuItemOptions{Disabled: s.table.Selection.Len() == 0}) {
|
||||
if m.Item(g.t("Clear selection", "清除选择"), overlay.PopupMenuItemOptions{Disabled: s.table.Selection.Len() == 0}) {
|
||||
s.table.Selection.Clear()
|
||||
log("selection cleared")
|
||||
}
|
||||
if m.Item(g.t("Restore samples", "恢复示例"), mujica.PopupMenuItemOptions{}) {
|
||||
if m.Item(g.t("Restore samples", "恢复示例"), overlay.PopupMenuItemOptions{}) {
|
||||
g.restoreFiles(log)
|
||||
}
|
||||
}
|
||||
@@ -229,16 +235,16 @@ func (g *gallery) restoreFiles(log func(string)) {
|
||||
// fileAdders are the drop zone and the picker; both only list files, nothing is uploaded.
|
||||
func (g *gallery) fileAdders(c *ui.Context, log func(string)) {
|
||||
s := &g.cFiles
|
||||
mujica.Card(c, mujica.CardOptions{Title: g.t("Add files", "添加文件")}, func() {
|
||||
ui.Text(c, g.t("Files are checked and listed, never read or uploaded.", "文件只做校验并列出,不会读取或上传。")).TextColor(mujica.Tokens(c).TextMuted)
|
||||
if mujica.FileDropZone(c, &s.dropped, mujica.FileDropZoneOptions{MaxFiles: 8, MaxSize: 50 << 20,
|
||||
data.Card(c, data.CardOptions{Title: g.t("Add files", "添加文件")}, func() {
|
||||
ui.Text(c, g.t("Files are checked and listed, never read or uploaded.", "文件只做校验并列出,不会读取或上传。")).TextColor(core.Tokens(c).TextMuted)
|
||||
if input.FileDropZone(c, &s.dropped, input.FileDropZoneOptions{MaxFiles: 8, MaxSize: 50 << 20,
|
||||
Hint: g.t("Drop files here to list them", "将文件拖到此处以列出")}).Changed() {
|
||||
for _, f := range s.dropped {
|
||||
g.addFile(c, log, f.Path, f.Size, time.Now())
|
||||
}
|
||||
s.dropped = nil
|
||||
}
|
||||
p := mujica.FilePicker(c, &s.picked, mujica.FilePickerOptions{Multiple: true, Label: g.t("Choose files…", "选取文件…")})
|
||||
p := input.FilePicker(c, &s.picked, input.FilePickerOptions{Multiple: true, Label: g.t("Choose files…", "选取文件…")})
|
||||
if p.Canceled() {
|
||||
log("picker canceled")
|
||||
}
|
||||
@@ -246,7 +252,7 @@ func (g *gallery) fileAdders(c *ui.Context, log func(string)) {
|
||||
for _, path := range s.picked {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
mujica.ShowToast(c, mujica.ToastMessage{Severity: mujica.SeverityError, Message: err.Error()})
|
||||
feedback.ShowToast(c, feedback.ToastMessage{Severity: core.SeverityError, Message: err.Error()})
|
||||
log("stat failed: " + err.Error())
|
||||
continue
|
||||
}
|
||||
@@ -263,7 +269,7 @@ func (g *gallery) addFile(c *ui.Context, log func(string), path string, size int
|
||||
return
|
||||
}
|
||||
log("skipped duplicate " + filepath.Base(path))
|
||||
mujica.ShowToast(c, mujica.ToastMessage{ID: "dup-" + path, Severity: mujica.SeverityWarning,
|
||||
feedback.ShowToast(c, feedback.ToastMessage{ID: "dup-" + path, Severity: core.SeverityWarning,
|
||||
Message: fmt.Sprintf(g.t("%s is already listed", "%s 已在列表中"), filepath.Base(path))})
|
||||
}
|
||||
|
||||
@@ -304,17 +310,17 @@ func (g *gallery) renameDialog(c *ui.Context, log func(string)) {
|
||||
s.version++
|
||||
s.renaming = false
|
||||
}
|
||||
mujica.Dialog(c, &s.renaming, mujica.DialogOptions{Title: g.t("Rename file", "重命名文件"), Actions: func() {
|
||||
if mujica.Button(c, g.t("Cancel", "取消"), mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
overlay.Dialog(c, &s.renaming, overlay.DialogOptions{Title: g.t("Rename file", "重命名文件"), Actions: func() {
|
||||
if input.Button(c, g.t("Cancel", "取消"), input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.renaming = false
|
||||
log("rename canceled")
|
||||
}
|
||||
if mujica.Button(c, g.t("Rename", "重命名"), mujica.ButtonOptions{Disabled: bad != ""}).Clicked() {
|
||||
if input.Button(c, g.t("Rename", "重命名"), input.ButtonOptions{Disabled: bad != ""}).Clicked() {
|
||||
commit()
|
||||
}
|
||||
}}, func() {
|
||||
mujica.FormField(c, g.t("New name", "新名称"), mujica.FormFieldOptions{Error: bad}, func() *ui.Element {
|
||||
r := mujica.TextInput(c, &s.newName, mujica.TextInputOptions{Error: bad})
|
||||
input.FormField(c, g.t("New name", "新名称"), input.FormFieldOptions{Error: bad}, func() *ui.Element {
|
||||
r := input.TextInput(c, &s.newName, input.TextInputOptions{Error: bad})
|
||||
if r.Submitted() {
|
||||
commit()
|
||||
}
|
||||
@@ -338,7 +344,7 @@ func (g *gallery) deleteDialog(c *ui.Context, log func(string)) {
|
||||
if len(s.doomed) == 1 {
|
||||
desc = fmt.Sprintf(g.t("“%s” will be removed from the list.", "将从列表中移除“%s”。"), s.entry(s.doomed[0]).Name)
|
||||
}
|
||||
r := mujica.AlertDialog(c, &s.deleting, mujica.AlertDialogOptions{Title: g.t("Delete files?", "删除文件?"), Description: desc,
|
||||
r := overlay.AlertDialog(c, &s.deleting, overlay.AlertDialogOptions{Title: g.t("Delete files?", "删除文件?"), Description: desc,
|
||||
ConfirmLabel: g.t("Delete permanently", "永久删除")})
|
||||
switch {
|
||||
case r.Confirmed:
|
||||
@@ -348,7 +354,7 @@ func (g *gallery) deleteDialog(c *ui.Context, log func(string)) {
|
||||
}
|
||||
s.version++
|
||||
log(fmt.Sprintf("deleted %d", len(s.doomed)))
|
||||
mujica.ShowToast(c, mujica.ToastMessage{ID: "files-deleted", Severity: mujica.SeveritySuccess,
|
||||
feedback.ShowToast(c, feedback.ToastMessage{ID: "files-deleted", Severity: core.SeveritySuccess,
|
||||
Message: fmt.Sprintf(g.t("Deleted %d files", "已删除 %d 个文件"), len(s.doomed))})
|
||||
s.doomed = nil
|
||||
case r.Canceled:
|
||||
|
||||
@@ -7,7 +7,11 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/datetime"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/feedback"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
const formID = "c-form"
|
||||
@@ -28,10 +32,10 @@ const (
|
||||
)
|
||||
|
||||
type formSubmitDemo struct {
|
||||
st mujica.FormState
|
||||
st input.FormState
|
||||
name string
|
||||
password string
|
||||
debut mujica.Date
|
||||
debut core.Date
|
||||
tags []string
|
||||
terms bool
|
||||
failNext bool
|
||||
@@ -65,7 +69,7 @@ func (g *gallery) formView(c *ui.Context, log func(string)) {
|
||||
drained = true
|
||||
}
|
||||
}
|
||||
mujica.Card(c, mujica.CardOptions{Title: g.t("Audition entry", "试演报名")}, func() {
|
||||
data.Card(c, data.CardOptions{Title: g.t("Audition entry", "试演报名")}, func() {
|
||||
switch s.phase {
|
||||
case formSucceeded:
|
||||
g.formSuccess(c, log)
|
||||
@@ -79,11 +83,11 @@ func (g *gallery) formView(c *ui.Context, log func(string)) {
|
||||
|
||||
func (g *gallery) formEdit(c *ui.Context, log func(string)) {
|
||||
s := &g.cForm
|
||||
if mujica.Switch(c, &s.failNext, g.t("The server rejects the next submit", "服务器拒绝下一次提交"), mujica.SwitchOptions{Disabled: s.st.Submitting()}).Changed() {
|
||||
if input.Switch(c, &s.failNext, g.t("The server rejects the next submit", "服务器拒绝下一次提交"), input.SwitchOptions{Disabled: s.st.Submitting()}).Changed() {
|
||||
log(fmt.Sprintf("fail next submit %v", s.failNext))
|
||||
}
|
||||
r := mujica.LoadingOverlay(c, s.st.Submitting(), mujica.LoadingOverlayOptions{Label: g.t("Submitting…", "正在提交…")}, func() {
|
||||
res := mujica.Form(c, &s.st, mujica.FormOptions{Horizontal: true, SubmitLabel: g.t("Submit entry", "提交报名")}, func(f *mujica.FormScope) {
|
||||
r := feedback.LoadingOverlay(c, s.st.Submitting(), feedback.LoadingOverlayOptions{Label: g.t("Submitting…", "正在提交…")}, func() {
|
||||
res := input.Form(c, &s.st, input.FormOptions{Horizontal: true, SubmitLabel: g.t("Submit entry", "提交报名")}, func(f *input.FormScope) {
|
||||
g.formFields(c, f, log)
|
||||
})
|
||||
res.Element.FillWidth()
|
||||
@@ -108,9 +112,9 @@ func (g *gallery) formEdit(c *ui.Context, log func(string)) {
|
||||
r.Element.FillWidth()
|
||||
}
|
||||
|
||||
func (g *gallery) formFields(c *ui.Context, f *mujica.FormScope, log func(string)) {
|
||||
func (g *gallery) formFields(c *ui.Context, f *input.FormScope, log func(string)) {
|
||||
s := &g.cForm
|
||||
mujica.FormItem(c, f, "name", &s.name, mujica.FormItemOptions[string]{Label: g.t("Stage name", "艺名"), Required: true,
|
||||
input.FormItem(c, f, "name", &s.name, input.FormItemOptions[string]{Label: g.t("Stage name", "艺名"), Required: true,
|
||||
Description: g.t("Checked against the archive; “Oblivionis” is taken.", "将与档案核对;“Oblivionis”已被占用。"),
|
||||
Validate: func(v string) string {
|
||||
if len([]rune(strings.TrimSpace(v))) < 2 {
|
||||
@@ -132,44 +136,44 @@ func (g *gallery) formFields(c *ui.Context, f *mujica.FormScope, log func(string
|
||||
c.Invalidate()
|
||||
}()
|
||||
}}, func() *ui.Element {
|
||||
return mujica.TextInput(c, &s.name, mujica.TextInputOptions{}).Input
|
||||
return input.TextInput(c, &s.name, input.TextInputOptions{}).Input
|
||||
})
|
||||
mujica.FormItem(c, f, "password", &s.password, mujica.FormItemOptions[string]{Label: g.t("Password", "密码"), Required: true,
|
||||
input.FormItem(c, f, "password", &s.password, input.FormItemOptions[string]{Label: g.t("Password", "密码"), Required: true,
|
||||
Validate: func(v string) string {
|
||||
if len([]rune(v)) < 8 {
|
||||
return g.t("Use at least 8 characters.", "至少 8 个字符。")
|
||||
}
|
||||
return ""
|
||||
}}, func() *ui.Element {
|
||||
r := mujica.PasswordInput(c, &s.password, mujica.PasswordInputOptions{})
|
||||
r := input.PasswordInput(c, &s.password, input.PasswordInputOptions{})
|
||||
if r.Submitted() {
|
||||
s.st.Submit()
|
||||
}
|
||||
return r.Input
|
||||
})
|
||||
mujica.FormItem(c, f, "debut", &s.debut, mujica.FormItemOptions[mujica.Date]{Label: g.t("Debut date", "首演日期"), Required: true}, func() *ui.Element {
|
||||
r := mujica.DatePicker(c, &s.debut, mujica.DatePickerOptions{Clearable: true})
|
||||
input.FormItem(c, f, "debut", &s.debut, input.FormItemOptions[core.Date]{Label: g.t("Debut date", "首演日期"), Required: true}, func() *ui.Element {
|
||||
r := datetime.DatePicker(c, &s.debut, datetime.DatePickerOptions{Clearable: true})
|
||||
if r.Changed() {
|
||||
log("debut " + s.debut.Format("2006-01-02"))
|
||||
}
|
||||
return r.Input
|
||||
})
|
||||
mujica.FormItem(c, f, "roles", &s.tags, mujica.FormItemOptions[[]string]{Label: g.t("Roles", "担当"), Required: true,
|
||||
input.FormItem(c, f, "roles", &s.tags, input.FormItemOptions[[]string]{Label: g.t("Roles", "担当"), Required: true,
|
||||
Description: g.t("Up to 4; press Enter after each.", "最多 4 个;每个后按 Enter。")}, func() *ui.Element {
|
||||
r := mujica.TagsInput(c, &s.tags, mujica.TagsInputOptions{Max: 4, Invalid: s.st.Error("roles") != "", Placeholder: g.t("keyboard, bass…", "键盘、贝斯…")})
|
||||
r := input.TagsInput(c, &s.tags, input.TagsInputOptions{Max: 4, Invalid: s.st.Error("roles") != "", Placeholder: g.t("keyboard, bass…", "键盘、贝斯…")})
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("roles %q", s.tags))
|
||||
}
|
||||
return r.Input
|
||||
})
|
||||
mujica.FormItem(c, f, "terms", &s.terms, mujica.FormItemOptions[bool]{Label: g.t("Terms", "条款"),
|
||||
input.FormItem(c, f, "terms", &s.terms, input.FormItemOptions[bool]{Label: g.t("Terms", "条款"),
|
||||
Validate: func(v bool) string {
|
||||
if !v {
|
||||
return g.t("Accept the terms to continue.", "请先同意条款。")
|
||||
}
|
||||
return ""
|
||||
}}, func() *ui.Element {
|
||||
e := mujica.Checkbox(c, &s.terms, g.t("I accept the covenant of the masquerade", "我接受假面舞会的盟约"), mujica.CheckboxOptions{})
|
||||
e := input.Checkbox(c, &s.terms, g.t("I accept the covenant of the masquerade", "我接受假面舞会的盟约"), input.CheckboxOptions{})
|
||||
if e.Changed() {
|
||||
log(fmt.Sprintf("terms %v", s.terms))
|
||||
}
|
||||
@@ -179,7 +183,7 @@ func (g *gallery) formFields(c *ui.Context, f *mujica.FormScope, log func(string
|
||||
|
||||
func (g *gallery) formSuccess(c *ui.Context, log func(string)) {
|
||||
s := &g.cForm
|
||||
r := mujica.Result(c, mujica.ResultOptions{Kind: mujica.ResultSuccess, Title: g.t("Entry received", "报名已收到"),
|
||||
r := feedback.Result(c, feedback.ResultOptions{Kind: feedback.ResultSuccess, Title: g.t("Entry received", "报名已收到"),
|
||||
Description: fmt.Sprintf(g.t("%s, your debut on %s is on the ledger.", "%s,你在 %s 的首演已登记。"), s.name, s.debut.Format("2006-01-02")),
|
||||
Actions: []string{g.t("New entry", "新的报名")}})
|
||||
if r.Action == 0 {
|
||||
@@ -190,7 +194,7 @@ func (g *gallery) formSuccess(c *ui.Context, log func(string)) {
|
||||
|
||||
func (g *gallery) formFailure(c *ui.Context, log func(string)) {
|
||||
s := &g.cForm
|
||||
r := mujica.Result(c, mujica.ResultOptions{Kind: mujica.ResultFailure, Title: g.t("Entry not sent", "报名未送达"), Description: s.failure,
|
||||
r := feedback.Result(c, feedback.ResultOptions{Kind: feedback.ResultFailure, Title: g.t("Entry not sent", "报名未送达"), Description: s.failure,
|
||||
Actions: []string{g.t("Retry", "重试"), g.t("Back to the form", "返回表单")}})
|
||||
switch r.Action {
|
||||
case 0:
|
||||
|
||||
@@ -6,8 +6,12 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/feedback"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/navigation"
|
||||
)
|
||||
|
||||
const settingsID = "c-settings"
|
||||
@@ -45,13 +49,13 @@ func newSettingsDemo() settingsDemo {
|
||||
|
||||
func (g *gallery) settingsView(c *ui.Context, log func(string)) {
|
||||
s := &g.cSettings
|
||||
mujica.Card(c, mujica.CardOptions{Title: g.t("Preferences", "偏好设置"), Footer: func() { g.settingsFooter(c, log) }}, func() {
|
||||
tabs := []mujica.Tab{
|
||||
data.Card(c, data.CardOptions{Title: g.t("Preferences", "偏好设置"), Footer: func() { g.settingsFooter(c, log) }}, func() {
|
||||
tabs := []navigation.Tab{
|
||||
{Label: g.t("General", "通用"), Icon: icons.Must("settings"), Panel: func() { g.settingsGeneral(c, log) }},
|
||||
{Label: g.t("Appearance", "外观"), Icon: icons.Must("palette"), Panel: func() { g.settingsAppearance(c, log) }},
|
||||
{Label: g.t("Privacy", "隐私"), Icon: icons.Must("lock"), Panel: func() { g.settingsPrivacy(c, log) }},
|
||||
}
|
||||
if mujica.Tabs(c, &s.tab, tabs, mujica.TabsOptions{Label: g.t("Preference groups", "设置分组")}).Changed() {
|
||||
if navigation.Tabs(c, &s.tab, tabs, navigation.TabsOptions{Label: g.t("Preference groups", "设置分组")}).Changed() {
|
||||
log("tab " + tabs[s.tab].Label)
|
||||
}
|
||||
})
|
||||
@@ -74,36 +78,36 @@ func (g *gallery) settingsGeneral(c *ui.Context, log func(string)) {
|
||||
d := &g.cSettings.draft
|
||||
settingsPanel(c, func() {
|
||||
bad := g.workspaceError(d.workspace)
|
||||
mujica.FormField(c, g.t("Workspace name", "工作区名称"), mujica.FormFieldOptions{Required: true, Error: bad,
|
||||
input.FormField(c, g.t("Workspace name", "工作区名称"), input.FormFieldOptions{Required: true, Error: bad,
|
||||
Description: g.t("Shown in the title bar.", "显示在标题栏中。")}, func() *ui.Element {
|
||||
r := mujica.TextInput(c, &d.workspace, mujica.TextInputOptions{Error: bad})
|
||||
r := input.TextInput(c, &d.workspace, input.TextInputOptions{Error: bad})
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("workspace %q", d.workspace))
|
||||
}
|
||||
return r.Input
|
||||
})
|
||||
mujica.FormField(c, g.t("Language", "语言"), mujica.FormFieldOptions{}, func() *ui.Element {
|
||||
r := mujica.Select(c, &d.language, []mujica.SelectOption[string]{
|
||||
input.FormField(c, g.t("Language", "语言"), input.FormFieldOptions{}, func() *ui.Element {
|
||||
r := input.Select(c, &d.language, []input.SelectOption[string]{
|
||||
{Value: "en", Label: "English"}, {Value: "zh", Label: "简体中文"}, {Value: "ja", Label: "日本語"},
|
||||
}, mujica.SelectOptions{})
|
||||
}, input.SelectOptions{})
|
||||
if r.Changed() {
|
||||
log("language " + d.language)
|
||||
}
|
||||
return r.Element
|
||||
})
|
||||
mujica.FormField(c, g.t("On startup", "启动时"), mujica.FormFieldOptions{}, func() *ui.Element {
|
||||
r := mujica.RadioGroup(c, &d.startPage, []mujica.RadioItem[string]{
|
||||
input.FormField(c, g.t("On startup", "启动时"), input.FormFieldOptions{}, func() *ui.Element {
|
||||
r := input.RadioGroup(c, &d.startPage, []input.RadioItem[string]{
|
||||
{Value: "blank", Label: g.t("Open a blank page", "打开空白页")},
|
||||
{Value: "last", Label: g.t("Reopen the last session", "恢复上次会话")},
|
||||
{Value: "home", Label: g.t("Open the home page", "打开主页")},
|
||||
}, mujica.RadioGroupOptions{})
|
||||
}, input.RadioGroupOptions{})
|
||||
if r.Changed() {
|
||||
log("startup " + d.startPage)
|
||||
}
|
||||
return r.Element
|
||||
})
|
||||
mujica.FormField(c, g.t("Login item", "登录项"), mujica.FormFieldOptions{}, func() *ui.Element {
|
||||
e := mujica.Switch(c, &d.launch, g.t("Launch at login", "登录时启动"), mujica.SwitchOptions{})
|
||||
input.FormField(c, g.t("Login item", "登录项"), input.FormFieldOptions{}, func() *ui.Element {
|
||||
e := input.Switch(c, &d.launch, g.t("Launch at login", "登录时启动"), input.SwitchOptions{})
|
||||
if e.Changed() {
|
||||
log(fmt.Sprintf("launch at login %v", d.launch))
|
||||
}
|
||||
@@ -115,33 +119,33 @@ func (g *gallery) settingsGeneral(c *ui.Context, log func(string)) {
|
||||
func (g *gallery) settingsAppearance(c *ui.Context, log func(string)) {
|
||||
d := &g.cSettings.draft
|
||||
settingsPanel(c, func() {
|
||||
mujica.FormField(c, g.t("Theme", "主题"), mujica.FormFieldOptions{}, func() *ui.Element {
|
||||
r := mujica.RadioGroup(c, &d.theme, []mujica.RadioItem[string]{
|
||||
input.FormField(c, g.t("Theme", "主题"), input.FormFieldOptions{}, func() *ui.Element {
|
||||
r := input.RadioGroup(c, &d.theme, []input.RadioItem[string]{
|
||||
{Value: "system", Label: g.t("System", "跟随系统")}, {Value: "light", Label: g.t("Light", "浅色")}, {Value: "dark", Label: g.t("Dark", "深色")},
|
||||
}, mujica.RadioGroupOptions{Horizontal: true})
|
||||
}, input.RadioGroupOptions{Horizontal: true})
|
||||
if r.Changed() {
|
||||
log("theme " + d.theme)
|
||||
}
|
||||
return r.Element
|
||||
})
|
||||
mujica.FormField(c, g.t("Text size", "文字大小"), mujica.FormFieldOptions{}, func() *ui.Element {
|
||||
r := mujica.Slider(c, &d.fontSize, mujica.SliderOptions{Min: 12, Max: 20, Step: 1, ShowValue: true,
|
||||
Marks: []mujica.SliderMark{{Value: 12, Label: "12"}, {Value: 16, Label: "16"}, {Value: 20, Label: "20"}},
|
||||
input.FormField(c, g.t("Text size", "文字大小"), input.FormFieldOptions{}, func() *ui.Element {
|
||||
r := input.Slider(c, &d.fontSize, input.SliderOptions{Min: 12, Max: 20, Step: 1, ShowValue: true,
|
||||
Marks: []input.SliderMark{{Value: 12, Label: "12"}, {Value: 16, Label: "16"}, {Value: 20, Label: "20"}},
|
||||
Format: func(v float64) string { return fmt.Sprintf("%.0f pt", v) }})
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("text size %.0f", d.fontSize))
|
||||
}
|
||||
return r.Element
|
||||
})
|
||||
mujica.FormField(c, g.t("Sidebar width", "侧栏宽度"), mujica.FormFieldOptions{Description: g.t("In DIPs, from 180 to 360.", "单位 DIP,180 至 360。")}, func() *ui.Element {
|
||||
r := mujica.NumberInput(c, &d.sidebar, mujica.NumberInputOptions{Min: 180, Max: 360, Step: 10})
|
||||
input.FormField(c, g.t("Sidebar width", "侧栏宽度"), input.FormFieldOptions{Description: g.t("In DIPs, from 180 to 360.", "单位 DIP,180 至 360。")}, func() *ui.Element {
|
||||
r := input.NumberInput(c, &d.sidebar, input.NumberInputOptions{Min: 180, Max: 360, Step: 10})
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("sidebar width %.0f", d.sidebar))
|
||||
}
|
||||
return r.Input
|
||||
})
|
||||
mujica.FormField(c, g.t("Motion", "动效"), mujica.FormFieldOptions{}, func() *ui.Element {
|
||||
e := mujica.Switch(c, &d.reduce, g.t("Reduce motion", "减少动效"), mujica.SwitchOptions{})
|
||||
input.FormField(c, g.t("Motion", "动效"), input.FormFieldOptions{}, func() *ui.Element {
|
||||
e := input.Switch(c, &d.reduce, g.t("Reduce motion", "减少动效"), input.SwitchOptions{})
|
||||
if e.Changed() {
|
||||
log(fmt.Sprintf("reduce motion %v", d.reduce))
|
||||
}
|
||||
@@ -153,32 +157,32 @@ func (g *gallery) settingsAppearance(c *ui.Context, log func(string)) {
|
||||
func (g *gallery) settingsPrivacy(c *ui.Context, log func(string)) {
|
||||
d := &g.cSettings.draft
|
||||
settingsPanel(c, func() {
|
||||
mujica.FormField(c, g.t("Usage data", "使用数据"), mujica.FormFieldOptions{Description: g.t("Nothing leaves this demo.", "本示例不会发送任何数据。")}, func() *ui.Element {
|
||||
e := mujica.Switch(c, &d.telemetry, g.t("Share anonymous usage data", "共享匿名使用数据"), mujica.SwitchOptions{})
|
||||
input.FormField(c, g.t("Usage data", "使用数据"), input.FormFieldOptions{Description: g.t("Nothing leaves this demo.", "本示例不会发送任何数据。")}, func() *ui.Element {
|
||||
e := input.Switch(c, &d.telemetry, g.t("Share anonymous usage data", "共享匿名使用数据"), input.SwitchOptions{})
|
||||
if e.Changed() {
|
||||
log(fmt.Sprintf("usage data %v", d.telemetry))
|
||||
}
|
||||
return e
|
||||
})
|
||||
mujica.FormField(c, g.t("Crash reports", "崩溃报告"), mujica.FormFieldOptions{}, func() *ui.Element {
|
||||
e := mujica.Switch(c, &d.crashes, g.t("Send crash reports", "发送崩溃报告"), mujica.SwitchOptions{})
|
||||
input.FormField(c, g.t("Crash reports", "崩溃报告"), input.FormFieldOptions{}, func() *ui.Element {
|
||||
e := input.Switch(c, &d.crashes, g.t("Send crash reports", "发送崩溃报告"), input.SwitchOptions{})
|
||||
if e.Changed() {
|
||||
log(fmt.Sprintf("crash reports %v", d.crashes))
|
||||
}
|
||||
return e
|
||||
})
|
||||
mujica.FormField(c, g.t("Keep history", "保留历史"), mujica.FormFieldOptions{Description: g.t("Days, from 0 to 365; 0 keeps none.", "天数,0 至 365;0 表示不保留。")}, func() *ui.Element {
|
||||
r := mujica.NumberInput(c, &d.history, mujica.NumberInputOptions{Min: 0, Max: 365, Step: 1})
|
||||
input.FormField(c, g.t("Keep history", "保留历史"), input.FormFieldOptions{Description: g.t("Days, from 0 to 365; 0 keeps none.", "天数,0 至 365;0 表示不保留。")}, func() *ui.Element {
|
||||
r := input.NumberInput(c, &d.history, input.NumberInputOptions{Min: 0, Max: 365, Step: 1})
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("history %.0f days", d.history))
|
||||
}
|
||||
return r.Input
|
||||
})
|
||||
mujica.FormField(c, g.t("Lock after", "自动锁定"), mujica.FormFieldOptions{}, func() *ui.Element {
|
||||
r := mujica.Select(c, &d.lock, []mujica.SelectOption[string]{
|
||||
input.FormField(c, g.t("Lock after", "自动锁定"), input.FormFieldOptions{}, func() *ui.Element {
|
||||
r := input.Select(c, &d.lock, []input.SelectOption[string]{
|
||||
{Value: "never", Label: g.t("Never", "从不")}, {Value: "5", Label: g.t("5 minutes", "5 分钟")},
|
||||
{Value: "15", Label: g.t("15 minutes", "15 分钟")}, {Value: "60", Label: g.t("1 hour", "1 小时")},
|
||||
}, mujica.SelectOptions{})
|
||||
}, input.SelectOptions{})
|
||||
if r.Changed() {
|
||||
log("lock after " + d.lock)
|
||||
}
|
||||
@@ -194,19 +198,19 @@ func (g *gallery) settingsFooter(c *ui.Context, log func(string)) {
|
||||
invalid := g.workspaceError(s.draft.workspace) != ""
|
||||
ui.Row(c).FillWidth().AlignItems(ui.Center).Gap(12).Children(func() {
|
||||
if dirty {
|
||||
mujica.StatusIndicator(c, mujica.StatusWarning, mujica.StatusIndicatorOptions{Label: g.t("Unsaved changes", "有未保存的更改")})
|
||||
feedback.StatusIndicator(c, feedback.StatusWarning, feedback.StatusIndicatorOptions{Label: g.t("Unsaved changes", "有未保存的更改")})
|
||||
} else {
|
||||
mujica.StatusIndicator(c, mujica.StatusSuccess, mujica.StatusIndicatorOptions{Label: g.t("All changes saved", "更改均已保存")})
|
||||
feedback.StatusIndicator(c, feedback.StatusSuccess, feedback.StatusIndicatorOptions{Label: g.t("All changes saved", "更改均已保存")})
|
||||
}
|
||||
ui.Box(c).Grow(1)
|
||||
if mujica.Button(c, g.t("Reset", "重置"), mujica.ButtonOptions{Variant: mujica.Secondary, Disabled: s.draft == settingsInitial}).Clicked() {
|
||||
if input.Button(c, g.t("Reset", "重置"), input.ButtonOptions{Variant: input.Secondary, Disabled: s.draft == settingsInitial}).Clicked() {
|
||||
s.draft = settingsInitial
|
||||
log("reset to initial values")
|
||||
}
|
||||
if mujica.Button(c, g.t("Save", "保存"), mujica.ButtonOptions{Disabled: !dirty || invalid}).Clicked() {
|
||||
if input.Button(c, g.t("Save", "保存"), input.ButtonOptions{Disabled: !dirty || invalid}).Clicked() {
|
||||
s.saved = s.draft
|
||||
log("saved")
|
||||
mujica.ShowToast(c, mujica.ToastMessage{ID: "settings-saved", Severity: mujica.SeveritySuccess, Message: g.t("Settings saved", "设置已保存")})
|
||||
feedback.ShowToast(c, feedback.ToastMessage{ID: "settings-saved", Severity: core.SeveritySuccess, Message: g.t("Settings saved", "设置已保存")})
|
||||
}
|
||||
})
|
||||
}
|
||||
+16
-15
@@ -5,8 +5,9 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/navigation"
|
||||
)
|
||||
|
||||
const overtureID = "overture"
|
||||
@@ -83,19 +84,19 @@ func (g *gallery) categoryName(cat string) string {
|
||||
func (g *gallery) pageName(p page) string { return g.t(p.Name, p.ZhName) }
|
||||
|
||||
// sections groups the pages that match the query by category, in PRD order.
|
||||
func (g *gallery) sections() []mujica.SidebarSection {
|
||||
out := []mujica.SidebarSection{{Title: g.t("Overture", "序章"), Items: []mujica.SidebarItem{
|
||||
func (g *gallery) sections() []navigation.SidebarSection {
|
||||
out := []navigation.SidebarSection{{Title: g.t("Overture", "序章"), Items: []navigation.SidebarItem{
|
||||
{ID: overtureID, Label: g.t("Overture", "序章"), Icon: icons.Must("house")},
|
||||
}}}
|
||||
var comps []mujica.SidebarItem
|
||||
var comps []navigation.SidebarItem
|
||||
q := strings.ToLower(strings.TrimSpace(g.query))
|
||||
for _, cp := range compositions {
|
||||
if q == "" || strings.Contains(strings.ToLower(cp.ID+" "+cp.Name+" "+cp.ZhName), q) {
|
||||
comps = append(comps, mujica.SidebarItem{ID: cp.ID, Label: g.t(cp.Name, cp.ZhName), Icon: icons.Must(cp.Icon)})
|
||||
comps = append(comps, navigation.SidebarItem{ID: cp.ID, Label: g.t(cp.Name, cp.ZhName), Icon: icons.Must(cp.Icon)})
|
||||
}
|
||||
}
|
||||
if len(comps) > 0 {
|
||||
out = append(out, mujica.SidebarSection{Title: g.t("Compositions", "组合示例"), Items: comps})
|
||||
out = append(out, navigation.SidebarSection{Title: g.t("Compositions", "组合示例"), Items: comps})
|
||||
}
|
||||
index := map[string]int{}
|
||||
for _, p := range g.pages {
|
||||
@@ -106,9 +107,9 @@ func (g *gallery) sections() []mujica.SidebarSection {
|
||||
if !ok {
|
||||
i = len(out)
|
||||
index[p.Category] = i
|
||||
out = append(out, mujica.SidebarSection{Title: g.categoryName(p.Category)})
|
||||
out = append(out, navigation.SidebarSection{Title: g.categoryName(p.Category)})
|
||||
}
|
||||
out[i].Items = append(out[i].Items, mujica.SidebarItem{ID: p.ID, Label: p.ID + " " + g.pageName(p)})
|
||||
out[i].Items = append(out[i].Items, navigation.SidebarItem{ID: p.ID, Label: p.ID + " " + g.pageName(p)})
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -116,7 +117,7 @@ func (g *gallery) sections() []mujica.SidebarSection {
|
||||
func (g *gallery) sidebar(c *ui.Context) {
|
||||
ui.Column(c).FillWidth().Gap(8).Padding(8, 8, 0, 8).Children(func() {
|
||||
before := g.query
|
||||
mujica.SearchInput(c, &g.query, mujica.SearchInputOptions{Placeholder: g.t("Search · 搜索", "搜索 · Search")})
|
||||
input.SearchInput(c, &g.query, input.SearchInputOptions{Placeholder: g.t("Search · 搜索", "搜索 · Search")})
|
||||
if g.query != before {
|
||||
g.logFor("shell")("search " + g.query)
|
||||
}
|
||||
@@ -125,27 +126,27 @@ func (g *gallery) sidebar(c *ui.Context) {
|
||||
if len(sections) == 1 && g.query != "" {
|
||||
ui.Text(c, g.t("No component matches.", "没有匹配的组件。")).Padding(8, 16)
|
||||
}
|
||||
if mujica.Sidebar(c, &g.selected, sections, mujica.SidebarOptions{}).Changed() {
|
||||
if navigation.Sidebar(c, &g.selected, sections, navigation.SidebarOptions{}).Changed() {
|
||||
g.logFor("shell")("open " + g.selected)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *gallery) commandPalette(c *ui.Context) {
|
||||
cmds := []mujica.Command{
|
||||
cmds := []navigation.Command{
|
||||
{ID: overtureID, Label: g.t("Overture", "序章"), Group: g.t("Go to", "前往")},
|
||||
{ID: "theme", Label: g.t("Cycle theme", "切换主题"), Group: g.t("Window", "窗口"), Keywords: []string{"dark", "light", "深色", "浅色"}},
|
||||
{ID: "density", Label: g.t("Cycle density", "切换密度"), Group: g.t("Window", "窗口"), Keywords: []string{"compact", "紧凑"}},
|
||||
{ID: "locale", Label: g.t("Switch to Chinese", "切换到英文"), Group: g.t("Window", "窗口"), Keywords: []string{"language", "语言"}},
|
||||
}
|
||||
for _, cp := range compositions {
|
||||
cmds = append(cmds, mujica.Command{ID: "page:" + cp.ID, Label: g.t(cp.Name, cp.ZhName), Group: g.t("Compositions", "组合示例"),
|
||||
cmds = append(cmds, navigation.Command{ID: "page:" + cp.ID, Label: g.t(cp.Name, cp.ZhName), Group: g.t("Compositions", "组合示例"),
|
||||
Keywords: []string{cp.Name, cp.ZhName, "composition", "组合"}})
|
||||
}
|
||||
for _, p := range g.pages {
|
||||
cmds = append(cmds, mujica.Command{ID: "page:" + p.ID, Label: p.ID + " " + g.pageName(p), Group: g.categoryName(p.Category),
|
||||
cmds = append(cmds, navigation.Command{ID: "page:" + p.ID, Label: p.ID + " " + g.pageName(p), Group: g.categoryName(p.Category),
|
||||
Keywords: []string{p.Name, p.ZhName, p.Category, categoryZh[p.Category]}})
|
||||
}
|
||||
id, ok := mujica.CommandPalette(c, &g.palette, cmds, mujica.CommandPaletteOptions{}).Chosen()
|
||||
id, ok := navigation.CommandPalette(c, &g.palette, cmds, navigation.CommandPaletteOptions{}).Chosen()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5,8 +5,12 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/display"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/layout"
|
||||
)
|
||||
|
||||
// overtureState is the continuity proof: values that must survive theme changes.
|
||||
@@ -18,11 +22,11 @@ type overtureState struct {
|
||||
}
|
||||
|
||||
func (g *gallery) overturePage(c *ui.Context) {
|
||||
k := mujica.Tokens(c)
|
||||
k := core.Tokens(c)
|
||||
w, _ := c.Size()
|
||||
mujica.Container(c, mujica.ContainerOptions{MaxWidth: 1080, Center: true}, func() {
|
||||
layout.Container(c, layout.ContainerOptions{MaxWidth: 1080, Center: true}, func() {
|
||||
ui.Column(c).FillWidth().Gap(32).Children(func() {
|
||||
mujica.Heading(c, "MujicaUI", mujica.HeadingOptions{
|
||||
display.Heading(c, "MujicaUI", display.HeadingOptions{
|
||||
Subtitle: g.t("One hundred court-gothic components for MyGo native UI.", "为 MyGo 原生界面打造的一百件宫廷哥特组件。"),
|
||||
Divider: true,
|
||||
})
|
||||
@@ -30,10 +34,10 @@ func (g *gallery) overturePage(c *ui.Context) {
|
||||
"Every pixel here is drawn by Go. No HTML, no WebView. Ivory and charcoal, a wine accent, old gold on the edges.",
|
||||
"这里每一个像素都由 Go 绘制。没有 HTML,没有 WebView。象牙白与炭黑,酒红为主色,旧金只落在边缘。",
|
||||
)).TextColor(k.TextMuted).FillWidth()
|
||||
mujica.Grid(c, mujica.GridOptions{Columns: []ui.Track{ui.Fr(1), ui.Fr(1), ui.Fr(1)}}, func() {
|
||||
mujica.Statistic(c, float64(len(g.pages)), mujica.StatisticOptions{Title: g.t("Components", "组件")})
|
||||
mujica.Statistic(c, 2, mujica.StatisticOptions{Title: g.t("Themes", "主题")})
|
||||
mujica.Statistic(c, 3, mujica.StatisticOptions{Title: g.t("Densities", "密度")})
|
||||
layout.Grid(c, layout.GridOptions{Columns: []ui.Track{ui.Fr(1), ui.Fr(1), ui.Fr(1)}}, func() {
|
||||
data.Statistic(c, float64(len(g.pages)), data.StatisticOptions{Title: g.t("Components", "组件")})
|
||||
data.Statistic(c, 2, data.StatisticOptions{Title: g.t("Themes", "主题")})
|
||||
data.Statistic(c, 3, data.StatisticOptions{Title: g.t("Densities", "密度")})
|
||||
})
|
||||
g.continuity(c)
|
||||
g.categoryGrid(c, w)
|
||||
@@ -44,29 +48,29 @@ func (g *gallery) overturePage(c *ui.Context) {
|
||||
// continuity holds live state while the user flips theme and density.
|
||||
func (g *gallery) continuity(c *ui.Context) {
|
||||
s := &g.overture
|
||||
k := mujica.Tokens(c)
|
||||
mujica.Card(c, mujica.CardOptions{Title: g.t("Proof of continuity", "连续性证明"), Decorated: true}, func() {
|
||||
k := core.Tokens(c)
|
||||
data.Card(c, data.CardOptions{Title: g.t("Proof of continuity", "连续性证明"), Decorated: true}, func() {
|
||||
ui.Column(c).FillWidth().Gap(16).Children(func() {
|
||||
ui.Text(c, g.t(
|
||||
"Type a line, move the slider, then switch theme or density above, or press ⌘K. Nothing resets: not the text, not the caret, not the focus.",
|
||||
"写下一行字,拖动滑块,再切换上方的主题或密度,或按 ⌘K。什么都不会重置:文字、光标、焦点都留在原处。",
|
||||
)).FillWidth()
|
||||
if mujica.TextInput(c, &s.note, mujica.TextInputOptions{Placeholder: g.t("A line for the court ledger", "写给宫廷账簿的一行"), Label: g.t("Ledger line", "账簿")}).Changed() {
|
||||
if input.TextInput(c, &s.note, input.TextInputOptions{Placeholder: g.t("A line for the court ledger", "写给宫廷账簿的一行"), Label: g.t("Ledger line", "账簿")}).Changed() {
|
||||
g.logFor(overtureID)("ledger line edited")
|
||||
}
|
||||
if mujica.Slider(c, &s.tempo, mujica.SliderOptions{Max: 100, Step: 1, ShowValue: true, Label: g.t("Tempo", "速度")}).Changed() {
|
||||
if input.Slider(c, &s.tempo, input.SliderOptions{Max: 100, Step: 1, ShowValue: true, Label: g.t("Tempo", "速度")}).Changed() {
|
||||
g.logFor(overtureID)(fmt.Sprintf("tempo %.0f", s.tempo))
|
||||
}
|
||||
if mujica.Checkbox(c, &s.sealed, g.t("Seal with wax", "以火漆封缄"), mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, &s.sealed, g.t("Seal with wax", "以火漆封缄"), input.CheckboxOptions{}).Changed() {
|
||||
g.logFor(overtureID)(fmt.Sprintf("sealed %v", s.sealed))
|
||||
}
|
||||
ui.Row(c).Gap(12).AlignItems(ui.Center).Children(func() {
|
||||
if mujica.Button(c, g.t("Cycle theme", "切换主题"), mujica.ButtonOptions{Icon: icons.Must("moon")}).Clicked() {
|
||||
if input.Button(c, g.t("Cycle theme", "切换主题"), input.ButtonOptions{Icon: icons.Must("moon")}).Clicked() {
|
||||
g.mode = (g.mode + 1) % 3
|
||||
s.switches++
|
||||
g.logFor(overtureID)("theme " + mujica.Mode(g.mode).String())
|
||||
g.logFor(overtureID)("theme " + core.Mode(g.mode).String())
|
||||
}
|
||||
if mujica.Button(c, g.t("Cycle density", "切换密度"), mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, g.t("Cycle density", "切换密度"), input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
g.density = (g.density + 1) % len(densities)
|
||||
s.switches++
|
||||
g.logFor(overtureID)("density " + densities[g.density].String())
|
||||
@@ -98,17 +102,17 @@ func (g *gallery) categoryGrid(c *ui.Context, width float32) {
|
||||
}
|
||||
count[p.Category]++
|
||||
}
|
||||
mujica.Grid(c, mujica.GridOptions{Columns: tracks}, func() {
|
||||
layout.Grid(c, layout.GridOptions{Columns: tracks}, func() {
|
||||
for _, cat := range cats {
|
||||
ui.Box(c).Key(cat).Children(func() {
|
||||
mujica.Card(c, mujica.CardOptions{Title: g.categoryName(cat), Footer: func() {
|
||||
if mujica.Button(c, g.t("Enter", "进入"), mujica.ButtonOptions{Variant: mujica.Outline}).Clicked() {
|
||||
data.Card(c, data.CardOptions{Title: g.categoryName(cat), Footer: func() {
|
||||
if input.Button(c, g.t("Enter", "进入"), input.ButtonOptions{Variant: input.Outline}).Clicked() {
|
||||
g.selected = first[cat].ID
|
||||
g.logFor(overtureID)("enter " + cat)
|
||||
}
|
||||
}}, func() {
|
||||
ui.Text(c, fmt.Sprintf(g.t("%d components, from %s", "%d 个组件,始于 %s"), count[cat], g.pageName(first[cat]))).
|
||||
TextColor(mujica.Tokens(c).TextMuted)
|
||||
TextColor(core.Tokens(c).TextMuted)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,32 +5,37 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/icons"
|
||||
"mujicaui/theme"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/display"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/feedback"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/layout"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||
)
|
||||
|
||||
// componentPage shows one component: demo, code, keys and its events.
|
||||
func (g *gallery) componentPage(c *ui.Context, p page) {
|
||||
k := mujica.Tokens(c)
|
||||
mujica.Container(c, mujica.ContainerOptions{MaxWidth: 1080, Center: true}, func() {
|
||||
k := core.Tokens(c)
|
||||
layout.Container(c, layout.ContainerOptions{MaxWidth: 1080, Center: true}, func() {
|
||||
ui.Column(c).FillWidth().Gap(24).Children(func() {
|
||||
mujica.Heading(c, g.pageName(p), mujica.HeadingOptions{
|
||||
display.Heading(c, g.pageName(p), display.HeadingOptions{
|
||||
Subtitle: fmt.Sprintf("%s · %s · %s", p.ID, g.t(p.ZhName, p.Name), g.categoryName(p.Category)),
|
||||
Divider: true,
|
||||
})
|
||||
ui.Column(c).FillWidth().Gap(16).Children(func() { p.Demo(c, g.logFor(p.ID)) })
|
||||
mujica.Grid(c, mujica.GridOptions{Columns: []ui.Track{ui.Fr(3), ui.Fr(2)}, Gap: mujica.SpaceL}, func() {
|
||||
mujica.Card(c, mujica.CardOptions{Title: g.t("Code", "代码"), Footer: func() {
|
||||
if mujica.Button(c, g.t("Copy", "复制"), mujica.ButtonOptions{Variant: mujica.Secondary, Icon: icons.Must("copy")}).Clicked() {
|
||||
layout.Grid(c, layout.GridOptions{Columns: []ui.Track{ui.Fr(3), ui.Fr(2)}, Gap: layout.SpaceL}, func() {
|
||||
data.Card(c, data.CardOptions{Title: g.t("Code", "代码"), Footer: func() {
|
||||
if input.Button(c, g.t("Copy", "复制"), input.ButtonOptions{Variant: input.Secondary, Icon: icons.Must("copy")}).Clicked() {
|
||||
c.WriteClipboard(p.Code)
|
||||
g.logFor(p.ID)("code copied")
|
||||
mujica.ShowToast(c, mujica.ToastMessage{ID: "copied", Severity: mujica.SeveritySuccess, Message: g.t("Code copied", "代码已复制")})
|
||||
feedback.ShowToast(c, feedback.ToastMessage{ID: "copied", Severity: core.SeveritySuccess, Message: g.t("Code copied", "代码已复制")})
|
||||
}
|
||||
}}, func() {
|
||||
ui.Text(c, p.Code).Font(theme.MonoFont).FontSize(mujica.FontSize(c, theme.CaptionSize)).Selectable().FillWidth()
|
||||
ui.Text(c, p.Code).Font(theme.MonoFont).FontSize(core.FontSize(c, theme.CaptionSize)).Selectable().FillWidth()
|
||||
})
|
||||
mujica.Card(c, mujica.CardOptions{Title: g.t("Keyboard", "键盘")}, func() {
|
||||
data.Card(c, data.CardOptions{Title: g.t("Keyboard", "键盘")}, func() {
|
||||
ui.Text(c, p.Keys).TextColor(k.TextMuted).FillWidth()
|
||||
})
|
||||
})
|
||||
@@ -41,8 +46,8 @@ func (g *gallery) componentPage(c *ui.Context, p page) {
|
||||
|
||||
// eventCard lists the newest events of one page.
|
||||
func (g *gallery) eventCard(c *ui.Context, id string) {
|
||||
k := mujica.Tokens(c)
|
||||
mujica.Card(c, mujica.CardOptions{Title: g.t("Events", "事件")}, func() {
|
||||
k := core.Tokens(c)
|
||||
data.Card(c, data.CardOptions{Title: g.t("Events", "事件")}, func() {
|
||||
shown := 0
|
||||
for i := len(g.events) - 1; i >= 0 && shown < 8; i-- {
|
||||
e := g.events[i]
|
||||
@@ -51,7 +56,7 @@ func (g *gallery) eventCard(c *ui.Context, id string) {
|
||||
}
|
||||
shown++
|
||||
ui.Row(c).Gap(12).Children(func() {
|
||||
ui.Text(c, e.at.Format("15:04:05.000")).Font(theme.MonoFont).FontSize(mujica.FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted)
|
||||
ui.Text(c, e.at.Format("15:04:05.000")).Font(theme.MonoFont).FontSize(core.FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted)
|
||||
ui.Text(c, e.text).Grow(1)
|
||||
})
|
||||
}
|
||||
@@ -64,9 +69,9 @@ func (g *gallery) eventCard(c *ui.Context, id string) {
|
||||
// compositePage shows one composition demo above its events.
|
||||
func (g *gallery) compositePage(c *ui.Context, id string, body func(c *ui.Context, log func(string))) {
|
||||
cp := compositionOf(id)
|
||||
mujica.Container(c, mujica.ContainerOptions{MaxWidth: 1080, Center: true}, func() {
|
||||
layout.Container(c, layout.ContainerOptions{MaxWidth: 1080, Center: true}, func() {
|
||||
ui.Column(c).FillWidth().Gap(24).Children(func() {
|
||||
mujica.Heading(c, g.t(cp.Name, cp.ZhName), mujica.HeadingOptions{Subtitle: g.t(cp.About, cp.ZhAbout), Divider: true})
|
||||
display.Heading(c, g.t(cp.Name, cp.ZhName), display.HeadingOptions{Subtitle: g.t(cp.About, cp.ZhAbout), Divider: true})
|
||||
ui.Column(c).FillWidth().Gap(16).Children(func() { body(c, g.logFor(id)) })
|
||||
g.eventCard(c, id)
|
||||
})
|
||||
|
||||
@@ -5,7 +5,9 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/account"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
const accountCategory = "Settings & Account"
|
||||
@@ -58,9 +60,9 @@ type accountSettingsState struct {
|
||||
sound, digest, report bool
|
||||
}
|
||||
|
||||
var accountSettingPages = []mujica.SettingsPage{{ID: "general", Label: "General"}, {ID: "privacy", Label: "Privacy"}}
|
||||
var accountSettingPages = []account.SettingsPage{{ID: "general", Label: "General"}, {ID: "privacy", Label: "Privacy"}}
|
||||
|
||||
var accountSettingEntries = []mujica.SettingEntry{
|
||||
var accountSettingEntries = []account.SettingEntry{
|
||||
{ID: "sound", Page: "general", Label: "Sound", Description: "Chime on new messages.", Keywords: []string{"audio", "notification"}},
|
||||
{ID: "digest", Page: "general", Label: "Weekly digest", Description: "A summary every Monday.", Keywords: []string{"email"}},
|
||||
{ID: "report", Page: "privacy", Label: "Usage reports", Description: "Send anonymous usage."},
|
||||
@@ -69,15 +71,15 @@ var accountSettingEntries = []mujica.SettingEntry{
|
||||
// accountSettingsPage builds the demo pages shared by 461 and 462.
|
||||
func accountSettingsPage(c *ui.Context, d *accountSettingsState, log func(string)) {
|
||||
ui.Box(c).Width(900).Height(320).Children(func() {
|
||||
if mujica.SettingsLayout(c, &d.page, accountSettingPages, mujica.SettingsLayoutOptions{}, func(id string) {
|
||||
if account.SettingsLayout(c, &d.page, accountSettingPages, account.SettingsLayoutOptions{}, func(id string) {
|
||||
switch id {
|
||||
case "general":
|
||||
mujica.SettingsSection(c, "Notifications", func() {
|
||||
account.SettingsSection(c, "Notifications", func() {
|
||||
accountSwitchRow(c, "sound", "Sound", "Chime on new messages.", &d.sound, log)
|
||||
accountSwitchRow(c, "digest", "Weekly digest", "A summary every Monday.", &d.digest, log)
|
||||
})
|
||||
case "privacy":
|
||||
mujica.SettingsSection(c, "Data", func() {
|
||||
account.SettingsSection(c, "Data", func() {
|
||||
accountSwitchRow(c, "report", "Usage reports", "Send anonymous usage.", &d.report, log)
|
||||
})
|
||||
}
|
||||
@@ -89,8 +91,8 @@ func accountSettingsPage(c *ui.Context, d *accountSettingsState, log func(string
|
||||
|
||||
// accountSwitchRow is a settings row with a switch.
|
||||
func accountSwitchRow(c *ui.Context, id, label, desc string, v *bool, log func(string)) {
|
||||
mujica.SettingsRow(c, label, mujica.SettingsRowOptions{ID: id, Description: desc}, func() *ui.Element {
|
||||
sw := mujica.Switch(c, v, "", mujica.SwitchOptions{})
|
||||
account.SettingsRow(c, label, account.SettingsRowOptions{ID: id, Description: desc}, func() *ui.Element {
|
||||
sw := input.Switch(c, v, "", input.SwitchOptions{})
|
||||
if sw.Changed() {
|
||||
log(fmt.Sprintf("%s %v", label, *v))
|
||||
}
|
||||
@@ -106,7 +108,7 @@ func accountSettingsDemo(c *ui.Context, log func(string)) {
|
||||
func accountSearchDemo(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "462", func() accountSettingsState { return accountSettingsState{sound: true} })
|
||||
ui.Box(c).Width(320).Children(func() {
|
||||
if e, ok := mujica.SettingsSearch(c, &d.query, accountSettingEntries, mujica.SettingsSearchOptions{}).Chosen(); ok {
|
||||
if e, ok := account.SettingsSearch(c, &d.query, accountSettingEntries, account.SettingsSearchOptions{}).Chosen(); ok {
|
||||
d.page = e.Page
|
||||
log("jump to " + e.ID)
|
||||
}
|
||||
@@ -115,14 +117,14 @@ func accountSearchDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
type accountThemeState struct {
|
||||
mode mujica.Mode
|
||||
mode core.Mode
|
||||
disabled bool
|
||||
}
|
||||
|
||||
func accountThemeDemo(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "463", func() accountThemeState { return accountThemeState{} })
|
||||
accountToggles(c, log, []string{"Disabled"}, &d.disabled)
|
||||
if mujica.ThemeSelector(c, &d.mode, mujica.ThemeSelectorOptions{Disabled: d.disabled}).Changed() {
|
||||
if account.ThemeSelector(c, &d.mode, account.ThemeSelectorOptions{Disabled: d.disabled}).Changed() {
|
||||
log("theme " + d.mode.String())
|
||||
}
|
||||
}
|
||||
@@ -135,10 +137,10 @@ type accountAccentState struct {
|
||||
func accountAccentDemo(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "464", func() accountAccentState { return accountAccentState{color: ui.Hex("#7D2034")} })
|
||||
accountToggles(c, log, []string{"Disabled"}, &d.disabled)
|
||||
if mujica.AccentColorPicker(c, &d.color, mujica.AccentColorPickerOptions{Disabled: d.disabled}).Changed() {
|
||||
if account.AccentColorPicker(c, &d.color, account.AccentColorPickerOptions{Disabled: d.disabled}).Changed() {
|
||||
log(fmt.Sprintf("accent %v", d.color))
|
||||
}
|
||||
mujica.Button(c, "Preview button", mujica.ButtonOptions{}).Background(d.color)
|
||||
input.Button(c, "Preview button", input.ButtonOptions{}).Background(d.color)
|
||||
}
|
||||
|
||||
func accountToggles(c *ui.Context, log func(string), labels []string, values ...*bool) {
|
||||
|
||||
@@ -6,27 +6,27 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/account"
|
||||
)
|
||||
|
||||
type accountRecorderState struct {
|
||||
palette, zen mujica.Shortcut
|
||||
palette, zen account.Shortcut
|
||||
disabled bool
|
||||
}
|
||||
|
||||
var accountTaken = []mujica.ShortcutBinding{{Action: "Save", Shortcut: mujica.Shortcut{Mods: ui.Cmd, Key: ui.KeyS}}}
|
||||
var accountTaken = []account.ShortcutBinding{{Action: "Save", Shortcut: account.Shortcut{Mods: ui.Cmd, Key: ui.KeyS}}}
|
||||
|
||||
func accountRecorderDemo(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "465", func() accountRecorderState {
|
||||
return accountRecorderState{palette: mujica.Shortcut{Mods: ui.Cmd | ui.Shift, Key: ui.KeyP}}
|
||||
return accountRecorderState{palette: account.Shortcut{Mods: ui.Cmd | ui.Shift, Key: ui.KeyP}}
|
||||
})
|
||||
accountToggles(c, log, []string{"Disabled"}, &d.disabled)
|
||||
ui.Text(c, "Cmd/Ctrl+S is taken by Save.")
|
||||
for _, r := range []struct {
|
||||
name string
|
||||
v *mujica.Shortcut
|
||||
v *account.Shortcut
|
||||
}{{"Command palette", &d.palette}, {"Zen mode", &d.zen}} {
|
||||
if mujica.ShortcutRecorder(c, r.v, mujica.ShortcutRecorderOptions{Label: r.name, Taken: accountTaken, Disabled: d.disabled}).Changed() {
|
||||
if account.ShortcutRecorder(c, r.v, account.ShortcutRecorderOptions{Label: r.name, Taken: accountTaken, Disabled: d.disabled}).Changed() {
|
||||
log(fmt.Sprintf("%s = %s", r.name, r.v.String()))
|
||||
}
|
||||
}
|
||||
@@ -36,27 +36,27 @@ type accountShortcutsState struct{ query string }
|
||||
|
||||
func accountShortcutsDemo(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "466", func() accountShortcutsState { return accountShortcutsState{} })
|
||||
groups := []mujica.ShortcutGroup{
|
||||
{Title: "General", Bindings: []mujica.ShortcutBinding{
|
||||
{Action: "Command palette", Shortcut: mujica.Shortcut{Mods: ui.Cmd | ui.Shift, Key: ui.KeyP}},
|
||||
{Action: "Settings", Shortcut: mujica.Shortcut{Mods: ui.Cmd, Key: ui.KeyComma}},
|
||||
{Action: "Save", Shortcut: mujica.Shortcut{Mods: ui.Cmd, Key: ui.KeyS}},
|
||||
groups := []account.ShortcutGroup{
|
||||
{Title: "General", Bindings: []account.ShortcutBinding{
|
||||
{Action: "Command palette", Shortcut: account.Shortcut{Mods: ui.Cmd | ui.Shift, Key: ui.KeyP}},
|
||||
{Action: "Settings", Shortcut: account.Shortcut{Mods: ui.Cmd, Key: ui.KeyComma}},
|
||||
{Action: "Save", Shortcut: account.Shortcut{Mods: ui.Cmd, Key: ui.KeyS}},
|
||||
}},
|
||||
{Title: "Editing", Bindings: []mujica.ShortcutBinding{
|
||||
{Action: "Duplicate line", Shortcut: mujica.Shortcut{Mods: ui.Alt | ui.Shift, Key: ui.KeyDown}},
|
||||
{Action: "Toggle comment", Shortcut: mujica.Shortcut{Mods: ui.Cmd, Key: ui.KeySlash}},
|
||||
{Title: "Editing", Bindings: []account.ShortcutBinding{
|
||||
{Action: "Duplicate line", Shortcut: account.Shortcut{Mods: ui.Alt | ui.Shift, Key: ui.KeyDown}},
|
||||
{Action: "Toggle comment", Shortcut: account.Shortcut{Mods: ui.Cmd, Key: ui.KeySlash}},
|
||||
{Action: "Zen mode"},
|
||||
}},
|
||||
}
|
||||
before := d.query
|
||||
ui.Box(c).Width(480).Children(func() { mujica.KeyboardShortcutsList(c, &d.query, groups, mujica.KeyboardShortcutsListOptions{}) })
|
||||
ui.Box(c).Width(480).Children(func() { account.KeyboardShortcutsList(c, &d.query, groups, account.KeyboardShortcutsListOptions{}) })
|
||||
if d.query != before {
|
||||
log(fmt.Sprintf("search %q", d.query))
|
||||
}
|
||||
}
|
||||
|
||||
type accountLoginState struct {
|
||||
form mujica.LoginFormState
|
||||
form account.LoginFormState
|
||||
disabled, reject bool
|
||||
attempts int
|
||||
}
|
||||
@@ -71,7 +71,7 @@ func accountLoginDemo(c *ui.Context, log func(string)) {
|
||||
default:
|
||||
}
|
||||
accountToggles(c, log, []string{"Disabled", "Server rejects"}, &d.disabled, &d.reject)
|
||||
mujica.LoginForm(c, &d.form, mujica.LoginFormOptions{Disabled: d.disabled, OnSubmit: func(account, password string, remember bool) {
|
||||
account.LoginForm(c, &d.form, account.LoginFormOptions{Disabled: d.disabled, OnSubmit: func(account, password string, remember bool) {
|
||||
// Credentials stay in the caller's sign-in request.
|
||||
d.attempts++
|
||||
log(fmt.Sprintf("submit %q, remember %v", account, remember))
|
||||
@@ -98,8 +98,8 @@ type accountOAuthState struct {
|
||||
func accountOAuthDemo(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "468", func() accountOAuthState { return accountOAuthState{} })
|
||||
accountToggles(c, log, []string{"Disabled"}, &d.disabled)
|
||||
providers := []mujica.OAuthProvider{{ID: "git", Name: "Git host"}, {ID: "sso", Name: "Company SSO"}, {ID: "mail", Name: "Email link"}}
|
||||
if id, ok := mujica.OAuthButtons(c, providers, mujica.OAuthButtonsOptions{Pending: d.pending, Disabled: d.disabled}).Chosen(); ok {
|
||||
providers := []account.OAuthProvider{{ID: "git", Name: "Git host"}, {ID: "sso", Name: "Company SSO"}, {ID: "mail", Name: "Email link"}}
|
||||
if id, ok := account.OAuthButtons(c, providers, account.OAuthButtonsOptions{Pending: d.pending, Disabled: d.disabled}).Chosen(); ok {
|
||||
d.pending = id
|
||||
log("continue with " + id)
|
||||
}
|
||||
@@ -119,7 +119,7 @@ type accountTwoFactorState struct {
|
||||
func accountTwoFactorDemo(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "469", func() accountTwoFactorState { return accountTwoFactorState{} })
|
||||
accountToggles(c, log, []string{"Disabled"}, &d.disabled)
|
||||
r := mujica.TwoFactorInput(c, &d.code, mujica.TwoFactorInputOptions{ResendAt: d.resendAt, Error: d.err, Disabled: d.disabled})
|
||||
r := account.TwoFactorInput(c, &d.code, account.TwoFactorInputOptions{ResendAt: d.resendAt, Error: d.err, Disabled: d.disabled})
|
||||
if r.Completed() {
|
||||
// The code is checked here and never logged.
|
||||
d.err = ""
|
||||
@@ -135,17 +135,17 @@ func accountTwoFactorDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
type accountProfileState struct {
|
||||
saved, edit mujica.Profile
|
||||
saved, edit account.Profile
|
||||
saving, disabled bool
|
||||
}
|
||||
|
||||
func accountProfileDemo(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "470", func() accountProfileState {
|
||||
p := mujica.Profile{Name: "Sakiko Togawa", Bio: "Keyboard, Ave Mujica."}
|
||||
p := account.Profile{Name: "Sakiko Togawa", Bio: "Keyboard, Ave Mujica."}
|
||||
return accountProfileState{saved: p, edit: p}
|
||||
})
|
||||
accountToggles(c, log, []string{"Saving", "Disabled"}, &d.saving, &d.disabled)
|
||||
r := mujica.ProfileEditor(c, &d.edit, mujica.ProfileEditorOptions{Saved: d.saved, Saving: d.saving, Disabled: d.disabled})
|
||||
r := account.ProfileEditor(c, &d.edit, account.ProfileEditorOptions{Saved: d.saved, Saving: d.saving, Disabled: d.disabled})
|
||||
if r.Save() {
|
||||
d.saved = d.edit
|
||||
log("saved")
|
||||
|
||||
@@ -4,71 +4,71 @@ package main
|
||||
var accountCode = map[string]string{
|
||||
"SettingsLayout": `page := ""
|
||||
sound := true
|
||||
pages := []mujica.SettingsPage{{ID: "general", Label: "General"}, {ID: "privacy", Label: "Privacy"}}
|
||||
pages := []account.SettingsPage{{ID: "general", Label: "General"}, {ID: "privacy", Label: "Privacy"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.SettingsLayout(c, &page, pages, mujica.SettingsLayoutOptions{}, func(id string) {
|
||||
mujica.SettingsSection(c, "Notifications", func() {
|
||||
mujica.SettingsRow(c, "Sound", mujica.SettingsRowOptions{ID: "sound"}, func() *ui.Element {
|
||||
return mujica.Switch(c, &sound, "", mujica.SwitchOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
account.SettingsLayout(c, &page, pages, account.SettingsLayoutOptions{}, func(id string) {
|
||||
account.SettingsSection(c, "Notifications", func() {
|
||||
account.SettingsRow(c, "Sound", account.SettingsRowOptions{ID: "sound"}, func() *ui.Element {
|
||||
return input.Switch(c, &sound, "", input.SwitchOptions{})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"SettingsSearch": `query, page := "", "general"
|
||||
entries := []mujica.SettingEntry{{ID: "sound", Page: "general", Label: "Sound", Keywords: []string{"audio"}}}
|
||||
entries := []account.SettingEntry{{ID: "sound", Page: "general", Label: "Sound", Keywords: []string{"audio"}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if e, ok := mujica.SettingsSearch(c, &query, entries, mujica.SettingsSearchOptions{}).Chosen(); ok {
|
||||
core.Use(c, core.Settings{})
|
||||
if e, ok := account.SettingsSearch(c, &query, entries, account.SettingsSearchOptions{}).Chosen(); ok {
|
||||
page = e.Page
|
||||
}
|
||||
fmt.Println("page", page)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"ThemeSelector": `mode := mujica.System
|
||||
"ThemeSelector": `mode := core.System
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{Mode: mode})
|
||||
mujica.ThemeSelector(c, &mode, mujica.ThemeSelectorOptions{})
|
||||
core.Use(c, core.Settings{Mode: mode})
|
||||
account.ThemeSelector(c, &mode, account.ThemeSelectorOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"AccentColorPicker": `accent := ui.Hex("#7D2034")
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.AccentColorPicker(c, &accent, mujica.AccentColorPickerOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
account.AccentColorPicker(c, &accent, account.AccentColorPickerOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"ShortcutRecorder": `palette := mujica.Shortcut{Mods: ui.Cmd | ui.Shift, Key: ui.KeyP}
|
||||
"ShortcutRecorder": `palette := account.Shortcut{Mods: ui.Cmd | ui.Shift, Key: ui.KeyP}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ShortcutRecorder(c, &palette, mujica.ShortcutRecorderOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
account.ShortcutRecorder(c, &palette, account.ShortcutRecorderOptions{
|
||||
Label: "Command palette",
|
||||
Taken: []mujica.ShortcutBinding{{Action: "Save", Shortcut: mujica.Shortcut{Mods: ui.Cmd, Key: ui.KeyS}}},
|
||||
Taken: []account.ShortcutBinding{{Action: "Save", Shortcut: account.Shortcut{Mods: ui.Cmd, Key: ui.KeyS}}},
|
||||
})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"KeyboardShortcutsList": `query := ""
|
||||
groups := []mujica.ShortcutGroup{{Title: "General", Bindings: []mujica.ShortcutBinding{
|
||||
{Action: "Command palette", Shortcut: mujica.Shortcut{Mods: ui.Cmd | ui.Shift, Key: ui.KeyP}},
|
||||
groups := []account.ShortcutGroup{{Title: "General", Bindings: []account.ShortcutBinding{
|
||||
{Action: "Command palette", Shortcut: account.Shortcut{Mods: ui.Cmd | ui.Shift, Key: ui.KeyP}},
|
||||
}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.KeyboardShortcutsList(c, &query, groups, mujica.KeyboardShortcutsListOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
account.KeyboardShortcutsList(c, &query, groups, account.KeyboardShortcutsListOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"LoginForm": `var state mujica.LoginFormState
|
||||
"LoginForm": `var state account.LoginFormState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.LoginForm(c, &state, mujica.LoginFormOptions{OnSubmit: func(account, password string, remember bool) {
|
||||
core.Use(c, core.Settings{})
|
||||
account.LoginForm(c, &state, account.LoginFormOptions{OnSubmit: func(account, password string, remember bool) {
|
||||
// Submit credentials through the caller-provided auth service.
|
||||
state.EndSubmit("")
|
||||
}})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"OAuthButtons": `providers := []mujica.OAuthProvider{{ID: "sso", Name: "Company SSO"}}
|
||||
"OAuthButtons": `providers := []account.OAuthProvider{{ID: "sso", Name: "Company SSO"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if id, ok := mujica.OAuthButtons(c, providers, mujica.OAuthButtonsOptions{}).Chosen(); ok {
|
||||
core.Use(c, core.Settings{})
|
||||
if id, ok := account.OAuthButtons(c, providers, account.OAuthButtonsOptions{}).Chosen(); ok {
|
||||
fmt.Println("start sign-in with", id)
|
||||
}
|
||||
}
|
||||
@@ -76,97 +76,97 @@ ui.Render(view, 400, 300, 1)`,
|
||||
"TwoFactorInput": `code := ""
|
||||
resendAt := time.Now().Add(30 * time.Second)
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.TwoFactorInput(c, &code, mujica.TwoFactorInputOptions{ResendAt: resendAt})
|
||||
core.Use(c, core.Settings{})
|
||||
r := account.TwoFactorInput(c, &code, account.TwoFactorInputOptions{ResendAt: resendAt})
|
||||
if r.Completed() {
|
||||
fmt.Println("verify the code")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"ProfileEditor": `saved := mujica.Profile{Name: "Sakiko", Bio: "Keyboard."}
|
||||
"ProfileEditor": `saved := account.Profile{Name: "Sakiko", Bio: "Keyboard."}
|
||||
profile := saved
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.ProfileEditor(c, &profile, mujica.ProfileEditorOptions{Saved: saved}).Save() {
|
||||
core.Use(c, core.Settings{})
|
||||
if account.ProfileEditor(c, &profile, account.ProfileEditorOptions{Saved: saved}).Save() {
|
||||
saved = profile
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"AccountSwitcher": `current := "a"
|
||||
accounts := []mujica.Account{{ID: "a", Name: "Sakiko Togawa", Email: "sakiko@court"}, {ID: "b", Name: "Oblivionis"}}
|
||||
accounts := []account.Account{{ID: "a", Name: "Sakiko Togawa", Email: "sakiko@court"}, {ID: "b", Name: "Oblivionis"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.AccountSwitcher(c, ¤t, accounts, mujica.AccountSwitcherOptions{}).Add() {
|
||||
core.Use(c, core.Settings{})
|
||||
if account.AccountSwitcher(c, ¤t, accounts, account.AccountSwitcherOptions{}).Add() {
|
||||
fmt.Println("sign in another account")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"WorkspaceSwitcher": `current := "ave"
|
||||
spaces := []mujica.Workspace{{ID: "ave", Name: "Ave Mujica"}, {ID: "mygo", Name: "MyGO!!!!!"}}
|
||||
spaces := []account.Workspace{{ID: "ave", Name: "Ave Mujica"}, {ID: "mygo", Name: "MyGO!!!!!"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.WorkspaceSwitcher(c, ¤t, spaces, mujica.WorkspaceSwitcherOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
account.WorkspaceSwitcher(c, ¤t, spaces, account.WorkspaceSwitcherOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"UserMenu": `status := mujica.UserStatusOnline
|
||||
"UserMenu": `status := account.UserStatusOnline
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.UserMenu(c, &status, mujica.UserMenuOptions{Name: "Sakiko Togawa"}).SignOut() {
|
||||
core.Use(c, core.Settings{})
|
||||
if account.UserMenu(c, &status, account.UserMenuOptions{Name: "Sakiko Togawa"}).SignOut() {
|
||||
fmt.Println("sign out")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"SessionList": `sessions := []mujica.Session{{ID: "s1", Device: "MacBook Pro", Current: true}, {ID: "s2", Device: "iPhone", Location: "Osaka"}}
|
||||
"SessionList": `sessions := []account.Session{{ID: "s1", Device: "MacBook Pro", Current: true}, {ID: "s2", Device: "iPhone", Location: "Osaka"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if id, ok := mujica.SessionList(c, sessions, mujica.SessionListOptions{}).Revoked(); ok {
|
||||
core.Use(c, core.Settings{})
|
||||
if id, ok := account.SessionList(c, sessions, account.SessionListOptions{}).Revoked(); ok {
|
||||
fmt.Println("sign out", id)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"ApiKeyManager": `var state mujica.ApiKeyManagerState
|
||||
keys := []mujica.APIKey{{ID: "k1", Name: "Deploy bot", Prefix: "mk_live_9f2c"}}
|
||||
"ApiKeyManager": `var state account.ApiKeyManagerState
|
||||
keys := []account.APIKey{{ID: "k1", Name: "Deploy bot", Prefix: "mk_live_9f2c"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if name, ok := mujica.ApiKeyManager(c, &state, keys, mujica.ApiKeyManagerOptions{}).Create(); ok {
|
||||
core.Use(c, core.Settings{})
|
||||
if name, ok := account.ApiKeyManager(c, &state, keys, account.ApiKeyManagerOptions{}).Create(); ok {
|
||||
fmt.Println("create", name)
|
||||
state.Reveal("secret from your key service")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"UsageQuota": `items := []mujica.QuotaItem{{Label: "API calls", Used: 4200, Limit: 10000}, {Label: "Storage", Used: 9, Limit: 10, Unit: "GB"}}
|
||||
"UsageQuota": `items := []account.QuotaItem{{Label: "API calls", Used: 4200, Limit: 10000}, {Label: "Storage", Used: 9, Limit: 10, Unit: "GB"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.UsageQuota(c, items, mujica.UsageQuotaOptions{ResetAt: time.Now().Add(72 * time.Hour)})
|
||||
core.Use(c, core.Settings{})
|
||||
account.UsageQuota(c, items, account.UsageQuotaOptions{ResetAt: time.Now().Add(72 * time.Hour)})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"OnboardingWizard": `step := 0
|
||||
steps := []mujica.WizardStep{{Title: "Welcome"}, {Title: "Pick a theme"}, {Title: "Done"}}
|
||||
steps := []account.WizardStep{{Title: "Welcome"}, {Title: "Pick a theme"}, {Title: "Done"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.OnboardingWizard(c, &step, steps, mujica.OnboardingWizardOptions{}).Finished() {
|
||||
core.Use(c, core.Settings{})
|
||||
if account.OnboardingWizard(c, &step, steps, account.OnboardingWizardOptions{}).Finished() {
|
||||
fmt.Println("onboarded")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"Coachmark": `open, step := true, 0
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
compose := mujica.Button(c, "Compose", mujica.ButtonOptions{})
|
||||
mujica.Coachmark(c, &open, &step, []mujica.CoachmarkStep{{Anchor: compose, Title: "Write here", Body: "Start a new letter."}})
|
||||
core.Use(c, core.Settings{})
|
||||
compose := input.Button(c, "Compose", input.ButtonOptions{})
|
||||
account.Coachmark(c, &open, &step, []account.CoachmarkStep{{Anchor: compose, Title: "Write here", Body: "Start a new letter."}})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"WhatsNewDialog": `open, hide := true, false
|
||||
releases := []mujica.Release{{Version: "1.1", Changes: []mujica.ReleaseChange{{Kind: mujica.ChangeNew, Text: "500 components."}}}}
|
||||
releases := []account.Release{{Version: "1.1", Changes: []account.ReleaseChange{{Kind: account.ChangeNew, Text: "500 components."}}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.WhatsNewDialog(c, &open, &hide, releases, mujica.WhatsNewDialogOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
account.WhatsNewDialog(c, &open, &hide, releases, account.WhatsNewDialogOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"FeedbackWidget": `var state mujica.FeedbackWidgetState
|
||||
"FeedbackWidget": `var state account.FeedbackWidgetState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if f, ok := mujica.FeedbackWidget(c, &state, mujica.FeedbackWidgetOptions{}).Submitted(); ok {
|
||||
core.Use(c, core.Settings{})
|
||||
if f, ok := account.FeedbackWidget(c, &state, account.FeedbackWidgetOptions{}).Submitted(); ok {
|
||||
fmt.Println("send", f.Kind)
|
||||
state.EndSubmit("")
|
||||
}
|
||||
|
||||
@@ -6,33 +6,35 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/account"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
type accountSwitchState struct {
|
||||
account, space string
|
||||
accounts []mujica.Account
|
||||
accounts []account.Account
|
||||
disabled bool
|
||||
}
|
||||
|
||||
func accountSwitcherDemo(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "471", func() accountSwitchState {
|
||||
return accountSwitchState{account: "a", accounts: []mujica.Account{
|
||||
return accountSwitchState{account: "a", accounts: []account.Account{
|
||||
{ID: "a", Name: "Sakiko Togawa", Email: "sakiko@court"}, {ID: "b", Name: "Oblivionis", Email: "ob@mask"}}}
|
||||
})
|
||||
accountToggles(c, log, []string{"Disabled"}, &d.disabled)
|
||||
r := mujica.AccountSwitcher(c, &d.account, d.accounts, mujica.AccountSwitcherOptions{Disabled: d.disabled})
|
||||
r := account.AccountSwitcher(c, &d.account, d.accounts, account.AccountSwitcherOptions{Disabled: d.disabled})
|
||||
if r.Changed() {
|
||||
log("switched to " + d.account)
|
||||
}
|
||||
if r.Add() {
|
||||
n := len(d.accounts) + 1
|
||||
d.accounts = append(d.accounts, mujica.Account{ID: fmt.Sprint(n), Name: fmt.Sprintf("Guest %d", n)})
|
||||
d.accounts = append(d.accounts, account.Account{ID: fmt.Sprint(n), Name: fmt.Sprintf("Guest %d", n)})
|
||||
log("add account")
|
||||
}
|
||||
}
|
||||
|
||||
var accountSpaces = []mujica.Workspace{
|
||||
var accountSpaces = []account.Workspace{
|
||||
{ID: "ave", Name: "Ave Mujica", Detail: "5 members"}, {ID: "mygo", Name: "MyGO!!!!!", Detail: "5 members"},
|
||||
{ID: "crychic", Name: "CRYCHIC", Detail: "Archived"}, {ID: "sumimi", Name: "sumimi", Detail: "2 members"},
|
||||
}
|
||||
@@ -40,20 +42,20 @@ var accountSpaces = []mujica.Workspace{
|
||||
func accountWorkspaceDemo(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "472", func() accountSwitchState { return accountSwitchState{space: "ave"} })
|
||||
accountToggles(c, log, []string{"Disabled"}, &d.disabled)
|
||||
if mujica.WorkspaceSwitcher(c, &d.space, accountSpaces, mujica.WorkspaceSwitcherOptions{Disabled: d.disabled}).Changed() {
|
||||
if account.WorkspaceSwitcher(c, &d.space, accountSpaces, account.WorkspaceSwitcherOptions{Disabled: d.disabled}).Changed() {
|
||||
log("workspace " + d.space)
|
||||
}
|
||||
}
|
||||
|
||||
type accountUserState struct {
|
||||
status mujica.UserStatus
|
||||
status account.UserStatus
|
||||
disabled bool
|
||||
}
|
||||
|
||||
func accountUserMenuDemo(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "473", func() accountUserState { return accountUserState{} })
|
||||
accountToggles(c, log, []string{"Disabled"}, &d.disabled)
|
||||
r := mujica.UserMenu(c, &d.status, mujica.UserMenuOptions{Name: "Sakiko Togawa", Email: "sakiko@court", Disabled: d.disabled})
|
||||
r := account.UserMenu(c, &d.status, account.UserMenuOptions{Name: "Sakiko Togawa", Email: "sakiko@court", Disabled: d.disabled})
|
||||
if r.StatusChanged() {
|
||||
log(fmt.Sprintf("status %d", d.status))
|
||||
}
|
||||
@@ -66,20 +68,20 @@ func accountUserMenuDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
type accountSessionsState struct {
|
||||
sessions []mujica.Session
|
||||
sessions []account.Session
|
||||
disabled bool
|
||||
}
|
||||
|
||||
func accountSessionsDemo(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "474", func() accountSessionsState {
|
||||
return accountSessionsState{sessions: []mujica.Session{
|
||||
return accountSessionsState{sessions: []account.Session{
|
||||
{ID: "s1", Device: "MacBook Pro · Safari", Location: "Tokyo", LastActive: time.Now(), Current: true},
|
||||
{ID: "s2", Device: "iPhone · App", Location: "Osaka", LastActive: time.Now().Add(-26 * time.Hour)},
|
||||
{ID: "s3", Device: "Windows · Edge", Location: "Kyoto"},
|
||||
}}
|
||||
})
|
||||
accountToggles(c, log, []string{"Disabled"}, &d.disabled)
|
||||
r := mujica.SessionList(c, d.sessions, mujica.SessionListOptions{Disabled: d.disabled})
|
||||
r := account.SessionList(c, d.sessions, account.SessionListOptions{Disabled: d.disabled})
|
||||
if id, ok := r.Revoked(); ok {
|
||||
for i, s := range d.sessions {
|
||||
if s.ID == id {
|
||||
@@ -96,17 +98,17 @@ func accountSessionsDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
type accountKeysState struct {
|
||||
state mujica.ApiKeyManagerState
|
||||
keys []mujica.APIKey
|
||||
state account.ApiKeyManagerState
|
||||
keys []account.APIKey
|
||||
disabled, reject bool
|
||||
}
|
||||
|
||||
func accountKeysDemo(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "475", func() accountKeysState {
|
||||
return accountKeysState{keys: []mujica.APIKey{{ID: "k1", Name: "Deploy bot", Prefix: "mk_live_9f2c", Created: time.Now().AddDate(0, -1, 0)}}}
|
||||
return accountKeysState{keys: []account.APIKey{{ID: "k1", Name: "Deploy bot", Prefix: "mk_live_9f2c", Created: time.Now().AddDate(0, -1, 0)}}}
|
||||
})
|
||||
accountToggles(c, log, []string{"Disabled", "Create fails"}, &d.disabled, &d.reject)
|
||||
r := mujica.ApiKeyManager(c, &d.state, d.keys, mujica.ApiKeyManagerOptions{Disabled: d.disabled})
|
||||
r := account.ApiKeyManager(c, &d.state, d.keys, account.ApiKeyManagerOptions{Disabled: d.disabled})
|
||||
if name, ok := r.Create(); ok {
|
||||
if d.reject {
|
||||
d.state.Fail("Key limit reached.")
|
||||
@@ -116,7 +118,7 @@ func accountKeysDemo(c *ui.Context, log func(string)) {
|
||||
// The caller supplies the generated key.
|
||||
n := len(d.keys) + 1
|
||||
prefix := fmt.Sprintf("mk_live_%04x", n*7919)
|
||||
d.keys = append(d.keys, mujica.APIKey{ID: fmt.Sprint("k", n), Name: name, Prefix: prefix, Created: time.Now()})
|
||||
d.keys = append(d.keys, account.APIKey{ID: fmt.Sprint("k", n), Name: name, Prefix: prefix, Created: time.Now()})
|
||||
d.state.Reveal(fmt.Sprintf("%s_%x", prefix, time.Now().UnixNano()))
|
||||
log("created key " + name)
|
||||
}
|
||||
@@ -135,18 +137,18 @@ type accountQuotaState struct{ calls float64 }
|
||||
|
||||
func accountQuotaDemo(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "476", func() accountQuotaState { return accountQuotaState{calls: 4200} })
|
||||
if mujica.Slider(c, &d.calls, mujica.SliderOptions{Min: 0, Max: 12000, Step: 100, Label: "API calls used", ShowValue: true}).Changed() {
|
||||
if input.Slider(c, &d.calls, input.SliderOptions{Min: 0, Max: 12000, Step: 100, Label: "API calls used", ShowValue: true}).Changed() {
|
||||
log(fmt.Sprintf("calls %.0f", d.calls))
|
||||
}
|
||||
ui.Box(c).Width(440).Children(func() {
|
||||
mujica.UsageQuota(c, []mujica.QuotaItem{{Label: "API calls", Used: d.calls, Limit: 10000}, {Label: "Storage", Used: 9, Limit: 10, Unit: "GB"}},
|
||||
mujica.UsageQuotaOptions{ResetAt: time.Now().Add(52 * time.Hour)})
|
||||
account.UsageQuota(c, []account.QuotaItem{{Label: "API calls", Used: d.calls, Limit: 10000}, {Label: "Storage", Used: 9, Limit: 10, Unit: "GB"}},
|
||||
account.UsageQuotaOptions{ResetAt: time.Now().Add(52 * time.Hour)})
|
||||
})
|
||||
}
|
||||
|
||||
type accountWizardState struct {
|
||||
step int
|
||||
theme mujica.Mode
|
||||
theme core.Mode
|
||||
name string
|
||||
noSkip bool
|
||||
}
|
||||
@@ -154,21 +156,21 @@ type accountWizardState struct {
|
||||
func accountWizardDemo(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "477", func() accountWizardState { return accountWizardState{} })
|
||||
accountToggles(c, log, []string{"Hide Skip"}, &d.noSkip)
|
||||
steps := []mujica.WizardStep{
|
||||
steps := []account.WizardStep{
|
||||
{Title: "Welcome", Description: "Three short steps to set up your court."},
|
||||
{Title: "Your name", Ready: func() bool { return d.name != "" }, Content: func() {
|
||||
if mujica.TextInput(c, &d.name, mujica.TextInputOptions{Label: "Name", Placeholder: "Name"}).Changed() {
|
||||
if input.TextInput(c, &d.name, input.TextInputOptions{Label: "Name", Placeholder: "Name"}).Changed() {
|
||||
log("name changed")
|
||||
}
|
||||
}},
|
||||
{Title: "Pick a theme", Content: func() {
|
||||
if mujica.ThemeSelector(c, &d.theme, mujica.ThemeSelectorOptions{}).Changed() {
|
||||
if account.ThemeSelector(c, &d.theme, account.ThemeSelectorOptions{}).Changed() {
|
||||
log("theme " + d.theme.String())
|
||||
}
|
||||
}},
|
||||
}
|
||||
from := d.step
|
||||
r := mujica.OnboardingWizard(c, &d.step, steps, mujica.OnboardingWizardOptions{NoSkip: d.noSkip})
|
||||
r := account.OnboardingWizard(c, &d.step, steps, account.OnboardingWizardOptions{NoSkip: d.noSkip})
|
||||
if d.step != from {
|
||||
log(fmt.Sprintf("step %d", d.step+1))
|
||||
}
|
||||
@@ -192,8 +194,8 @@ func accountCoachDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
var compose, archive *ui.Element
|
||||
ui.Row(c).Gap(48).Padding(8).Children(func() {
|
||||
compose = mujica.Button(c, "Compose", mujica.ButtonOptions{Disabled: d.disabled})
|
||||
archive = mujica.Button(c, "Archive", mujica.ButtonOptions{Variant: mujica.Secondary})
|
||||
compose = input.Button(c, "Compose", input.ButtonOptions{Disabled: d.disabled})
|
||||
archive = input.Button(c, "Archive", input.ButtonOptions{Variant: input.Secondary})
|
||||
})
|
||||
if compose.Clicked() {
|
||||
log("compose")
|
||||
@@ -202,7 +204,7 @@ func accountCoachDemo(c *ui.Context, log func(string)) {
|
||||
log("archive")
|
||||
}
|
||||
from := d.step
|
||||
r := mujica.Coachmark(c, &d.open, &d.step, []mujica.CoachmarkStep{
|
||||
r := account.Coachmark(c, &d.open, &d.step, []account.CoachmarkStep{
|
||||
{Anchor: compose, Title: "Write here", Body: "Start a new letter."},
|
||||
{Anchor: archive, Title: "Keep it tidy", Body: "Archive threads you have finished."},
|
||||
})
|
||||
@@ -225,12 +227,12 @@ func accountWhatsNewDemo(c *ui.Context, log func(string)) {
|
||||
d.open = true
|
||||
log("open")
|
||||
}
|
||||
releases := []mujica.Release{
|
||||
{Version: "1.1", Date: "2026-10-05", Changes: []mujica.ReleaseChange{{Kind: mujica.ChangeNew, Text: "400 new components."}, {Kind: mujica.ChangeFixed, Text: "Snapshots no longer drift."}}},
|
||||
{Version: "1.0", Date: "2026-09-01", Changes: []mujica.ReleaseChange{{Kind: mujica.ChangeImproved, Text: "Court-gothic theme."}}},
|
||||
releases := []account.Release{
|
||||
{Version: "1.1", Date: "2026-10-05", Changes: []account.ReleaseChange{{Kind: account.ChangeNew, Text: "400 new components."}, {Kind: account.ChangeFixed, Text: "Snapshots no longer drift."}}},
|
||||
{Version: "1.0", Date: "2026-09-01", Changes: []account.ReleaseChange{{Kind: account.ChangeImproved, Text: "Court-gothic theme."}}},
|
||||
}
|
||||
was := d.open
|
||||
if mujica.WhatsNewDialog(c, &d.open, &d.hide, releases, mujica.WhatsNewDialogOptions{}).Changed() {
|
||||
if account.WhatsNewDialog(c, &d.open, &d.hide, releases, account.WhatsNewDialogOptions{}).Changed() {
|
||||
log(fmt.Sprintf("don't show again %v", d.hide))
|
||||
}
|
||||
if was && !d.open {
|
||||
@@ -239,7 +241,7 @@ func accountWhatsNewDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
type accountFeedbackState struct {
|
||||
state mujica.FeedbackWidgetState
|
||||
state account.FeedbackWidgetState
|
||||
disabled bool
|
||||
}
|
||||
|
||||
@@ -254,7 +256,7 @@ func accountFeedbackDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
accountToggles(c, log, []string{"Disabled"}, &d.disabled)
|
||||
before := d.state.Draft
|
||||
r := mujica.FeedbackWidget(c, &d.state, mujica.FeedbackWidgetOptions{Disabled: d.disabled, Attachments: func() {
|
||||
r := account.FeedbackWidget(c, &d.state, account.FeedbackWidgetOptions{Disabled: d.disabled, Attachments: func() {
|
||||
if tradingAction(c, "Attach screenshot") {
|
||||
log("attach screenshot")
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/agent"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
const agentCategory = "Agent"
|
||||
@@ -58,13 +59,13 @@ var agentPages = []page{
|
||||
var agentStates = []string{"Pending", "Planning", "Running", "Waiting", "Done", "Failed", "Skipped"}
|
||||
|
||||
// agentStatePick is a select over agentStates, logging changes.
|
||||
func agentStatePick(c *ui.Context, log func(string), v *string) mujica.AgentState {
|
||||
func agentStatePick(c *ui.Context, log func(string), v *string) agent.AgentState {
|
||||
if ui.Select(c, v, agentStates).Changed() {
|
||||
log("state = " + *v)
|
||||
}
|
||||
for i, s := range agentStates {
|
||||
if s == *v {
|
||||
return mujica.AgentState(i)
|
||||
return agent.AgentState(i)
|
||||
}
|
||||
}
|
||||
panic("gallery: unknown state " + *v)
|
||||
@@ -78,41 +79,41 @@ type toolCallDemoState struct {
|
||||
|
||||
func toolCallCardDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "261", func() toolCallDemoState { return toolCallDemoState{state: "Done"} })
|
||||
var st mujica.AgentState
|
||||
var st agent.AgentState
|
||||
overlayControls(c, func() {
|
||||
st = agentStatePick(c, log, &s.state)
|
||||
overlayCheck(c, log, &s.failed, "Error")
|
||||
overlayCheck(c, log, &s.still, "Still")
|
||||
})
|
||||
call := mujica.ToolCall{ID: "1", Name: "read_file", Args: `{"path":"court/ledger.md","limit":40}`,
|
||||
call := agent.ToolCall{ID: "1", Name: "read_file", Args: `{"path":"court/ledger.md","limit":40}`,
|
||||
Result: `{"lines":40,"truncated":false}`, State: st, Duration: 420 * time.Millisecond}
|
||||
if s.failed {
|
||||
call.Error = "permission denied: court/ledger.md"
|
||||
}
|
||||
if mujica.ToolCallCard(c, call, mujica.ToolCallCardOptions{Still: s.still}).Changed() {
|
||||
if agent.ToolCallCard(c, call, agent.ToolCallCardOptions{Still: s.still}).Changed() {
|
||||
log("folded or unfolded a section")
|
||||
}
|
||||
}
|
||||
|
||||
func toolCallGroupDemo(c *ui.Context, log func(string)) {
|
||||
open := ui.Local(c.Root(), "262", func() bool { return true })
|
||||
calls := []mujica.ToolCall{
|
||||
{ID: "a", Name: "list_dir", Args: `{"path":"court"}`, Result: `["ledger.md","menu.md"]`, State: mujica.AgentDone, Duration: 300 * time.Millisecond},
|
||||
{ID: "b", Name: "read_file", Args: `{"path":"court/wine.md"}`, Error: "not found", State: mujica.AgentFailed, Duration: 1200 * time.Millisecond},
|
||||
{ID: "c", Name: "grep", Args: `{"pattern":"Sakiko"}`, Result: `{"matches":3}`, State: mujica.AgentDone, Duration: 500 * time.Millisecond},
|
||||
calls := []agent.ToolCall{
|
||||
{ID: "a", Name: "list_dir", Args: `{"path":"court"}`, Result: `["ledger.md","menu.md"]`, State: agent.AgentDone, Duration: 300 * time.Millisecond},
|
||||
{ID: "b", Name: "read_file", Args: `{"path":"court/wine.md"}`, Error: "not found", State: agent.AgentFailed, Duration: 1200 * time.Millisecond},
|
||||
{ID: "c", Name: "grep", Args: `{"pattern":"Sakiko"}`, Result: `{"matches":3}`, State: agent.AgentDone, Duration: 500 * time.Millisecond},
|
||||
}
|
||||
if mujica.ToolCallGroup(c, open, calls, mujica.ToolCallGroupOptions{}).Changed() {
|
||||
if agent.ToolCallGroup(c, open, calls, agent.ToolCallGroupOptions{}).Changed() {
|
||||
log(fmt.Sprintf("group open = %v", *open))
|
||||
}
|
||||
}
|
||||
|
||||
func toolApprovalDemo(c *ui.Context, log func(string)) {
|
||||
open := ui.Local(c.Root(), "263", func() bool { return false })
|
||||
if mujica.Button(c, "Run delete_files", mujica.ButtonOptions{}).Clicked() {
|
||||
if input.Button(c, "Run delete_files", input.ButtonOptions{}).Clicked() {
|
||||
*open = true
|
||||
log("asked for approval")
|
||||
}
|
||||
r := mujica.ToolApprovalDialog(c, open, mujica.ToolApprovalOptions{Tool: "delete_files",
|
||||
r := agent.ToolApprovalDialog(c, open, agent.ToolApprovalOptions{Tool: "delete_files",
|
||||
Args: `{"glob":"build/*.tmp","dry_run":false}`, Reason: "The agent wants to clean temporary files from the build folder."})
|
||||
if r.Allowed {
|
||||
log("allowed")
|
||||
@@ -130,35 +131,35 @@ type permissionDemoState struct {
|
||||
func permissionDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "264", func() permissionDemoState { return permissionDemoState{grant: "none"} })
|
||||
ui.Row(c).Gap(12).AlignItems(ui.Center).Children(func() {
|
||||
if mujica.Button(c, "Ask to write", mujica.ButtonOptions{}).Clicked() {
|
||||
if input.Button(c, "Ask to write", input.ButtonOptions{}).Clicked() {
|
||||
s.open = true
|
||||
log("asked for permission")
|
||||
}
|
||||
ui.Text(c, "Grant: "+s.grant)
|
||||
})
|
||||
r := mujica.PermissionPrompt(c, &s.open, mujica.PermissionPromptOptions{Title: "Write to the ledger?",
|
||||
r := agent.PermissionPrompt(c, &s.open, agent.PermissionPromptOptions{Title: "Write to the ledger?",
|
||||
Description: "The agent wants to record tonight's guests.", Scope: "Write access to ~/court/ledger and its subfolders"})
|
||||
if r.Choice != mujica.PermissionNone {
|
||||
if r.Choice != agent.PermissionNone {
|
||||
s.grant = [...]string{"none", "once", "always", "denied"}[r.Choice]
|
||||
log("choice = " + s.grant)
|
||||
}
|
||||
}
|
||||
|
||||
type stepDemoState struct {
|
||||
steps []mujica.AgentStep
|
||||
steps []agent.AgentStep
|
||||
disabled bool
|
||||
}
|
||||
|
||||
func agentStepListDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "265", func() stepDemoState {
|
||||
return stepDemoState{steps: []mujica.AgentStep{
|
||||
{ID: "read", Title: "Read the guest list", State: mujica.AgentDone, Duration: 800 * time.Millisecond, Detail: "42 guests, 3 arrive late."},
|
||||
{ID: "seat", Title: "Assign the seats", State: mujica.AgentFailed, Duration: 2 * time.Second, Detail: "Table 4 is over capacity."},
|
||||
{ID: "send", Title: "Send the invitations", State: mujica.AgentPending},
|
||||
return stepDemoState{steps: []agent.AgentStep{
|
||||
{ID: "read", Title: "Read the guest list", State: agent.AgentDone, Duration: 800 * time.Millisecond, Detail: "42 guests, 3 arrive late."},
|
||||
{ID: "seat", Title: "Assign the seats", State: agent.AgentFailed, Duration: 2 * time.Second, Detail: "Table 4 is over capacity."},
|
||||
{ID: "send", Title: "Send the invitations", State: agent.AgentPending},
|
||||
}}
|
||||
})
|
||||
overlayControls(c, func() { overlayCheck(c, log, &s.disabled, "Disabled") })
|
||||
r := mujica.AgentStepList(c, s.steps, mujica.AgentStepListOptions{Disabled: s.disabled})
|
||||
r := agent.AgentStepList(c, s.steps, agent.AgentStepListOptions{Disabled: s.disabled})
|
||||
if r.Toggled != "" {
|
||||
log("toggled " + r.Toggled)
|
||||
}
|
||||
@@ -166,38 +167,38 @@ func agentStepListDemo(c *ui.Context, log func(string)) {
|
||||
log("retry " + r.Retry)
|
||||
for i := range s.steps {
|
||||
if s.steps[i].ID == r.Retry {
|
||||
s.steps[i].State, s.steps[i].Detail = mujica.AgentRunning, "Retrying with a larger table."
|
||||
s.steps[i].State, s.steps[i].Detail = agent.AgentRunning, "Retrying with a larger table."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type planDemoState struct {
|
||||
items []mujica.PlanItem
|
||||
items []agent.PlanItem
|
||||
disabled bool
|
||||
}
|
||||
|
||||
func agentPlanDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "266", func() planDemoState {
|
||||
return planDemoState{items: []mujica.PlanItem{
|
||||
{ID: "a", Text: "Read the guest list", State: mujica.AgentDone},
|
||||
{ID: "b", Text: "Assign the seats", State: mujica.AgentRunning},
|
||||
return planDemoState{items: []agent.PlanItem{
|
||||
{ID: "a", Text: "Read the guest list", State: agent.AgentDone},
|
||||
{ID: "b", Text: "Assign the seats", State: agent.AgentRunning},
|
||||
{ID: "c", Text: "Order the flowers"},
|
||||
}}
|
||||
})
|
||||
overlayControls(c, func() {
|
||||
overlayCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.Button(c, "Agent finishes a step", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Agent finishes a step", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
for i := range s.items {
|
||||
if s.items[i].State != mujica.AgentDone && s.items[i].State != mujica.AgentSkipped {
|
||||
s.items[i].State = mujica.AgentDone
|
||||
if s.items[i].State != agent.AgentDone && s.items[i].State != agent.AgentSkipped {
|
||||
s.items[i].State = agent.AgentDone
|
||||
log("agent checked " + s.items[i].Text)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if mujica.AgentPlan(c, &s.items, mujica.AgentPlanOptions{Disabled: s.disabled}).Changed() {
|
||||
if agent.AgentPlan(c, &s.items, agent.AgentPlanOptions{Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("plan changed (%d items)", len(s.items)))
|
||||
}
|
||||
}
|
||||
@@ -209,15 +210,15 @@ type statusDemoState struct {
|
||||
|
||||
func agentStatusDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "267", func() statusDemoState { return statusDemoState{state: "Running"} })
|
||||
var st mujica.AgentState
|
||||
var st agent.AgentState
|
||||
overlayControls(c, func() {
|
||||
st = agentStatePick(c, log, &s.state)
|
||||
overlayCheck(c, log, &s.still, "Still")
|
||||
})
|
||||
ui.Row(c).Gap(8).Wrap().Children(func() {
|
||||
mujica.AgentStatus(c, st, mujica.AgentStatusOptions{Still: s.still})
|
||||
for _, v := range []mujica.AgentState{mujica.AgentPlanning, mujica.AgentRunning, mujica.AgentWaiting, mujica.AgentDone, mujica.AgentFailed} {
|
||||
mujica.AgentStatus(c, v, mujica.AgentStatusOptions{Still: s.still})
|
||||
agent.AgentStatus(c, st, agent.AgentStatusOptions{Still: s.still})
|
||||
for _, v := range []agent.AgentState{agent.AgentPlanning, agent.AgentRunning, agent.AgentWaiting, agent.AgentDone, agent.AgentFailed} {
|
||||
agent.AgentStatus(c, v, agent.AgentStatusOptions{Still: s.still})
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -239,25 +240,25 @@ func agentProgressDemo(c *ui.Context, log func(string)) {
|
||||
if s.value > 0.5 {
|
||||
action = "Writing the seating chart"
|
||||
}
|
||||
mujica.AgentProgress(c, s.value, mujica.AgentProgressOptions{Label: "Preparing the ball", Action: action, Indeterminate: s.indeterminate})
|
||||
agent.AgentProgress(c, s.value, agent.AgentProgressOptions{Label: "Preparing the ball", Action: action, Indeterminate: s.indeterminate})
|
||||
}
|
||||
|
||||
func subAgentTreeDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "269", func() bool { return false })
|
||||
overlayControls(c, func() { overlayCheck(c, log, disabled, "Disabled") })
|
||||
agents := []mujica.SubAgent{{ID: "lead", Name: "Steward", Task: "Prepare the ball", State: mujica.AgentRunning, Children: []mujica.SubAgent{
|
||||
{ID: "music", Name: "Musician", Task: "Hire the quartet", State: mujica.AgentDone},
|
||||
{ID: "kitchen", Name: "Cook", Task: "Plan the menu", State: mujica.AgentWaiting, Children: []mujica.SubAgent{
|
||||
{ID: "wine", Name: "Sommelier", Task: "Choose the wine", State: mujica.AgentFailed},
|
||||
agents := []agent.SubAgent{{ID: "lead", Name: "Steward", Task: "Prepare the ball", State: agent.AgentRunning, Children: []agent.SubAgent{
|
||||
{ID: "music", Name: "Musician", Task: "Hire the quartet", State: agent.AgentDone},
|
||||
{ID: "kitchen", Name: "Cook", Task: "Plan the menu", State: agent.AgentWaiting, Children: []agent.SubAgent{
|
||||
{ID: "wine", Name: "Sommelier", Task: "Choose the wine", State: agent.AgentFailed},
|
||||
}},
|
||||
}}}
|
||||
if mujica.SubAgentTree(c, agents, mujica.SubAgentTreeOptions{Disabled: *disabled}).Changed() {
|
||||
if agent.SubAgentTree(c, agents, agent.SubAgentTreeOptions{Disabled: *disabled}).Changed() {
|
||||
log("expanded or collapsed an agent")
|
||||
}
|
||||
}
|
||||
|
||||
type fileChangeDemoState struct {
|
||||
decision mujica.FileDecision
|
||||
decision agent.FileDecision
|
||||
disabled bool
|
||||
preview float64
|
||||
}
|
||||
@@ -268,7 +269,7 @@ func fileChangeCardDemo(c *ui.Context, log func(string)) {
|
||||
overlayCheck(c, log, &s.disabled, "Disabled")
|
||||
overlaySlider(c, log, &s.preview, "Preview", "lines", 2, 12, 1)
|
||||
})
|
||||
r := mujica.FileChangeCard(c, &s.decision, agentDemoChange, mujica.FileChangeCardOptions{Preview: int(s.preview), Disabled: s.disabled})
|
||||
r := agent.FileChangeCard(c, &s.decision, agentDemoChange, agent.FileChangeCardOptions{Preview: int(s.preview), Disabled: s.disabled})
|
||||
if r.Decided {
|
||||
log(fmt.Sprintf("decision = %d", s.decision))
|
||||
}
|
||||
@@ -277,6 +278,6 @@ func fileChangeCardDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
}
|
||||
|
||||
var agentDemoChange = mujica.FileChange{Path: "court/ledger.md",
|
||||
var agentDemoChange = agent.FileChange{Path: "court/ledger.md",
|
||||
Old: "# Ledger\nGuests: 40\nWine: claret\nMusic: none\nHall: east wing\n",
|
||||
New: "# Ledger\nGuests: 42\nWine: claret\nMusic: quartet\nFlowers: lilies\nHall: east wing\n"}
|
||||
@@ -2,29 +2,29 @@ package main
|
||||
|
||||
// Code snippets of the Agent pages, the same as their compiled Examples.
|
||||
|
||||
const toolCallCardCode = `call := mujica.ToolCall{ID: "1", Name: "read_file", Args: "{\"path\":\"ledger.md\"}",
|
||||
Result: "{\"lines\":40}", State: mujica.AgentDone, Duration: 420 * time.Millisecond}
|
||||
const toolCallCardCode = `call := agent.ToolCall{ID: "1", Name: "read_file", Args: "{\"path\":\"ledger.md\"}",
|
||||
Result: "{\"lines\":40}", State: agent.AgentDone, Duration: 420 * time.Millisecond}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ToolCallCard(c, call, mujica.ToolCallCardOptions{OpenArgs: true})
|
||||
core.Use(c, core.Settings{})
|
||||
agent.ToolCallCard(c, call, agent.ToolCallCardOptions{OpenArgs: true})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const toolCallGroupCode = `open := false
|
||||
calls := []mujica.ToolCall{
|
||||
{ID: "a", Name: "list_dir", State: mujica.AgentDone, Duration: 300 * time.Millisecond},
|
||||
{ID: "b", Name: "read_file", State: mujica.AgentFailed, Error: "not found"},
|
||||
calls := []agent.ToolCall{
|
||||
{ID: "a", Name: "list_dir", State: agent.AgentDone, Duration: 300 * time.Millisecond},
|
||||
{ID: "b", Name: "read_file", State: agent.AgentFailed, Error: "not found"},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ToolCallGroup(c, &open, calls, mujica.ToolCallGroupOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
agent.ToolCallGroup(c, &open, calls, agent.ToolCallGroupOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const toolApprovalDialogCode = `open := true
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.ToolApprovalDialog(c, &open, mujica.ToolApprovalOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
r := agent.ToolApprovalDialog(c, &open, agent.ToolApprovalOptions{
|
||||
Tool: "delete_files", Args: "{\"glob\":\"*.tmp\"}", Reason: "Clean the build folder.",
|
||||
})
|
||||
if r.Allowed {
|
||||
@@ -35,149 +35,149 @@ ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const permissionPromptCode = `open := true
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.PermissionPrompt(c, &open, mujica.PermissionPromptOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
r := agent.PermissionPrompt(c, &open, agent.PermissionPromptOptions{
|
||||
Title: "Write to the ledger?", Scope: "Write access to ~/court/ledger",
|
||||
})
|
||||
if r.Choice == mujica.PermissionAllowAlways {
|
||||
if r.Choice == agent.PermissionAllowAlways {
|
||||
fmt.Println("remember the grant")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const agentStepListCode = `steps := []mujica.AgentStep{
|
||||
{ID: "read", Title: "Read the guest list", State: mujica.AgentDone, Duration: time.Second},
|
||||
{ID: "seat", Title: "Assign the seats", State: mujica.AgentFailed, Detail: "Table 4 is full."},
|
||||
{ID: "send", Title: "Send invitations", State: mujica.AgentPending},
|
||||
const agentStepListCode = `steps := []agent.AgentStep{
|
||||
{ID: "read", Title: "Read the guest list", State: agent.AgentDone, Duration: time.Second},
|
||||
{ID: "seat", Title: "Assign the seats", State: agent.AgentFailed, Detail: "Table 4 is full."},
|
||||
{ID: "send", Title: "Send invitations", State: agent.AgentPending},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if r := mujica.AgentStepList(c, steps, mujica.AgentStepListOptions{}); r.Retry != "" {
|
||||
core.Use(c, core.Settings{})
|
||||
if r := agent.AgentStepList(c, steps, agent.AgentStepListOptions{}); r.Retry != "" {
|
||||
fmt.Println("retry", r.Retry)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const agentPlanCode = `items := []mujica.PlanItem{
|
||||
{ID: "a", Text: "Read the guest list", State: mujica.AgentDone},
|
||||
{ID: "b", Text: "Assign the seats", State: mujica.AgentRunning},
|
||||
const agentPlanCode = `items := []agent.PlanItem{
|
||||
{ID: "a", Text: "Read the guest list", State: agent.AgentDone},
|
||||
{ID: "b", Text: "Assign the seats", State: agent.AgentRunning},
|
||||
{ID: "c", Text: "Order the flowers"},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.AgentPlan(c, &items, mujica.AgentPlanOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
agent.AgentPlan(c, &items, agent.AgentPlanOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const agentStatusCode = `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.AgentStatus(c, mujica.AgentWaiting, mujica.AgentStatusOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
agent.AgentStatus(c, agent.AgentWaiting, agent.AgentStatusOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const agentProgressCode = `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.AgentProgress(c, 0.4, mujica.AgentProgressOptions{Action: "Reading ledger.md"})
|
||||
core.Use(c, core.Settings{})
|
||||
agent.AgentProgress(c, 0.4, agent.AgentProgressOptions{Action: "Reading ledger.md"})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const subAgentTreeCode = `agents := []mujica.SubAgent{{ID: "lead", Name: "Steward", State: mujica.AgentRunning, Children: []mujica.SubAgent{
|
||||
{ID: "cook", Name: "Cook", Task: "Plan the menu", State: mujica.AgentWaiting},
|
||||
const subAgentTreeCode = `agents := []agent.SubAgent{{ID: "lead", Name: "Steward", State: agent.AgentRunning, Children: []agent.SubAgent{
|
||||
{ID: "cook", Name: "Cook", Task: "Plan the menu", State: agent.AgentWaiting},
|
||||
}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.SubAgentTree(c, agents, mujica.SubAgentTreeOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
agent.SubAgentTree(c, agents, agent.SubAgentTreeOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const fileChangeCardCode = `decision := mujica.FilePending
|
||||
change := mujica.FileChange{Path: "ledger.md", Old: "Guests: 40\n", New: "Guests: 42\n"}
|
||||
const fileChangeCardCode = `decision := agent.FilePending
|
||||
change := agent.FileChange{Path: "ledger.md", Old: "Guests: 40\n", New: "Guests: 42\n"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.FileChangeCard(c, &decision, change, mujica.FileChangeCardOptions{}).OpenDiff {
|
||||
core.Use(c, core.Settings{})
|
||||
if agent.FileChangeCard(c, &decision, change, agent.FileChangeCardOptions{}).OpenDiff {
|
||||
fmt.Println("open the full diff")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const multiFileDiffReviewCode = `files := []mujica.FileChange{
|
||||
const multiFileDiffReviewCode = `files := []agent.FileChange{
|
||||
{Path: "ledger.md", Old: "Guests: 40\n", New: "Guests: 42\n"},
|
||||
{Path: "menu.md", Old: "soup\n", New: "soup\ntart\n"},
|
||||
}
|
||||
decisions := make([]mujica.FileDecision, len(files))
|
||||
decisions := make([]agent.FileDecision, len(files))
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.MultiFileDiffReview(c, &decisions, files, mujica.MultiFileDiffReviewOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
agent.MultiFileDiffReview(c, &decisions, files, agent.MultiFileDiffReviewOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const commandExecutionCardCode = `run := mujica.CommandRun{Command: "go test ./...", Dir: "~/court", Running: true, Output: "\x1b[32mok\x1b[0m court\n"}
|
||||
const commandExecutionCardCode = `run := agent.CommandRun{Command: "go test ./...", Dir: "~/court", Running: true, Output: "\x1b[32mok\x1b[0m court\n"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.CommandExecutionCard(c, run, mujica.CommandExecutionCardOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if agent.CommandExecutionCard(c, run, agent.CommandExecutionCardOptions{}).Changed() {
|
||||
fmt.Println("abort the process")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const screenshotStreamCode = `var st mujica.ScreenshotStreamState
|
||||
var frames []mujica.ScreenshotFrame // appended as the agent captures them
|
||||
const screenshotStreamCode = `var st agent.ScreenshotStreamState
|
||||
var frames []agent.ScreenshotFrame // appended as the agent captures them
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ScreenshotStream(c, &st, frames, mujica.ScreenshotStreamOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
agent.ScreenshotStream(c, &st, frames, agent.ScreenshotStreamOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const artifactPanelCode = `art := mujica.Artifact{Title: "seating.go", Language: "go", Content: "package court\n"}
|
||||
const artifactPanelCode = `art := agent.Artifact{Title: "seating.go", Language: "go", Content: "package court\n"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.ArtifactPanel(c, art, mujica.ArtifactPanelOptions{}).Exported {
|
||||
core.Use(c, core.Settings{})
|
||||
if agent.ArtifactPanel(c, art, agent.ArtifactPanelOptions{}).Exported {
|
||||
fmt.Println("save the file")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const artifactVersionSwitcherCode = `current := 1
|
||||
versions := []mujica.ArtifactVersion{{Label: "v1", Note: "First draft"}, {Label: "v2", Note: "Added music"}}
|
||||
versions := []agent.ArtifactVersion{{Label: "v1", Note: "First draft"}, {Label: "v2", Note: "Added music"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.ArtifactVersionSwitcher(c, ¤t, versions, mujica.ArtifactVersionSwitcherOptions{}).Compare {
|
||||
core.Use(c, core.Settings{})
|
||||
if agent.ArtifactVersionSwitcher(c, ¤t, versions, agent.ArtifactVersionSwitcherOptions{}).Compare {
|
||||
fmt.Println("diff v1 and v2")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const sandboxStatusCode = `info := mujica.SandboxInfo{Name: "sandbox-7", State: mujica.SandboxRunning,
|
||||
Resources: []mujica.SandboxResource{{Name: "CPU", Used: 1.5, Limit: 2, Unit: "cores"}}}
|
||||
const sandboxStatusCode = `info := agent.SandboxInfo{Name: "sandbox-7", State: agent.SandboxRunning,
|
||||
Resources: []agent.SandboxResource{{Name: "CPU", Used: 1.5, Limit: 2, Unit: "cores"}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.SandboxStatus(c, info, mujica.SandboxStatusOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
agent.SandboxStatus(c, info, agent.SandboxStatusOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const mcpServerListCode = `servers := []mujica.MCPServer{{ID: "fs", Name: "filesystem", Transport: "stdio", State: mujica.MCPConnected, Tools: 12}}
|
||||
const mcpServerListCode = `servers := []agent.MCPServer{{ID: "fs", Name: "filesystem", Transport: "stdio", State: agent.MCPConnected, Tools: 12}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if r := mujica.MCPServerList(c, servers, mujica.MCPServerListOptions{}); r.Action == mujica.MCPStop {
|
||||
core.Use(c, core.Settings{})
|
||||
if r := agent.MCPServerList(c, servers, agent.MCPServerListOptions{}); r.Action == agent.MCPStop {
|
||||
fmt.Println("stop", r.Server)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const memoryPanelCode = `entries := []mujica.MemoryEntry{{ID: "1", Text: "Lady Sakiko prefers the east wing.", Source: "Chat"}}
|
||||
const memoryPanelCode = `entries := []agent.MemoryEntry{{ID: "1", Text: "Lady Sakiko prefers the east wing.", Source: "Chat"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if r := mujica.MemoryPanel(c, &entries, mujica.MemoryPanelOptions{}); r.Deleted != "" {
|
||||
core.Use(c, core.Settings{})
|
||||
if r := agent.MemoryPanel(c, &entries, agent.MemoryPanelOptions{}); r.Deleted != "" {
|
||||
fmt.Println("forget", r.Deleted)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const checkpointListCode = `current := "c2"
|
||||
points := []mujica.Checkpoint{{ID: "c2", Label: "Menu drafted"}, {ID: "c1", Label: "Start"}}
|
||||
points := []agent.Checkpoint{{ID: "c2", Label: "Menu drafted"}, {ID: "c1", Label: "Start"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.CheckpointList(c, ¤t, points, mujica.CheckpointListOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if agent.CheckpointList(c, ¤t, points, agent.CheckpointListOptions{}).Changed() {
|
||||
fmt.Println("restore", current)
|
||||
}
|
||||
}
|
||||
@@ -185,8 +185,8 @@ ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const humanInputRequestCode = `answer := ""
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.HumanInputRequest(c, &answer, mujica.HumanInputRequestOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
r := agent.HumanInputRequest(c, &answer, agent.HumanInputRequestOptions{
|
||||
Question: "Which hall?", Options: []string{"East wing", "Great hall"}, AllowFree: true,
|
||||
})
|
||||
if r.Submitted {
|
||||
|
||||
@@ -7,15 +7,17 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/agent"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/display"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
type multiFileDemoState struct {
|
||||
decisions []mujica.FileDecision
|
||||
decisions []agent.FileDecision
|
||||
disabled bool
|
||||
}
|
||||
|
||||
var agentDemoFiles = []mujica.FileChange{
|
||||
var agentDemoFiles = []agent.FileChange{
|
||||
agentDemoChange,
|
||||
{Path: "court/menu.md", Old: "soup\nroast\n", New: "soup\nroast\ntart\n"},
|
||||
{Path: "court/old-plan.md", Old: "Hold the ball in the cellar.\n"},
|
||||
@@ -23,44 +25,44 @@ var agentDemoFiles = []mujica.FileChange{
|
||||
|
||||
func multiFileDiffDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "271", func() multiFileDemoState {
|
||||
return multiFileDemoState{decisions: make([]mujica.FileDecision, len(agentDemoFiles))}
|
||||
return multiFileDemoState{decisions: make([]agent.FileDecision, len(agentDemoFiles))}
|
||||
})
|
||||
overlayControls(c, func() {
|
||||
overlayCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.Button(c, "Reset", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Reset", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
clear(s.decisions)
|
||||
log("reset")
|
||||
}
|
||||
})
|
||||
if mujica.MultiFileDiffReview(c, &s.decisions, agentDemoFiles, mujica.MultiFileDiffReviewOptions{Disabled: s.disabled}).Changed() {
|
||||
if agent.MultiFileDiffReview(c, &s.decisions, agentDemoFiles, agent.MultiFileDiffReviewOptions{Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("decisions = %v", s.decisions))
|
||||
}
|
||||
}
|
||||
|
||||
type commandDemoState struct {
|
||||
run mujica.CommandRun
|
||||
run agent.CommandRun
|
||||
disabled bool
|
||||
lines int
|
||||
}
|
||||
|
||||
func commandExecutionDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "272", func() commandDemoState {
|
||||
return commandDemoState{run: mujica.CommandRun{Command: "go test ./court/...", Dir: "~/projects/court", Running: true}}
|
||||
return commandDemoState{run: agent.CommandRun{Command: "go test ./court/...", Dir: "~/projects/court", Running: true}}
|
||||
})
|
||||
overlayControls(c, func() {
|
||||
overlayCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.Button(c, "Print a line", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Print a line", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.lines++
|
||||
color := []string{"32mok ", "33mwarn", "31mFAIL"}[s.lines%3]
|
||||
s.run.Output += fmt.Sprintf("\x1b[%s\x1b[0m court/case%d 0.%02ds\n", color, s.lines, s.lines)
|
||||
log("printed a line")
|
||||
}
|
||||
if mujica.Button(c, "Restart", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Restart", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.run.Running, s.run.Output, s.lines = true, "", 0
|
||||
log("restarted")
|
||||
}
|
||||
})
|
||||
if mujica.CommandExecutionCard(c, s.run, mujica.CommandExecutionCardOptions{Disabled: s.disabled}).Changed() {
|
||||
if agent.CommandExecutionCard(c, s.run, agent.CommandExecutionCardOptions{Disabled: s.disabled}).Changed() {
|
||||
s.run.Running, s.run.ExitCode, s.run.Duration = false, 130, time.Duration(s.lines)*time.Second
|
||||
s.run.Output += "\x1b[31maborted by user\x1b[0m\n"
|
||||
log("abort requested")
|
||||
@@ -68,13 +70,13 @@ func commandExecutionDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
type screenshotDemoState struct {
|
||||
st mujica.ScreenshotStreamState
|
||||
frames []mujica.ScreenshotFrame
|
||||
st agent.ScreenshotStreamState
|
||||
frames []agent.ScreenshotFrame
|
||||
disabled bool
|
||||
}
|
||||
|
||||
// agentDemoFrame paints a frame with a moving block.
|
||||
func agentDemoFrame(i int) mujica.ScreenshotFrame {
|
||||
func agentDemoFrame(i int) agent.ScreenshotFrame {
|
||||
img := image.NewRGBA(image.Rect(0, 0, 320, 180))
|
||||
for y := range 180 {
|
||||
for x := range 320 {
|
||||
@@ -86,21 +88,21 @@ func agentDemoFrame(i int) mujica.ScreenshotFrame {
|
||||
img.Pix[o], img.Pix[o+1], img.Pix[o+2], img.Pix[o+3] = v, v-10, v-20, 255
|
||||
}
|
||||
}
|
||||
return mujica.ScreenshotFrame{Image: ui.NewBitmap(img), Caption: fmt.Sprintf("Clicked button %d", i+1), At: time.Duration(i) * 2 * time.Second}
|
||||
return agent.ScreenshotFrame{Image: ui.NewBitmap(img), Caption: fmt.Sprintf("Clicked button %d", i+1), At: time.Duration(i) * 2 * time.Second}
|
||||
}
|
||||
|
||||
func screenshotStreamDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "273", func() screenshotDemoState {
|
||||
return screenshotDemoState{frames: []mujica.ScreenshotFrame{agentDemoFrame(0), agentDemoFrame(1)}}
|
||||
return screenshotDemoState{frames: []agent.ScreenshotFrame{agentDemoFrame(0), agentDemoFrame(1)}}
|
||||
})
|
||||
overlayControls(c, func() {
|
||||
overlayCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.Button(c, "Capture a frame", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Capture a frame", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.frames = append(s.frames, agentDemoFrame(len(s.frames)))
|
||||
log(fmt.Sprintf("frame %d arrived", len(s.frames)))
|
||||
}
|
||||
})
|
||||
if mujica.ScreenshotStream(c, &s.st, s.frames, mujica.ScreenshotStreamOptions{Height: 220, Disabled: s.disabled}).Changed() {
|
||||
if agent.ScreenshotStream(c, &s.st, s.frames, agent.ScreenshotStreamOptions{Height: 220, Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("frame %d, paused = %v", s.st.Index+1, s.st.Paused))
|
||||
}
|
||||
}
|
||||
@@ -118,17 +120,17 @@ func artifactPanelDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
overlayCheck(c, log, &s.disabled, "Disabled")
|
||||
})
|
||||
art := mujica.Artifact{Title: "seating.go", Language: "go",
|
||||
art := agent.Artifact{Title: "seating.go", Language: "go",
|
||||
Content: "package court\n\n// Seats maps guests to tables.\nfunc Seats() map[string]int {\n\treturn map[string]int{\"Sakiko\": 1, \"Uika\": 2}\n}\n"}
|
||||
opts := mujica.ArtifactPanelOptions{Height: 200, Disabled: s.disabled}
|
||||
opts := agent.ArtifactPanelOptions{Height: 200, Disabled: s.disabled}
|
||||
if s.kind == "Document" {
|
||||
art = mujica.Artifact{Title: "Invitation", Kind: mujica.ArtifactDocument, Content: "# You are invited\n\nThe masquerade begins at nine.\n"}
|
||||
art = agent.Artifact{Title: "Invitation", Kind: agent.ArtifactDocument, Content: "# You are invited\n\nThe masquerade begins at nine.\n"}
|
||||
opts.Preview = func() {
|
||||
mujica.Heading(c, "You are invited", mujica.HeadingOptions{})
|
||||
display.Heading(c, "You are invited", display.HeadingOptions{})
|
||||
ui.Text(c, "The masquerade begins at nine.")
|
||||
}
|
||||
}
|
||||
r := mujica.ArtifactPanel(c, art, opts)
|
||||
r := agent.ArtifactPanel(c, art, opts)
|
||||
if r.Copied {
|
||||
log("copied to the clipboard")
|
||||
}
|
||||
@@ -145,8 +147,8 @@ type versionDemoState struct {
|
||||
func artifactVersionDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "275", func() versionDemoState { return versionDemoState{current: 2} })
|
||||
overlayControls(c, func() { overlayCheck(c, log, &s.disabled, "Disabled") })
|
||||
versions := []mujica.ArtifactVersion{{Label: "v1", Note: "First draft"}, {Label: "v2", Note: "Added the quartet"}, {Label: "v3", Note: "Moved the wine table"}}
|
||||
r := mujica.ArtifactVersionSwitcher(c, &s.current, versions, mujica.ArtifactVersionSwitcherOptions{Disabled: s.disabled})
|
||||
versions := []agent.ArtifactVersion{{Label: "v1", Note: "First draft"}, {Label: "v2", Note: "Added the quartet"}, {Label: "v3", Note: "Moved the wine table"}}
|
||||
r := agent.ArtifactVersionSwitcher(c, &s.current, versions, agent.ArtifactVersionSwitcherOptions{Disabled: s.disabled})
|
||||
if r.Changed {
|
||||
log("version = " + versions[s.current].Label)
|
||||
}
|
||||
@@ -166,43 +168,43 @@ func sandboxStatusDemo(c *ui.Context, log func(string)) {
|
||||
return sandboxDemoState{state: "Running", cpu: 1.2, expires: time.Now().Add(10 * time.Minute)}
|
||||
})
|
||||
names := []string{"Starting", "Running", "Stopped", "Failed"}
|
||||
state := mujica.SandboxStarting
|
||||
state := agent.SandboxStarting
|
||||
overlayControls(c, func() {
|
||||
if ui.Select(c, &s.state, names).Changed() {
|
||||
log("state = " + s.state)
|
||||
}
|
||||
overlaySlider(c, log, &s.cpu, "CPU", "cores", 0, 2, 0.1)
|
||||
if mujica.Button(c, "Extend 5 min", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Extend 5 min", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.expires = s.expires.Add(5 * time.Minute)
|
||||
log("extended")
|
||||
}
|
||||
})
|
||||
for i, n := range names {
|
||||
if n == s.state {
|
||||
state = mujica.SandboxState(i)
|
||||
state = agent.SandboxState(i)
|
||||
}
|
||||
}
|
||||
mujica.SandboxStatus(c, mujica.SandboxInfo{Name: "sandbox-7", State: state, Expires: s.expires, Resources: []mujica.SandboxResource{
|
||||
agent.SandboxStatus(c, agent.SandboxInfo{Name: "sandbox-7", State: state, Expires: s.expires, Resources: []agent.SandboxResource{
|
||||
{Name: "CPU", Used: s.cpu, Limit: 2, Unit: "cores"}, {Name: "Memory", Used: 3.1, Limit: 4, Unit: "GB"},
|
||||
}}, mujica.SandboxStatusOptions{})
|
||||
}}, agent.SandboxStatusOptions{})
|
||||
}
|
||||
|
||||
type mcpDemoState struct {
|
||||
servers []mujica.MCPServer
|
||||
servers []agent.MCPServer
|
||||
disabled bool
|
||||
}
|
||||
|
||||
func mcpServerListDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "277", func() mcpDemoState {
|
||||
return mcpDemoState{servers: []mujica.MCPServer{
|
||||
{ID: "fs", Name: "filesystem", Transport: "stdio", State: mujica.MCPConnected, Tools: 12},
|
||||
{ID: "gh", Name: "github", Transport: "http", State: mujica.MCPError, Error: "401 Unauthorized"},
|
||||
{ID: "db", Name: "postgres", Transport: "stdio", State: mujica.MCPStopped},
|
||||
return mcpDemoState{servers: []agent.MCPServer{
|
||||
{ID: "fs", Name: "filesystem", Transport: "stdio", State: agent.MCPConnected, Tools: 12},
|
||||
{ID: "gh", Name: "github", Transport: "http", State: agent.MCPError, Error: "401 Unauthorized"},
|
||||
{ID: "db", Name: "postgres", Transport: "stdio", State: agent.MCPStopped},
|
||||
}}
|
||||
})
|
||||
overlayControls(c, func() { overlayCheck(c, log, &s.disabled, "Disabled") })
|
||||
r := mujica.MCPServerList(c, s.servers, mujica.MCPServerListOptions{Disabled: s.disabled})
|
||||
if r.Action == mujica.MCPNone {
|
||||
r := agent.MCPServerList(c, s.servers, agent.MCPServerListOptions{Disabled: s.disabled})
|
||||
if r.Action == agent.MCPNone {
|
||||
return
|
||||
}
|
||||
log(fmt.Sprintf("%s: %s", r.Server, [...]string{"", "start", "stop", "reconnect"}[r.Action]))
|
||||
@@ -211,29 +213,29 @@ func mcpServerListDemo(c *ui.Context, log func(string)) {
|
||||
continue
|
||||
}
|
||||
switch r.Action {
|
||||
case mujica.MCPStop:
|
||||
s.servers[i].State = mujica.MCPStopped
|
||||
case agent.MCPStop:
|
||||
s.servers[i].State = agent.MCPStopped
|
||||
default:
|
||||
s.servers[i].State, s.servers[i].Tools, s.servers[i].Error = mujica.MCPConnected, 5, ""
|
||||
s.servers[i].State, s.servers[i].Tools, s.servers[i].Error = agent.MCPConnected, 5, ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type memoryDemoState struct {
|
||||
entries []mujica.MemoryEntry
|
||||
entries []agent.MemoryEntry
|
||||
disabled bool
|
||||
}
|
||||
|
||||
func memoryPanelDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "278", func() memoryDemoState {
|
||||
return memoryDemoState{entries: []mujica.MemoryEntry{
|
||||
return memoryDemoState{entries: []agent.MemoryEntry{
|
||||
{ID: "1", Text: "Lady Sakiko prefers the east wing.", Source: "Chat, 3 Oct"},
|
||||
{ID: "2", Text: "The quartet needs two days' notice.", Source: "Plan, 1 Oct"},
|
||||
{ID: "3", Text: "Claret is served after nine.", Source: "Ledger"},
|
||||
}}
|
||||
})
|
||||
overlayControls(c, func() { overlayCheck(c, log, &s.disabled, "Disabled") })
|
||||
r := mujica.MemoryPanel(c, &s.entries, mujica.MemoryPanelOptions{Disabled: s.disabled})
|
||||
r := agent.MemoryPanel(c, &s.entries, agent.MemoryPanelOptions{Disabled: s.disabled})
|
||||
if r.Edited != "" {
|
||||
log("edited " + r.Edited)
|
||||
}
|
||||
@@ -250,9 +252,9 @@ type checkpointDemoState struct {
|
||||
func checkpointListDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "279", func() checkpointDemoState { return checkpointDemoState{current: "c3"} })
|
||||
overlayControls(c, func() { overlayCheck(c, log, &s.disabled, "Disabled") })
|
||||
points := []mujica.Checkpoint{{ID: "c3", Label: "Seats assigned", Note: "12 files changed"},
|
||||
points := []agent.Checkpoint{{ID: "c3", Label: "Seats assigned", Note: "12 files changed"},
|
||||
{ID: "c2", Label: "Menu drafted", Note: "3 files changed"}, {ID: "c1", Label: "Start"}}
|
||||
if mujica.CheckpointList(c, &s.current, points, mujica.CheckpointListOptions{Disabled: s.disabled}).Changed() {
|
||||
if agent.CheckpointList(c, &s.current, points, agent.CheckpointListOptions{Disabled: s.disabled}).Changed() {
|
||||
log("rolled back to " + s.current)
|
||||
}
|
||||
}
|
||||
@@ -269,12 +271,12 @@ func humanInputDemo(c *ui.Context, log func(string)) {
|
||||
overlayControls(c, func() {
|
||||
overlayCheck(c, log, &s.free, "AllowFree")
|
||||
overlayCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.Button(c, "Start a 30 s timer", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Start a 30 s timer", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.deadline = time.Now().Add(30 * time.Second)
|
||||
log("timer started")
|
||||
}
|
||||
})
|
||||
r := mujica.HumanInputRequest(c, &s.answer, mujica.HumanInputRequestOptions{Question: "Which hall should host the masquerade?",
|
||||
r := agent.HumanInputRequest(c, &s.answer, agent.HumanInputRequestOptions{Question: "Which hall should host the masquerade?",
|
||||
Options: []string{"East wing", "Great hall"}, AllowFree: s.free, Deadline: s.deadline, Disabled: s.disabled})
|
||||
if r.Submitted {
|
||||
log("answer sent: " + s.answer)
|
||||
|
||||
@@ -2,85 +2,85 @@ package main
|
||||
|
||||
// Code snippets mirror example_calendar_test.go.
|
||||
const (
|
||||
calCalendarMonthViewCode = `func exampleEvents(loc *time.Location) []mujica.CalendarEvent {
|
||||
calCalendarMonthViewCode = `func exampleEvents(loc *time.Location) []datetime.CalendarEvent {
|
||||
start := time.Date(2026, time.October, 14, 9, 0, 0, 0, loc)
|
||||
return []mujica.CalendarEvent{
|
||||
return []datetime.CalendarEvent{
|
||||
{ID: "rehearsal", Title: "Rehearsal", Start: start, End: start.Add(2 * time.Hour), Location: "Hall"},
|
||||
{ID: "fitting", Title: "Fitting", Start: start.Add(time.Hour), End: start.Add(90 * time.Minute), Color: mujica.EventInfo},
|
||||
{ID: "fitting", Title: "Fitting", Start: start.Add(time.Hour), End: start.Add(90 * time.Minute), Color: datetime.EventInfo},
|
||||
}
|
||||
}
|
||||
|
||||
loc := time.Local
|
||||
events := exampleEvents(loc)
|
||||
month := mujica.YearMonth{Year: 2026, Month: time.October}
|
||||
month := datetime.YearMonth{Year: 2026, Month: time.October}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.CalendarMonthView(c, &month, events, mujica.CalendarMonthViewOptions{Location: loc})
|
||||
core.Use(c, core.Settings{})
|
||||
r := datetime.CalendarMonthView(c, &month, events, datetime.CalendarMonthViewOptions{Location: loc})
|
||||
if ch, ok := r.Changed(); ok {
|
||||
fmt.Println("move", ch.ID, "to", ch.Start)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calCalendarWeekViewCode = `func exampleEvents(loc *time.Location) []mujica.CalendarEvent {
|
||||
calCalendarWeekViewCode = `func exampleEvents(loc *time.Location) []datetime.CalendarEvent {
|
||||
start := time.Date(2026, time.October, 14, 9, 0, 0, 0, loc)
|
||||
return []mujica.CalendarEvent{
|
||||
return []datetime.CalendarEvent{
|
||||
{ID: "rehearsal", Title: "Rehearsal", Start: start, End: start.Add(2 * time.Hour), Location: "Hall"},
|
||||
{ID: "fitting", Title: "Fitting", Start: start.Add(time.Hour), End: start.Add(90 * time.Minute), Color: mujica.EventInfo},
|
||||
{ID: "fitting", Title: "Fitting", Start: start.Add(time.Hour), End: start.Add(90 * time.Minute), Color: datetime.EventInfo},
|
||||
}
|
||||
}
|
||||
|
||||
loc := time.Local
|
||||
events := exampleEvents(loc)
|
||||
week := mujica.Date{Year: 2026, Month: time.October, Day: 14}
|
||||
week := core.Date{Year: 2026, Month: time.October, Day: 14}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.CalendarWeekView(c, &week, events, mujica.CalendarWeekViewOptions{Location: loc, SlotMinutes: 15})
|
||||
if ch, ok := r.Changed(); ok && ch.Kind == mujica.CalendarCreate {
|
||||
core.Use(c, core.Settings{})
|
||||
r := datetime.CalendarWeekView(c, &week, events, datetime.CalendarWeekViewOptions{Location: loc, SlotMinutes: 15})
|
||||
if ch, ok := r.Changed(); ok && ch.Kind == datetime.CalendarCreate {
|
||||
fmt.Println("new event", ch.Start, ch.End)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calCalendarDayViewCode = `func exampleEvents(loc *time.Location) []mujica.CalendarEvent {
|
||||
calCalendarDayViewCode = `func exampleEvents(loc *time.Location) []datetime.CalendarEvent {
|
||||
start := time.Date(2026, time.October, 14, 9, 0, 0, 0, loc)
|
||||
return []mujica.CalendarEvent{
|
||||
return []datetime.CalendarEvent{
|
||||
{ID: "rehearsal", Title: "Rehearsal", Start: start, End: start.Add(2 * time.Hour), Location: "Hall"},
|
||||
{ID: "fitting", Title: "Fitting", Start: start.Add(time.Hour), End: start.Add(90 * time.Minute), Color: mujica.EventInfo},
|
||||
{ID: "fitting", Title: "Fitting", Start: start.Add(time.Hour), End: start.Add(90 * time.Minute), Color: datetime.EventInfo},
|
||||
}
|
||||
}
|
||||
|
||||
loc := time.Local
|
||||
events := exampleEvents(loc)
|
||||
day := mujica.Date{Year: 2026, Month: time.October, Day: 14}
|
||||
day := core.Date{Year: 2026, Month: time.October, Day: 14}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if id, ok := mujica.CalendarDayView(c, &day, events, mujica.CalendarDayViewOptions{Location: loc}).Opened(); ok {
|
||||
core.Use(c, core.Settings{})
|
||||
if id, ok := datetime.CalendarDayView(c, &day, events, datetime.CalendarDayViewOptions{Location: loc}).Opened(); ok {
|
||||
fmt.Println("open", id)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calAgendaViewCode = `func exampleEvents(loc *time.Location) []mujica.CalendarEvent {
|
||||
calAgendaViewCode = `func exampleEvents(loc *time.Location) []datetime.CalendarEvent {
|
||||
start := time.Date(2026, time.October, 14, 9, 0, 0, 0, loc)
|
||||
return []mujica.CalendarEvent{
|
||||
return []datetime.CalendarEvent{
|
||||
{ID: "rehearsal", Title: "Rehearsal", Start: start, End: start.Add(2 * time.Hour), Location: "Hall"},
|
||||
{ID: "fitting", Title: "Fitting", Start: start.Add(time.Hour), End: start.Add(90 * time.Minute), Color: mujica.EventInfo},
|
||||
{ID: "fitting", Title: "Fitting", Start: start.Add(time.Hour), End: start.Add(90 * time.Minute), Color: datetime.EventInfo},
|
||||
}
|
||||
}
|
||||
|
||||
loc := time.Local
|
||||
events := exampleEvents(loc)
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
opts := mujica.AgendaViewOptions{Location: loc, From: mujica.Date{Year: 2026, Month: time.October, Day: 12}, Days: 14}
|
||||
if id, ok := mujica.AgendaView(c, events, opts).Opened(); ok {
|
||||
core.Use(c, core.Settings{})
|
||||
opts := datetime.AgendaViewOptions{Location: loc, From: core.Date{Year: 2026, Month: time.October, Day: 12}, Days: 14}
|
||||
if id, ok := datetime.AgendaView(c, events, opts).Opened(); ok {
|
||||
fmt.Println("open", id)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calYearViewCode = `func exampleEvents(loc *time.Location) []mujica.CalendarEvent {
|
||||
calYearViewCode = `func exampleEvents(loc *time.Location) []datetime.CalendarEvent {
|
||||
start := time.Date(2026, time.October, 14, 9, 0, 0, 0, loc)
|
||||
return []mujica.CalendarEvent{
|
||||
return []datetime.CalendarEvent{
|
||||
{ID: "rehearsal", Title: "Rehearsal", Start: start, End: start.Add(2 * time.Hour), Location: "Hall"},
|
||||
{ID: "fitting", Title: "Fitting", Start: start.Add(time.Hour), End: start.Add(90 * time.Minute), Color: mujica.EventInfo},
|
||||
{ID: "fitting", Title: "Fitting", Start: start.Add(time.Hour), End: start.Add(90 * time.Minute), Color: datetime.EventInfo},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,52 +88,52 @@ loc := time.Local
|
||||
events := exampleEvents(loc)
|
||||
year := 2026
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if day, ok := mujica.YearView(c, &year, events, mujica.YearViewOptions{Location: loc}).DayChosen(); ok {
|
||||
core.Use(c, core.Settings{})
|
||||
if day, ok := datetime.YearView(c, &year, events, datetime.YearViewOptions{Location: loc}).DayChosen(); ok {
|
||||
fmt.Println("go to", day)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calEventChipCode = `func exampleEvents(loc *time.Location) []mujica.CalendarEvent {
|
||||
calEventChipCode = `func exampleEvents(loc *time.Location) []datetime.CalendarEvent {
|
||||
start := time.Date(2026, time.October, 14, 9, 0, 0, 0, loc)
|
||||
return []mujica.CalendarEvent{
|
||||
return []datetime.CalendarEvent{
|
||||
{ID: "rehearsal", Title: "Rehearsal", Start: start, End: start.Add(2 * time.Hour), Location: "Hall"},
|
||||
{ID: "fitting", Title: "Fitting", Start: start.Add(time.Hour), End: start.Add(90 * time.Minute), Color: mujica.EventInfo},
|
||||
{ID: "fitting", Title: "Fitting", Start: start.Add(time.Hour), End: start.Add(90 * time.Minute), Color: datetime.EventInfo},
|
||||
}
|
||||
}
|
||||
|
||||
loc := time.Local
|
||||
ev := exampleEvents(loc)[0]
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.EventChip(c, ev, mujica.EventChipOptions{Location: loc}).Clicked() {
|
||||
core.Use(c, core.Settings{})
|
||||
if datetime.EventChip(c, ev, datetime.EventChipOptions{Location: loc}).Clicked() {
|
||||
fmt.Println("open", ev.ID)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calEventEditorCode = `func exampleEvents(loc *time.Location) []mujica.CalendarEvent {
|
||||
calEventEditorCode = `func exampleEvents(loc *time.Location) []datetime.CalendarEvent {
|
||||
start := time.Date(2026, time.October, 14, 9, 0, 0, 0, loc)
|
||||
return []mujica.CalendarEvent{
|
||||
return []datetime.CalendarEvent{
|
||||
{ID: "rehearsal", Title: "Rehearsal", Start: start, End: start.Add(2 * time.Hour), Location: "Hall"},
|
||||
{ID: "fitting", Title: "Fitting", Start: start.Add(time.Hour), End: start.Add(90 * time.Minute), Color: mujica.EventInfo},
|
||||
{ID: "fitting", Title: "Fitting", Start: start.Add(time.Hour), End: start.Add(90 * time.Minute), Color: datetime.EventInfo},
|
||||
}
|
||||
}
|
||||
|
||||
loc := time.Local
|
||||
ev := exampleEvents(loc)[0]
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.EventEditor(c, &ev, mujica.EventEditorOptions{Location: loc})
|
||||
core.Use(c, core.Settings{})
|
||||
r := datetime.EventEditor(c, &ev, datetime.EventEditorOptions{Location: loc})
|
||||
if r.Saved() {
|
||||
fmt.Println("saved", ev.Title, ev.Start)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calEventPopoverCode = `func exampleEvents(loc *time.Location) []mujica.CalendarEvent {
|
||||
calEventPopoverCode = `func exampleEvents(loc *time.Location) []datetime.CalendarEvent {
|
||||
start := time.Date(2026, time.October, 14, 9, 0, 0, 0, loc)
|
||||
return []mujica.CalendarEvent{
|
||||
return []datetime.CalendarEvent{
|
||||
{ID: "rehearsal", Title: "Rehearsal", Start: start, End: start.Add(2 * time.Hour), Location: "Hall"},
|
||||
{ID: "fitting", Title: "Fitting", Start: start.Add(time.Hour), End: start.Add(90 * time.Minute), Color: mujica.EventInfo},
|
||||
{ID: "fitting", Title: "Fitting", Start: start.Add(time.Hour), End: start.Add(90 * time.Minute), Color: datetime.EventInfo},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,88 +141,88 @@ loc := time.Local
|
||||
ev := exampleEvents(loc)[0]
|
||||
open := false
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
chip := mujica.EventChip(c, ev, mujica.EventChipOptions{Location: loc})
|
||||
core.Use(c, core.Settings{})
|
||||
chip := datetime.EventChip(c, ev, datetime.EventChipOptions{Location: loc})
|
||||
if chip.Clicked() {
|
||||
open = true
|
||||
}
|
||||
r := mujica.EventPopover(c, chip, &open, ev, mujica.EventPopoverOptions{Location: loc})
|
||||
r := datetime.EventPopover(c, chip, &open, ev, datetime.EventPopoverOptions{Location: loc})
|
||||
if r.Delete() {
|
||||
fmt.Println("delete", ev.ID)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calCurrentTimeIndicatorCode = `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
core.Use(c, core.Settings{})
|
||||
ui.Box(c).Size(200, 240).Children(func() {
|
||||
mujica.CurrentTimeIndicator(c, mujica.CurrentTimeIndicatorOptions{Location: time.Local})
|
||||
datetime.CurrentTimeIndicator(c, datetime.CurrentTimeIndicatorOptions{Location: time.Local})
|
||||
})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calRecurrenceEditorCode = `rule := mujica.Recurrence{Unit: mujica.RepeatWeekly, Interval: 1, Weekdays: []time.Weekday{time.Monday}}
|
||||
calRecurrenceEditorCode = `rule := datetime.Recurrence{Unit: datetime.RepeatWeekly, Interval: 1, Weekdays: []time.Weekday{time.Monday}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.RecurrenceEditor(c, &rule, mujica.RecurrenceEditorOptions{Start: mujica.Date{Year: 2026, Month: time.October, Day: 5}})
|
||||
core.Use(c, core.Settings{})
|
||||
r := datetime.RecurrenceEditor(c, &rule, datetime.RecurrenceEditorOptions{Start: core.Date{Year: 2026, Month: time.October, Day: 5}})
|
||||
if r.Changed() && r.Problem() == "" {
|
||||
fmt.Println(mujica.RecurrenceSummary(c, rule))
|
||||
fmt.Println(datetime.RecurrenceSummary(c, rule))
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calAvailabilityPickerCode = `days := []mujica.Date{{Year: 2026, Month: time.October, Day: 12}, {Year: 2026, Month: time.October, Day: 13}}
|
||||
calAvailabilityPickerCode = `days := []core.Date{{Year: 2026, Month: time.October, Day: 12}, {Year: 2026, Month: time.October, Day: 13}}
|
||||
mine := [][]bool{make([]bool, 18), make([]bool, 18)}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.AvailabilityPicker(c, &mine, mujica.AvailabilityPickerOptions{Days: days}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if datetime.AvailabilityPicker(c, &mine, datetime.AvailabilityPickerOptions{Days: days}).Changed() {
|
||||
fmt.Println("availability changed")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calTimezoneSelectCode = `zone := "Asia/Shanghai"
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.TimezoneSelect(c, &zone, mujica.TimezoneSelectOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if datetime.TimezoneSelect(c, &zone, datetime.TimezoneSelectOptions{}).Changed() {
|
||||
fmt.Println("zone", zone)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calReminderPickerCode = `reminders := []time.Duration{15 * time.Minute}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.ReminderPicker(c, &reminders, mujica.ReminderPickerOptions{Max: 3}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if datetime.ReminderPicker(c, &reminders, datetime.ReminderPickerOptions{Max: 3}).Changed() {
|
||||
fmt.Println("reminders", reminders)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calAttendeeListCode = `people := []mujica.Attendee{{Name: "Sakiko", Status: mujica.AttendeeAccepted, Organizer: true}, {Name: "Mutsumi"}}
|
||||
calAttendeeListCode = `people := []datetime.Attendee{{Name: "Sakiko", Status: datetime.AttendeeAccepted, Organizer: true}, {Name: "Mutsumi"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if i, ok := mujica.AttendeeList(c, people, mujica.AttendeeListOptions{Removable: true}).Removed(); ok {
|
||||
core.Use(c, core.Settings{})
|
||||
if i, ok := datetime.AttendeeList(c, people, datetime.AttendeeListOptions{Removable: true}).Removed(); ok {
|
||||
people = append(people[:i], people[i+1:]...)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calWeekPickerCode = `var week mujica.Date
|
||||
calWeekPickerCode = `var week core.Date
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.WeekPicker(c, &week, mujica.WeekPickerOptions{WeekStart: mujica.WeekStartMonday, Clearable: true}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if datetime.WeekPicker(c, &week, datetime.WeekPickerOptions{WeekStart: datetime.WeekStartMonday, Clearable: true}).Changed() {
|
||||
fmt.Println("week of", week)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calDurationPickerCode = `length := 90 * time.Minute
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
opts := mujica.DurationPickerOptions{Min: 15 * time.Minute, Max: 8 * time.Hour}
|
||||
if mujica.DurationPicker(c, &length, opts).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
opts := datetime.DurationPickerOptions{Min: 15 * time.Minute, Max: 8 * time.Hour}
|
||||
if datetime.DurationPicker(c, &length, opts).Changed() {
|
||||
fmt.Println("length", length)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calCronEditorCode = `expr := "0 9 * * MON-FRI"
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.CronEditor(c, &expr, mujica.CronEditorOptions{Location: time.Local}).Changed() {
|
||||
if _, err := mujica.ParseCron(expr); err == nil {
|
||||
core.Use(c, core.Settings{})
|
||||
if datetime.CronEditor(c, &expr, datetime.CronEditorOptions{Location: time.Local}).Changed() {
|
||||
if _, err := datetime.ParseCron(expr); err == nil {
|
||||
fmt.Println("schedule", expr)
|
||||
}
|
||||
}
|
||||
@@ -230,22 +230,22 @@ view := func(c *ui.Context) {
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calRelativeTimeCode = `posted := time.Date(2026, time.October, 5, 9, 0, 0, 0, time.Local)
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.RelativeTime(c, posted, mujica.RelativeTimeOptions{Location: time.Local})
|
||||
core.Use(c, core.Settings{})
|
||||
datetime.RelativeTime(c, posted, datetime.RelativeTimeOptions{Location: time.Local})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calCountdownCode = `deadline := time.Date(2026, time.December, 31, 23, 59, 59, 0, time.Local)
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.Countdown(c, deadline, mujica.CountdownOptions{}).Finished() {
|
||||
core.Use(c, core.Settings{})
|
||||
if datetime.Countdown(c, deadline, datetime.CountdownOptions{}).Finished() {
|
||||
fmt.Println("happy new year")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
calStopwatchCode = `var sw mujica.StopwatchState
|
||||
calStopwatchCode = `var sw datetime.StopwatchState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.Stopwatch(c, &sw, mujica.StopwatchOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if datetime.Stopwatch(c, &sw, datetime.StopwatchOptions{}).Changed() {
|
||||
fmt.Println("laps", len(sw.Laps))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/datetime"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
var calColors = []string{"Accent", "Info", "Success", "Warning", "Danger", "Neutral"}
|
||||
@@ -20,19 +22,19 @@ func calChipDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "426", func() state { return state{color: "Accent"} })
|
||||
dtSection(c, "EventChip", func() {
|
||||
dtControls(c, func() {
|
||||
mujica.Select(c, &s.color, calOptions(calColors), mujica.SelectOptions{Label: "Color"})
|
||||
mujica.Checkbox(c, &s.inline, "Inline", mujica.CheckboxOptions{})
|
||||
mujica.Checkbox(c, &s.selected, "Selected", mujica.CheckboxOptions{})
|
||||
mujica.Checkbox(c, &s.disabled, "Disabled", mujica.CheckboxOptions{})
|
||||
input.Select(c, &s.color, calOptions(calColors), input.SelectOptions{Label: "Color"})
|
||||
input.Checkbox(c, &s.inline, "Inline", input.CheckboxOptions{})
|
||||
input.Checkbox(c, &s.selected, "Selected", input.CheckboxOptions{})
|
||||
input.Checkbox(c, &s.disabled, "Disabled", input.CheckboxOptions{})
|
||||
})
|
||||
ev := calSample()[0]
|
||||
for i, n := range calColors {
|
||||
if n == s.color {
|
||||
ev.Color = mujica.EventColor(i)
|
||||
ev.Color = datetime.EventColor(i)
|
||||
}
|
||||
}
|
||||
ui.Box(c).Width(240).Children(func() {
|
||||
if mujica.EventChip(c, ev, mujica.EventChipOptions{Location: time.Local, Inline: s.inline, Selected: s.selected, Disabled: s.disabled}).Clicked() {
|
||||
if datetime.EventChip(c, ev, datetime.EventChipOptions{Location: time.Local, Inline: s.inline, Selected: s.selected, Disabled: s.disabled}).Clicked() {
|
||||
log("clicked " + ev.Title)
|
||||
}
|
||||
})
|
||||
@@ -41,13 +43,13 @@ func calChipDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func calEditorDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
ev mujica.CalendarEvent
|
||||
ev datetime.CalendarEvent
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "427", func() state { return state{ev: calSample()[0]} })
|
||||
dtSection(c, "EventEditor", func() {
|
||||
dtControls(c, func() { mujica.Checkbox(c, &s.disabled, "Disabled", mujica.CheckboxOptions{}) })
|
||||
r := mujica.EventEditor(c, &s.ev, mujica.EventEditorOptions{Location: time.Local, Disabled: s.disabled})
|
||||
dtControls(c, func() { input.Checkbox(c, &s.disabled, "Disabled", input.CheckboxOptions{}) })
|
||||
r := datetime.EventEditor(c, &s.ev, datetime.EventEditorOptions{Location: time.Local, Disabled: s.disabled})
|
||||
if r.Saved() {
|
||||
log(fmt.Sprintf("saved %q %s, %d guests", s.ev.Title, s.ev.Start.Format("Jan 2 15:04"), len(s.ev.Attendees)))
|
||||
}
|
||||
@@ -64,19 +66,19 @@ func calPopoverDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "428", func() state { return state{} })
|
||||
dtSection(c, "EventPopover", func() {
|
||||
dtControls(c, func() {
|
||||
mujica.Checkbox(c, &s.readOnly, "Read-only", mujica.CheckboxOptions{})
|
||||
mujica.Checkbox(c, &s.disabled, "Disabled", mujica.CheckboxOptions{})
|
||||
input.Checkbox(c, &s.readOnly, "Read-only", input.CheckboxOptions{})
|
||||
input.Checkbox(c, &s.disabled, "Disabled", input.CheckboxOptions{})
|
||||
})
|
||||
ev := calSample()[0]
|
||||
ev.Attendees = []mujica.Attendee{{Name: "Sakiko", Status: mujica.AttendeeAccepted, Organizer: true}, {Name: "Mutsumi"}}
|
||||
ev.Attendees = []datetime.Attendee{{Name: "Sakiko", Status: datetime.AttendeeAccepted, Organizer: true}, {Name: "Mutsumi"}}
|
||||
ev.Reminders = []time.Duration{15 * time.Minute}
|
||||
ui.Box(c).Width(240).Children(func() {
|
||||
chip := mujica.EventChip(c, ev, mujica.EventChipOptions{Location: time.Local, Disabled: s.disabled})
|
||||
chip := datetime.EventChip(c, ev, datetime.EventChipOptions{Location: time.Local, Disabled: s.disabled})
|
||||
if chip.Clicked() {
|
||||
s.open = true
|
||||
log("opened popover")
|
||||
}
|
||||
r := mujica.EventPopover(c, chip, &s.open, ev, mujica.EventPopoverOptions{Location: time.Local, ReadOnly: s.readOnly, Disabled: s.disabled})
|
||||
r := datetime.EventPopover(c, chip, &s.open, ev, datetime.EventPopoverOptions{Location: time.Local, ReadOnly: s.readOnly, Disabled: s.disabled})
|
||||
if r.Edit() {
|
||||
log("edit " + ev.ID)
|
||||
}
|
||||
@@ -89,24 +91,24 @@ func calPopoverDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func calNowDemo(c *ui.Context, log func(string)) {
|
||||
dtSection(c, "CurrentTimeIndicator", func() {
|
||||
k := mujica.Tokens(c)
|
||||
k := core.Tokens(c)
|
||||
ui.Box(c).Size(320, 240).Border(1, k.Border).Children(func() {
|
||||
mujica.CurrentTimeIndicator(c, mujica.CurrentTimeIndicatorOptions{Location: time.Local})
|
||||
datetime.CurrentTimeIndicator(c, datetime.CurrentTimeIndicatorOptions{Location: time.Local})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func calRecurrenceDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
rule mujica.Recurrence
|
||||
rule datetime.Recurrence
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "430", func() state { return state{} })
|
||||
dtSection(c, "RecurrenceEditor", func() {
|
||||
dtControls(c, func() { mujica.Checkbox(c, &s.disabled, "Disabled", mujica.CheckboxOptions{}) })
|
||||
r := mujica.RecurrenceEditor(c, &s.rule, mujica.RecurrenceEditorOptions{Start: mujica.DateOf(time.Now()), Disabled: s.disabled})
|
||||
dtControls(c, func() { input.Checkbox(c, &s.disabled, "Disabled", input.CheckboxOptions{}) })
|
||||
r := datetime.RecurrenceEditor(c, &s.rule, datetime.RecurrenceEditorOptions{Start: core.DateOf(time.Now()), Disabled: s.disabled})
|
||||
if r.Changed() {
|
||||
log(mujica.RecurrenceSummary(c, s.rule) + strings.TrimSpace(" "+r.Problem()))
|
||||
log(datetime.RecurrenceSummary(c, s.rule) + strings.TrimSpace(" "+r.Problem()))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -114,11 +116,11 @@ func calRecurrenceDemo(c *ui.Context, log func(string)) {
|
||||
func calAvailabilityDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
mine, theirs [][]bool
|
||||
days []mujica.Date
|
||||
days []core.Date
|
||||
others, frozen bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "431", func() state {
|
||||
today := mujica.DateOf(time.Now())
|
||||
today := core.DateOf(time.Now())
|
||||
grid := func(seed int) [][]bool {
|
||||
g := make([][]bool, 5)
|
||||
for i := range g {
|
||||
@@ -129,19 +131,19 @@ func calAvailabilityDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
return g
|
||||
}
|
||||
days := []mujica.Date{today, today.AddDays(1), today.AddDays(2), today.AddDays(3), today.AddDays(4)}
|
||||
days := []core.Date{today, today.AddDays(1), today.AddDays(2), today.AddDays(3), today.AddDays(4)}
|
||||
return state{mine: grid(2), theirs: grid(1), days: days, others: true}
|
||||
})
|
||||
dtSection(c, "AvailabilityPicker", func() {
|
||||
dtControls(c, func() {
|
||||
mujica.Checkbox(c, &s.others, "Show Mutsumi", mujica.CheckboxOptions{})
|
||||
mujica.Checkbox(c, &s.frozen, "Disabled", mujica.CheckboxOptions{})
|
||||
input.Checkbox(c, &s.others, "Show Mutsumi", input.CheckboxOptions{})
|
||||
input.Checkbox(c, &s.frozen, "Disabled", input.CheckboxOptions{})
|
||||
})
|
||||
var others []mujica.AvailabilityPerson
|
||||
var others []datetime.AvailabilityPerson
|
||||
if s.others {
|
||||
others = []mujica.AvailabilityPerson{{Name: "Mutsumi", Free: s.theirs}}
|
||||
others = []datetime.AvailabilityPerson{{Name: "Mutsumi", Free: s.theirs}}
|
||||
}
|
||||
if mujica.AvailabilityPicker(c, &s.mine, mujica.AvailabilityPickerOptions{Days: s.days, Others: others, Disabled: s.frozen}).Changed() {
|
||||
if datetime.AvailabilityPicker(c, &s.mine, datetime.AvailabilityPickerOptions{Days: s.days, Others: others, Disabled: s.frozen}).Changed() {
|
||||
n := 0
|
||||
for _, d := range s.mine {
|
||||
for _, v := range d {
|
||||
@@ -162,8 +164,8 @@ func calTimezoneDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
s := ui.Local(c.Root(), "432", func() state { return state{zone: "Asia/Shanghai"} })
|
||||
dtSection(c, "TimezoneSelect", func() {
|
||||
dtControls(c, func() { mujica.Checkbox(c, &s.disabled, "Disabled", mujica.CheckboxOptions{}) })
|
||||
if mujica.TimezoneSelect(c, &s.zone, mujica.TimezoneSelectOptions{Disabled: s.disabled}).Changed() {
|
||||
dtControls(c, func() { input.Checkbox(c, &s.disabled, "Disabled", input.CheckboxOptions{}) })
|
||||
if datetime.TimezoneSelect(c, &s.zone, datetime.TimezoneSelectOptions{Disabled: s.disabled}).Changed() {
|
||||
log("zone " + s.zone)
|
||||
}
|
||||
})
|
||||
@@ -178,13 +180,13 @@ func calReminderDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "433", func() state { return state{v: []time.Duration{15 * time.Minute}, max: "0"} })
|
||||
dtSection(c, "ReminderPicker", func() {
|
||||
dtControls(c, func() {
|
||||
mujica.Select(c, &s.max, calOptions([]string{"0", "2", "3"}), mujica.SelectOptions{Label: "Max (0 = any)"})
|
||||
mujica.Checkbox(c, &s.disabled, "Disabled", mujica.CheckboxOptions{})
|
||||
input.Select(c, &s.max, calOptions([]string{"0", "2", "3"}), input.SelectOptions{Label: "Max (0 = any)"})
|
||||
input.Checkbox(c, &s.disabled, "Disabled", input.CheckboxOptions{})
|
||||
})
|
||||
if limit := calAtoi(s.max); limit > 0 && len(s.v) > limit {
|
||||
s.v = s.v[:limit]
|
||||
}
|
||||
if mujica.ReminderPicker(c, &s.v, mujica.ReminderPickerOptions{Max: calAtoi(s.max), Disabled: s.disabled}).Changed() {
|
||||
if datetime.ReminderPicker(c, &s.v, datetime.ReminderPickerOptions{Max: calAtoi(s.max), Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprint("reminders ", s.v))
|
||||
}
|
||||
})
|
||||
@@ -192,24 +194,24 @@ func calReminderDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func calAttendeeDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
list []mujica.Attendee
|
||||
list []datetime.Attendee
|
||||
removable bool
|
||||
}
|
||||
people := func() []mujica.Attendee {
|
||||
return []mujica.Attendee{{Name: "Sakiko Togawa", Status: mujica.AttendeeAccepted, Organizer: true},
|
||||
{Name: "Mutsumi Wakaba", Status: mujica.AttendeeTentative}, {Name: "Uika Misumi", Status: mujica.AttendeeDeclined},
|
||||
people := func() []datetime.Attendee {
|
||||
return []datetime.Attendee{{Name: "Sakiko Togawa", Status: datetime.AttendeeAccepted, Organizer: true},
|
||||
{Name: "Mutsumi Wakaba", Status: datetime.AttendeeTentative}, {Name: "Uika Misumi", Status: datetime.AttendeeDeclined},
|
||||
{Name: "Nyamu Yuutenji", Email: "nyamu@example.com"}}
|
||||
}
|
||||
s := ui.Local(c.Root(), "434", func() state { return state{list: people(), removable: true} })
|
||||
dtSection(c, "AttendeeList", func() {
|
||||
dtControls(c, func() {
|
||||
mujica.Checkbox(c, &s.removable, "Removable", mujica.CheckboxOptions{})
|
||||
if mujica.Button(c, "Reset", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
input.Checkbox(c, &s.removable, "Removable", input.CheckboxOptions{})
|
||||
if input.Button(c, "Reset", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.list = people()
|
||||
log("reset")
|
||||
}
|
||||
})
|
||||
if i, ok := mujica.AttendeeList(c, s.list, mujica.AttendeeListOptions{Removable: s.removable}).Removed(); ok {
|
||||
if i, ok := datetime.AttendeeList(c, s.list, datetime.AttendeeListOptions{Removable: s.removable}).Removed(); ok {
|
||||
log("removed " + s.list[i].Name)
|
||||
s.list = append(s.list[:i:i], s.list[i+1:]...)
|
||||
}
|
||||
|
||||
@@ -5,26 +5,28 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/datetime"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func calWeekPickerDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
week mujica.Date
|
||||
week core.Date
|
||||
weekStart string
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "435", func() state { return state{weekStart: "Locale"} })
|
||||
dtSection(c, "WeekPicker", func() {
|
||||
dtControls(c, func() {
|
||||
if mujica.Select(c, &s.weekStart, calOptions(calWeekStarts), mujica.SelectOptions{Label: "Week start"}).Changed() {
|
||||
s.week = mujica.Date{}
|
||||
if input.Select(c, &s.weekStart, calOptions(calWeekStarts), input.SelectOptions{Label: "Week start"}).Changed() {
|
||||
s.week = core.Date{}
|
||||
log("week start " + s.weekStart + ", value cleared")
|
||||
}
|
||||
mujica.Checkbox(c, &s.disabled, "Disabled", mujica.CheckboxOptions{})
|
||||
input.Checkbox(c, &s.disabled, "Disabled", input.CheckboxOptions{})
|
||||
})
|
||||
opts := mujica.WeekPickerOptions{WeekStart: calWeekStart(s.weekStart), Clearable: true, Disabled: s.disabled}
|
||||
if mujica.WeekPicker(c, &s.week, opts).Changed() {
|
||||
opts := datetime.WeekPickerOptions{WeekStart: calWeekStart(s.weekStart), Clearable: true, Disabled: s.disabled}
|
||||
if datetime.WeekPicker(c, &s.week, opts).Changed() {
|
||||
log("week of " + s.week.String())
|
||||
}
|
||||
})
|
||||
@@ -38,11 +40,11 @@ func calDurationDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "436", func() state { return state{v: 90 * time.Minute} })
|
||||
dtSection(c, "DurationPicker", func() {
|
||||
dtControls(c, func() {
|
||||
mujica.Checkbox(c, &s.seconds, "Seconds", mujica.CheckboxOptions{})
|
||||
mujica.Checkbox(c, &s.disabled, "Disabled", mujica.CheckboxOptions{})
|
||||
input.Checkbox(c, &s.seconds, "Seconds", input.CheckboxOptions{})
|
||||
input.Checkbox(c, &s.disabled, "Disabled", input.CheckboxOptions{})
|
||||
})
|
||||
opts := mujica.DurationPickerOptions{Seconds: s.seconds, Min: 15 * time.Minute, Max: 8 * time.Hour, Disabled: s.disabled}
|
||||
if mujica.DurationPicker(c, &s.v, opts).Changed() {
|
||||
opts := datetime.DurationPickerOptions{Seconds: s.seconds, Min: 15 * time.Minute, Max: 8 * time.Hour, Disabled: s.disabled}
|
||||
if datetime.DurationPicker(c, &s.v, opts).Changed() {
|
||||
log("duration " + s.v.String())
|
||||
}
|
||||
})
|
||||
@@ -55,9 +57,9 @@ func calCronDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
s := ui.Local(c.Root(), "437", func() state { return state{expr: "*/15 9-17 * * MON-FRI"} })
|
||||
dtSection(c, "CronEditor", func() {
|
||||
dtControls(c, func() { mujica.Checkbox(c, &s.disabled, "Disabled", mujica.CheckboxOptions{}) })
|
||||
if mujica.CronEditor(c, &s.expr, mujica.CronEditorOptions{Location: time.Local, Disabled: s.disabled}).Changed() {
|
||||
if _, err := mujica.ParseCron(s.expr); err != nil {
|
||||
dtControls(c, func() { input.Checkbox(c, &s.disabled, "Disabled", input.CheckboxOptions{}) })
|
||||
if datetime.CronEditor(c, &s.expr, datetime.CronEditorOptions{Location: time.Local, Disabled: s.disabled}).Changed() {
|
||||
if _, err := datetime.ParseCron(s.expr); err != nil {
|
||||
log("invalid: " + s.expr)
|
||||
} else {
|
||||
log("schedule " + s.expr)
|
||||
@@ -75,13 +77,13 @@ func calRelativeDemo(c *ui.Context, log func(string)) {
|
||||
label string
|
||||
d time.Duration
|
||||
}{{"Now", 0}, {"5 minutes ago", -5 * time.Minute}, {"In 3 hours", 3 * time.Hour}, {"40 days ago", -40 * 24 * time.Hour}} {
|
||||
if mujica.Button(c, b.label, mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, b.label, input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.at = time.Now().Add(b.d)
|
||||
log("time set: " + b.label)
|
||||
}
|
||||
}
|
||||
})
|
||||
mujica.RelativeTime(c, s.at, mujica.RelativeTimeOptions{Location: time.Local})
|
||||
datetime.RelativeTime(c, s.at, datetime.RelativeTimeOptions{Location: time.Local})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -93,17 +95,17 @@ func calCountdownDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "439", func() state { return state{deadline: time.Now().Add(10 * time.Second)} })
|
||||
dtSection(c, "Countdown", func() {
|
||||
dtControls(c, func() {
|
||||
if mujica.Button(c, "Restart 10 s", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Restart 10 s", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.deadline = time.Now().Add(10 * time.Second)
|
||||
log("restarted")
|
||||
}
|
||||
mujica.Checkbox(c, &s.words, "Words", mujica.CheckboxOptions{})
|
||||
input.Checkbox(c, &s.words, "Words", input.CheckboxOptions{})
|
||||
})
|
||||
format := mujica.CountdownClock
|
||||
format := datetime.CountdownClock
|
||||
if s.words {
|
||||
format = mujica.CountdownWords
|
||||
format = datetime.CountdownWords
|
||||
}
|
||||
if mujica.Countdown(c, s.deadline, mujica.CountdownOptions{Format: format}).Finished() {
|
||||
if datetime.Countdown(c, s.deadline, datetime.CountdownOptions{Format: format}).Finished() {
|
||||
log("finished")
|
||||
}
|
||||
})
|
||||
@@ -111,18 +113,18 @@ func calCountdownDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func calStopwatchDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
sw mujica.StopwatchState
|
||||
sw datetime.StopwatchState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "440", func() state { return state{} })
|
||||
dtSection(c, "Stopwatch", func() {
|
||||
dtControls(c, func() { mujica.Checkbox(c, &s.disabled, "Disabled", mujica.CheckboxOptions{}) })
|
||||
if mujica.Stopwatch(c, &s.sw, mujica.StopwatchOptions{Disabled: s.disabled}).Changed() {
|
||||
dtControls(c, func() { input.Checkbox(c, &s.disabled, "Disabled", input.CheckboxOptions{}) })
|
||||
if datetime.Stopwatch(c, &s.sw, datetime.StopwatchOptions{Disabled: s.disabled}).Changed() {
|
||||
state := "paused"
|
||||
if s.sw.Running() {
|
||||
state = "running"
|
||||
}
|
||||
log(state + ", " + mujica.StopwatchText(s.sw.Elapsed(time.Now())))
|
||||
log(state + ", " + datetime.StopwatchText(s.sw.Elapsed(time.Now())))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -7,39 +7,41 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/datetime"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
// calSample is demo events around today in the local zone.
|
||||
func calSample() []mujica.CalendarEvent {
|
||||
func calSample() []datetime.CalendarEvent {
|
||||
now := time.Now()
|
||||
day := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
|
||||
at := func(d, h, m, mins int) (time.Time, time.Time) {
|
||||
s := day.AddDate(0, 0, d).Add(time.Duration(h)*time.Hour + time.Duration(m)*time.Minute)
|
||||
return s, s.Add(time.Duration(mins) * time.Minute)
|
||||
}
|
||||
mk := func(id, title string, color mujica.EventColor, d, h, m, mins int) mujica.CalendarEvent {
|
||||
mk := func(id, title string, color datetime.EventColor, d, h, m, mins int) datetime.CalendarEvent {
|
||||
s, e := at(d, h, m, mins)
|
||||
return mujica.CalendarEvent{ID: id, Title: title, Color: color, Start: s, End: e, Location: "Rehearsal hall"}
|
||||
return datetime.CalendarEvent{ID: id, Title: title, Color: color, Start: s, End: e, Location: "Rehearsal hall"}
|
||||
}
|
||||
evs := []mujica.CalendarEvent{
|
||||
mk("1", "Rehearsal", mujica.EventAccent, 0, 9, 0, 120), mk("2", "Costume fitting", mujica.EventInfo, 0, 10, 0, 60),
|
||||
mk("3", "Lunch", mujica.EventSuccess, 0, 12, 30, 60), mk("4", "Lyrics review", mujica.EventWarning, 1, 14, 0, 90),
|
||||
mk("5", "Press", mujica.EventDanger, 2, 16, 0, 45), mk("6", "Sound check", mujica.EventNeutral, 3, 18, 0, 60),
|
||||
mk("7", "Encore notes", mujica.EventInfo, 0, 15, 0, 30), mk("8", "Stage walk", mujica.EventAccent, 0, 17, 0, 30),
|
||||
evs := []datetime.CalendarEvent{
|
||||
mk("1", "Rehearsal", datetime.EventAccent, 0, 9, 0, 120), mk("2", "Costume fitting", datetime.EventInfo, 0, 10, 0, 60),
|
||||
mk("3", "Lunch", datetime.EventSuccess, 0, 12, 30, 60), mk("4", "Lyrics review", datetime.EventWarning, 1, 14, 0, 90),
|
||||
mk("5", "Press", datetime.EventDanger, 2, 16, 0, 45), mk("6", "Sound check", datetime.EventNeutral, 3, 18, 0, 60),
|
||||
mk("7", "Encore notes", datetime.EventInfo, 0, 15, 0, 30), mk("8", "Stage walk", datetime.EventAccent, 0, 17, 0, 30),
|
||||
}
|
||||
holiday := mk("9", "Masquerade", mujica.EventAccent, 4, 0, 0, 24*60)
|
||||
holiday := mk("9", "Masquerade", datetime.EventAccent, 4, 0, 0, 24*60)
|
||||
holiday.AllDay, holiday.Start = true, day.AddDate(0, 0, 4)
|
||||
holiday.End = holiday.Start.AddDate(0, 0, 1)
|
||||
return append(evs, holiday)
|
||||
}
|
||||
|
||||
// calApply applies and logs a view change.
|
||||
func calApply(evs *[]mujica.CalendarEvent, ch mujica.CalendarChange, log func(string)) {
|
||||
func calApply(evs *[]datetime.CalendarEvent, ch datetime.CalendarChange, log func(string)) {
|
||||
switch ch.Kind {
|
||||
case mujica.CalendarCreate:
|
||||
case datetime.CalendarCreate:
|
||||
id := fmt.Sprint(len(*evs) + 100)
|
||||
*evs = append(*evs, mujica.CalendarEvent{ID: id, Title: "New event", Start: ch.Start, End: ch.End})
|
||||
*evs = append(*evs, datetime.CalendarEvent{ID: id, Title: "New event", Start: ch.Start, End: ch.End})
|
||||
log("created " + ch.Start.Format("Jan 2 15:04") + " – " + ch.End.Format("15:04"))
|
||||
return
|
||||
}
|
||||
@@ -49,14 +51,14 @@ func calApply(evs *[]mujica.CalendarEvent, ch mujica.CalendarChange, log func(st
|
||||
}
|
||||
}
|
||||
verb := "moved"
|
||||
if ch.Kind == mujica.CalendarResize {
|
||||
if ch.Kind == datetime.CalendarResize {
|
||||
verb = "resized"
|
||||
}
|
||||
log(fmt.Sprintf("%s %s to %s – %s", verb, ch.ID, ch.Start.Format("Jan 2 15:04"), ch.End.Format("15:04")))
|
||||
}
|
||||
|
||||
// calViewResult logs what a view reported and applies its change.
|
||||
func calViewResult(r mujica.CalendarViewResult, evs *[]mujica.CalendarEvent, version *int, log func(string)) {
|
||||
func calViewResult(r datetime.CalendarViewResult, evs *[]datetime.CalendarEvent, version *int, log func(string)) {
|
||||
if id, ok := r.Opened(); ok {
|
||||
log("opened " + id)
|
||||
}
|
||||
@@ -71,10 +73,10 @@ func calViewResult(r mujica.CalendarViewResult, evs *[]mujica.CalendarEvent, ver
|
||||
|
||||
var calWeekStarts = []string{"Locale", "Sunday", "Monday", "Saturday"}
|
||||
|
||||
func calWeekStart(s string) mujica.WeekStart {
|
||||
func calWeekStart(s string) datetime.WeekStart {
|
||||
for i, n := range calWeekStarts {
|
||||
if n == s {
|
||||
return mujica.WeekStart(i)
|
||||
return datetime.WeekStart(i)
|
||||
}
|
||||
}
|
||||
panic("gallery: unknown week start " + s)
|
||||
@@ -82,24 +84,24 @@ func calWeekStart(s string) mujica.WeekStart {
|
||||
|
||||
func calMonthDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
month mujica.YearMonth
|
||||
events []mujica.CalendarEvent
|
||||
month datetime.YearMonth
|
||||
events []datetime.CalendarEvent
|
||||
version, maxRows int
|
||||
weekStart, maxStr string
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "421", func() state {
|
||||
now := time.Now()
|
||||
return state{month: mujica.YearMonth{Year: now.Year(), Month: now.Month()}, events: calSample(), weekStart: "Locale", maxStr: "3"}
|
||||
return state{month: datetime.YearMonth{Year: now.Year(), Month: now.Month()}, events: calSample(), weekStart: "Locale", maxStr: "3"}
|
||||
})
|
||||
dtSection(c, "CalendarMonthView", func() {
|
||||
dtControls(c, func() {
|
||||
mujica.Select(c, &s.weekStart, calOptions(calWeekStarts), mujica.SelectOptions{Label: "Week start"})
|
||||
mujica.Select(c, &s.maxStr, calOptions([]string{"1", "2", "3", "4"}), mujica.SelectOptions{Label: "Rows per day"})
|
||||
mujica.Checkbox(c, &s.disabled, "Disabled", mujica.CheckboxOptions{})
|
||||
input.Select(c, &s.weekStart, calOptions(calWeekStarts), input.SelectOptions{Label: "Week start"})
|
||||
input.Select(c, &s.maxStr, calOptions([]string{"1", "2", "3", "4"}), input.SelectOptions{Label: "Rows per day"})
|
||||
input.Checkbox(c, &s.disabled, "Disabled", input.CheckboxOptions{})
|
||||
})
|
||||
s.maxRows = calAtoi(s.maxStr)
|
||||
r := mujica.CalendarMonthView(c, &s.month, s.events, mujica.CalendarMonthViewOptions{Location: time.Local, Version: s.version,
|
||||
r := datetime.CalendarMonthView(c, &s.month, s.events, datetime.CalendarMonthViewOptions{Location: time.Local, Version: s.version,
|
||||
WeekStart: calWeekStart(s.weekStart), MaxPerDay: s.maxRows, Disabled: s.disabled})
|
||||
calViewResult(r, &s.events, &s.version, log)
|
||||
})
|
||||
@@ -107,24 +109,24 @@ func calMonthDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func calWeekDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
week mujica.Date
|
||||
events []mujica.CalendarEvent
|
||||
week core.Date
|
||||
events []datetime.CalendarEvent
|
||||
version int
|
||||
slot string
|
||||
hour12 bool
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "422", func() state {
|
||||
return state{week: mujica.DateOf(time.Now()), events: calSample(), slot: "30"}
|
||||
return state{week: core.DateOf(time.Now()), events: calSample(), slot: "30"}
|
||||
})
|
||||
dtSection(c, "CalendarWeekView", func() {
|
||||
dtControls(c, func() {
|
||||
mujica.Select(c, &s.slot, calOptions([]string{"15", "30", "60"}), mujica.SelectOptions{Label: "Slot minutes"})
|
||||
mujica.Checkbox(c, &s.hour12, "12-hour", mujica.CheckboxOptions{})
|
||||
mujica.Checkbox(c, &s.disabled, "Disabled", mujica.CheckboxOptions{})
|
||||
input.Select(c, &s.slot, calOptions([]string{"15", "30", "60"}), input.SelectOptions{Label: "Slot minutes"})
|
||||
input.Checkbox(c, &s.hour12, "12-hour", input.CheckboxOptions{})
|
||||
input.Checkbox(c, &s.disabled, "Disabled", input.CheckboxOptions{})
|
||||
})
|
||||
slot := calAtoi(s.slot)
|
||||
r := mujica.CalendarWeekView(c, &s.week, s.events, mujica.CalendarWeekViewOptions{Location: time.Local, Version: s.version,
|
||||
r := datetime.CalendarWeekView(c, &s.week, s.events, datetime.CalendarWeekViewOptions{Location: time.Local, Version: s.version,
|
||||
SlotMinutes: slot, Hour12: s.hour12, Disabled: s.disabled})
|
||||
calViewResult(r, &s.events, &s.version, log)
|
||||
})
|
||||
@@ -132,32 +134,32 @@ func calWeekDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func calDayDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
day mujica.Date
|
||||
events []mujica.CalendarEvent
|
||||
day core.Date
|
||||
events []datetime.CalendarEvent
|
||||
version int
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "423", func() state { return state{day: mujica.DateOf(time.Now()), events: calSample()} })
|
||||
s := ui.Local(c.Root(), "423", func() state { return state{day: core.DateOf(time.Now()), events: calSample()} })
|
||||
dtSection(c, "CalendarDayView", func() {
|
||||
dtControls(c, func() { mujica.Checkbox(c, &s.disabled, "Disabled", mujica.CheckboxOptions{}) })
|
||||
r := mujica.CalendarDayView(c, &s.day, s.events, mujica.CalendarDayViewOptions{Location: time.Local, Version: s.version, Disabled: s.disabled})
|
||||
dtControls(c, func() { input.Checkbox(c, &s.disabled, "Disabled", input.CheckboxOptions{}) })
|
||||
r := datetime.CalendarDayView(c, &s.day, s.events, datetime.CalendarDayViewOptions{Location: time.Local, Version: s.version, Disabled: s.disabled})
|
||||
calViewResult(r, &s.events, &s.version, log)
|
||||
})
|
||||
}
|
||||
|
||||
func calAgendaDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
events []mujica.CalendarEvent
|
||||
events []datetime.CalendarEvent
|
||||
days string
|
||||
}
|
||||
s := ui.Local(c.Root(), "424", func() state { return state{events: calSample(), days: "7"} })
|
||||
dtSection(c, "AgendaView", func() {
|
||||
dtControls(c, func() {
|
||||
mujica.Select(c, &s.days, calOptions([]string{"1", "7", "30"}), mujica.SelectOptions{Label: "Days"})
|
||||
input.Select(c, &s.days, calOptions([]string{"1", "7", "30"}), input.SelectOptions{Label: "Days"})
|
||||
})
|
||||
days := calAtoi(s.days)
|
||||
ui.Column(c).Height(420).Children(func() {
|
||||
if id, ok := mujica.AgendaView(c, s.events, mujica.AgendaViewOptions{Location: time.Local, Days: days}).Opened(); ok {
|
||||
if id, ok := datetime.AgendaView(c, s.events, datetime.AgendaViewOptions{Location: time.Local, Days: days}).Opened(); ok {
|
||||
log("opened " + id)
|
||||
}
|
||||
})
|
||||
@@ -167,11 +169,11 @@ func calAgendaDemo(c *ui.Context, log func(string)) {
|
||||
func calYearDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
year int
|
||||
events []mujica.CalendarEvent
|
||||
events []datetime.CalendarEvent
|
||||
}
|
||||
s := ui.Local(c.Root(), "425", func() state { return state{year: time.Now().Year(), events: calSample()} })
|
||||
dtSection(c, "YearView", func() {
|
||||
if d, ok := mujica.YearView(c, &s.year, s.events, mujica.YearViewOptions{Location: time.Local}).DayChosen(); ok {
|
||||
if d, ok := datetime.YearView(c, &s.year, s.events, datetime.YearViewOptions{Location: time.Local}).DayChosen(); ok {
|
||||
log("day " + d.String())
|
||||
}
|
||||
})
|
||||
@@ -185,10 +187,10 @@ func calAtoi(s string) int {
|
||||
return n
|
||||
}
|
||||
|
||||
func calOptions(values []string) []mujica.SelectOption[string] {
|
||||
out := make([]mujica.SelectOption[string], len(values))
|
||||
func calOptions(values []string) []input.SelectOption[string] {
|
||||
out := make([]input.SelectOption[string], len(values))
|
||||
for i, v := range values {
|
||||
out[i] = mujica.SelectOption[string]{Value: v, Label: v}
|
||||
out[i] = input.SelectOption[string]{Value: v, Label: v}
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -5,8 +5,10 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/theme"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chart"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||
)
|
||||
|
||||
const c1Category = "Charts I"
|
||||
@@ -39,14 +41,14 @@ var charts1Pages = []page{
|
||||
var c1Statuses = []string{"Ready", "Loading", "Failed", "Empty"}
|
||||
|
||||
// c1Frame shows the status picker and returns the frame.
|
||||
func c1Frame(c *ui.Context, label string, status *string) (mujica.ChartFrame, bool) {
|
||||
func c1Frame(c *ui.Context, label string, status *string) (chart.ChartFrame, bool) {
|
||||
ui.Select(c, status, c1Statuses).Width(160).AlignSelf(ui.Start)
|
||||
f := mujica.ChartFrame{Label: label}
|
||||
f := chart.ChartFrame{Label: label}
|
||||
switch *status {
|
||||
case "Loading":
|
||||
f.Status = mujica.DataLoading
|
||||
f.Status = data.DataLoading
|
||||
case "Failed":
|
||||
f.Status, f.Error = mujica.DataFailed, "The archive did not answer"
|
||||
f.Status, f.Error = data.DataFailed, "The archive did not answer"
|
||||
}
|
||||
return f, *status != "Empty"
|
||||
}
|
||||
@@ -54,14 +56,14 @@ func c1Frame(c *ui.Context, label string, status *string) (mujica.ChartFrame, bo
|
||||
// c1Section lays out a title, controls, then the chart.
|
||||
func c1Section(c *ui.Context, title string, controls, body func()) {
|
||||
ui.Column(c).Gap(12).Children(func() {
|
||||
ui.Text(c, title).Font(theme.SerifFont).FontSize(mujica.FontSize(c, theme.H3Size))
|
||||
ui.Text(c, title).Font(theme.SerifFont).FontSize(core.FontSize(c, theme.H3Size))
|
||||
ui.Row(c).Gap(16).Wrap().AlignItems(ui.Center).Children(controls)
|
||||
ui.Column(c).Width(560).Children(body)
|
||||
})
|
||||
}
|
||||
|
||||
// c1Log records a chart's events in the gallery log.
|
||||
func c1Log(log func(string), name string, r mujica.ChartResult) {
|
||||
func c1Log(log func(string), name string, r chart.ChartResult) {
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("%s: point %d", name, r.Point))
|
||||
}
|
||||
|
||||
@@ -2,173 +2,173 @@ package main
|
||||
|
||||
// Snippets are the bodies of the Examples in example_charts1_test.go.
|
||||
|
||||
const c1LineCode = `series := []mujica.ChartSeries{
|
||||
const c1LineCode = `series := []chart.ChartSeries{
|
||||
{Name: "Pews", Values: []float64{3, 5, math.NaN(), 4, 8}},
|
||||
{Name: "Candles", Values: []float64{5, 2, 6, 7, 1}},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.LineChart(c, series, mujica.LineChartOptions{Frame: mujica.ChartFrame{Label: "Chapel"}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.LineChart(c, series, chart.LineChartOptions{Frame: chart.ChartFrame{Label: "Chapel"}})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1AreaCode = `series := []mujica.ChartSeries{
|
||||
const c1AreaCode = `series := []chart.ChartSeries{
|
||||
{Name: "Matins", Values: []float64{3, 4, 6, 5}},
|
||||
{Name: "Vespers", Values: []float64{2, 3, 2, 4}},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.AreaChart(c, series, mujica.AreaChartOptions{Frame: mujica.ChartFrame{Label: "Attendance"}, Stacked: true})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.AreaChart(c, series, chart.AreaChartOptions{Frame: chart.ChartFrame{Label: "Attendance"}, Stacked: true})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1BarCode = `categories := []string{"Nave", "Choir", "Crypt"}
|
||||
series := []mujica.ChartSeries{{Name: "Gold", Values: []float64{4, -2, 3}}, {Name: "Silver", Values: []float64{2, 5, 1}}}
|
||||
series := []chart.ChartSeries{{Name: "Gold", Values: []float64{4, -2, 3}}, {Name: "Silver", Values: []float64{2, 5, 1}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.BarChart(c, categories, series, mujica.BarChartOptions{Frame: mujica.ChartFrame{Label: "Tithes"}, Labels: true})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.BarChart(c, categories, series, chart.BarChartOptions{Frame: chart.ChartFrame{Label: "Tithes"}, Labels: true})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1PieCode = `slices := []mujica.ChartSlice{{Label: "Wax", Value: 50}, {Label: "Oil", Value: 30}, {Label: "Salt", Value: 4}, {Label: "Ash", Value: 3}}
|
||||
const c1PieCode = `slices := []chart.ChartSlice{{Label: "Wax", Value: 50}, {Label: "Oil", Value: 30}, {Label: "Salt", Value: 4}, {Label: "Ash", Value: 3}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.PieChart(c, slices, mujica.PieChartOptions{Frame: mujica.ChartFrame{Label: "Stores"}, Donut: true, MinAngle: 20})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.PieChart(c, slices, chart.PieChartOptions{Frame: chart.ChartFrame{Label: "Stores"}, Donut: true, MinAngle: 20})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1ScatterCode = `var selected []mujica.ScatterKey
|
||||
groups := []mujica.ScatterGroup{{Name: "Monks", Points: []mujica.ScatterPoint{{X: 1, Y: 2, Size: 4}, {X: 3, Y: 5, Size: 9}}}}
|
||||
const c1ScatterCode = `var selected []chart.ScatterKey
|
||||
groups := []chart.ScatterGroup{{Name: "Monks", Points: []chart.ScatterPoint{{X: 1, Y: 2, Size: 4}, {X: 3, Y: 5, Size: 9}}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ScatterChart(c, &selected, groups, mujica.ScatterChartOptions{Frame: mujica.ChartFrame{Label: "Choir"}, Bubbles: true})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.ScatterChart(c, &selected, groups, chart.ScatterChartOptions{Frame: chart.ChartFrame{Label: "Choir"}, Bubbles: true})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1RadarCode = `axes := []string{"Voice", "Latin", "Chant", "Organ"}
|
||||
series := []mujica.ChartSeries{{Name: "Agnès", Values: []float64{8, 6, 9, 4}}}
|
||||
series := []chart.ChartSeries{{Name: "Agnès", Values: []float64{8, 6, 9, 4}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.RadarChart(c, axes, series, mujica.RadarChartOptions{Frame: mujica.ChartFrame{Label: "Skills"}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.RadarChart(c, axes, series, chart.RadarChartOptions{Frame: chart.ChartFrame{Label: "Skills"}})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1HeatCode = `rows, cols := []string{"Mon", "Tue"}, []string{"Matins", "Lauds", "Vespers"}
|
||||
values := [][]float64{{1, 4, 9}, {3, math.NaN(), 7}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.HeatmapChart(c, rows, cols, values, mujica.HeatmapChartOptions{Frame: mujica.ChartFrame{Label: "Hours"}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.HeatmapChart(c, rows, cols, values, chart.HeatmapChartOptions{Frame: chart.ChartFrame{Label: "Hours"}})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1CalendarCode = `year := 2026
|
||||
values := map[mujica.Date]float64{{Year: 2026, Month: time.March, Day: 4}: 5}
|
||||
values := map[core.Date]float64{{Year: 2026, Month: time.March, Day: 4}: 5}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.CalendarHeatmap(c, &year, values, mujica.CalendarHeatmapOptions{Frame: mujica.ChartFrame{Label: "Prayers"}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.CalendarHeatmap(c, &year, values, chart.CalendarHeatmapOptions{Frame: chart.ChartFrame{Label: "Prayers"}})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1HistogramCode = `values := []float64{1, 2, 2, 3, 4, 5, 9, 10}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Histogram(c, values, mujica.HistogramOptions{Frame: mujica.ChartFrame{Label: "Ages"}, BinWidth: 5})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.Histogram(c, values, chart.HistogramOptions{Frame: chart.ChartFrame{Label: "Ages"}, BinWidth: 5})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1BoxCode = `groups := []mujica.BoxGroup{{Name: "Choir", Values: []float64{1, 2, 3, 4, 5, 40}}, {Name: "Nave", Values: []float64{4, 5, 6, 7}}}
|
||||
const c1BoxCode = `groups := []chart.BoxGroup{{Name: "Choir", Values: []float64{1, 2, 3, 4, 5, 40}}, {Name: "Nave", Values: []float64{4, 5, 6, 7}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.BoxPlot(c, groups, mujica.BoxPlotOptions{Frame: mujica.ChartFrame{Label: "Hymns"}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.BoxPlot(c, groups, chart.BoxPlotOptions{Frame: chart.ChartFrame{Label: "Hymns"}})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1WaterfallCode = `steps := []mujica.WaterfallStep{
|
||||
const c1WaterfallCode = `steps := []chart.WaterfallStep{
|
||||
{Label: "Tithes", Value: 12},
|
||||
{Label: "Repairs", Value: -5},
|
||||
{Label: "Year", Kind: mujica.WaterfallTotal},
|
||||
{Label: "Year", Kind: chart.WaterfallTotal},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.WaterfallChart(c, steps, mujica.WaterfallChartOptions{Frame: mujica.ChartFrame{Label: "Ledger"}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.WaterfallChart(c, steps, chart.WaterfallChartOptions{Frame: chart.ChartFrame{Label: "Ledger"}})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1FunnelCode = `stages := []mujica.ChartSlice{{Label: "Visitors", Value: 1000}, {Label: "Novices", Value: 400}, {Label: "Vows", Value: 100}}
|
||||
const c1FunnelCode = `stages := []chart.ChartSlice{{Label: "Visitors", Value: 1000}, {Label: "Novices", Value: 400}, {Label: "Vows", Value: 100}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.FunnelChart(c, stages, mujica.FunnelChartOptions{Frame: mujica.ChartFrame{Label: "Order"}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.FunnelChart(c, stages, chart.FunnelChartOptions{Frame: chart.ChartFrame{Label: "Order"}})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1BulletCode = `target := 250.0
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.BulletChart(c, 270, mujica.BulletChartOptions{Frame: mujica.ChartFrame{Label: "Alms"}, Target: &target, Bands: []float64{150, 225, 300}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.BulletChart(c, 270, chart.BulletChartOptions{Frame: chart.ChartFrame{Label: "Alms"}, Target: &target, Bands: []float64{150, 225, 300}})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1SparkCode = `values := []float64{3, 5, 4, 7, 6, 9}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Sparkline(c, values, mujica.SparklineOptions{Frame: mujica.ChartFrame{Label: "Bells"}, Kind: mujica.SparklineArea})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.Sparkline(c, values, chart.SparklineOptions{Frame: chart.ChartFrame{Label: "Bells"}, Kind: chart.SparklineArea})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1GaugeCode = `segments := []mujica.GaugeSegment{{To: 60, Label: "Calm", Tone: mujica.GaugeGood}, {To: 100, Label: "Crowded", Tone: mujica.GaugeDanger}}
|
||||
const c1GaugeCode = `segments := []chart.GaugeSegment{{To: 60, Label: "Calm", Tone: chart.GaugeGood}, {To: 100, Label: "Crowded", Tone: chart.GaugeDanger}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Gauge(c, 72, mujica.GaugeOptions{Frame: mujica.ChartFrame{Label: "Nave"}, Max: 100, Unit: "%", Segments: segments})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.Gauge(c, 72, chart.GaugeOptions{Frame: chart.ChartFrame{Label: "Nave"}, Max: 100, Unit: "%", Segments: segments})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1TreemapCode = `var path []string
|
||||
root := mujica.ChartNode{Name: "Abbey", Children: []mujica.ChartNode{
|
||||
{Name: "Library", Children: []mujica.ChartNode{{Name: "Psalters", Value: 30}, {Name: "Maps", Value: 10}}},
|
||||
root := chart.ChartNode{Name: "Abbey", Children: []chart.ChartNode{
|
||||
{Name: "Library", Children: []chart.ChartNode{{Name: "Psalters", Value: 30}, {Name: "Maps", Value: 10}}},
|
||||
{Name: "Kitchen", Value: 25},
|
||||
}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Treemap(c, &path, root, mujica.TreemapOptions{Frame: mujica.ChartFrame{Label: "Rooms"}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.Treemap(c, &path, root, chart.TreemapOptions{Frame: chart.ChartFrame{Label: "Rooms"}})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1SunburstCode = `var path []string
|
||||
root := mujica.ChartNode{Name: "Abbey", Children: []mujica.ChartNode{
|
||||
{Name: "Library", Children: []mujica.ChartNode{{Name: "Psalters", Value: 30}, {Name: "Maps", Value: 10}}},
|
||||
root := chart.ChartNode{Name: "Abbey", Children: []chart.ChartNode{
|
||||
{Name: "Library", Children: []chart.ChartNode{{Name: "Psalters", Value: 30}, {Name: "Maps", Value: 10}}},
|
||||
{Name: "Kitchen", Value: 25},
|
||||
}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Sunburst(c, &path, root, mujica.SunburstOptions{Frame: mujica.ChartFrame{Label: "Rooms"}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.Sunburst(c, &path, root, chart.SunburstOptions{Frame: chart.ChartFrame{Label: "Rooms"}})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1SankeyCode = `nodes := []mujica.SankeyNode{{ID: "tithe", Label: "Tithes"}, {ID: "fund", Label: "Fund"}, {ID: "roof", Label: "Roof"}}
|
||||
links := []mujica.SankeyLink{{From: "tithe", To: "fund", Value: 60}, {From: "fund", To: "roof", Value: 50}}
|
||||
const c1SankeyCode = `nodes := []chart.SankeyNode{{ID: "tithe", Label: "Tithes"}, {ID: "fund", Label: "Fund"}, {ID: "roof", Label: "Roof"}}
|
||||
links := []chart.SankeyLink{{From: "tithe", To: "fund", Value: 60}, {From: "fund", To: "roof", Value: 50}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.SankeyChart(c, nodes, links, mujica.SankeyChartOptions{Frame: mujica.ChartFrame{Label: "Money"}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.SankeyChart(c, nodes, links, chart.SankeyChartOptions{Frame: chart.ChartFrame{Label: "Money"}})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1NetworkCode = `var state mujica.NetworkGraphState
|
||||
nodes := []mujica.GraphNode{{ID: "a", Label: "Abbot"}, {ID: "b", Label: "Prior"}, {ID: "c", Label: "Novice"}}
|
||||
edges := []mujica.GraphEdge{{From: "a", To: "b"}, {From: "b", To: "c"}}
|
||||
const c1NetworkCode = `var state chart.NetworkGraphState
|
||||
nodes := []chart.GraphNode{{ID: "a", Label: "Abbot"}, {ID: "b", Label: "Prior"}, {ID: "c", Label: "Novice"}}
|
||||
edges := []chart.GraphEdge{{From: "a", To: "b"}, {From: "b", To: "c"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.NetworkGraph(c, &state, nodes, edges, mujica.NetworkGraphOptions{Frame: mujica.ChartFrame{Label: "House"}, Force: true})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.NetworkGraph(c, &state, nodes, edges, chart.NetworkGraphOptions{Frame: chart.ChartFrame{Label: "House"}, Force: true})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c1RealtimeCode = `buf := mujica.NewRealtimeBuffer(600, "Bell")
|
||||
const c1RealtimeCode = `buf := chart.NewRealtimeBuffer(600, "Bell")
|
||||
start := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
for i := range 60 {
|
||||
buf.Append(start.Add(time.Duration(i)*time.Second), float64(i%7))
|
||||
}
|
||||
var state mujica.RealtimeChartState
|
||||
var state chart.RealtimeChartState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.RealtimeChart(c, &state, buf, mujica.RealtimeChartOptions{Frame: mujica.ChartFrame{Label: "Sound"}, Window: 30 * time.Second})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.RealtimeChart(c, &state, buf, chart.RealtimeChartOptions{Frame: chart.ChartFrame{Label: "Sound"}, Window: 30 * time.Second})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
@@ -8,14 +8,15 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chart"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
var c1Abbey = mujica.ChartNode{Name: "Abbey", Children: []mujica.ChartNode{
|
||||
{Name: "Library", Children: []mujica.ChartNode{{Name: "Psalters", Value: 30}, {Name: "Maps", Value: 10}, {Name: "Letters", Value: 8}}},
|
||||
var c1Abbey = chart.ChartNode{Name: "Abbey", Children: []chart.ChartNode{
|
||||
{Name: "Library", Children: []chart.ChartNode{{Name: "Psalters", Value: 30}, {Name: "Maps", Value: 10}, {Name: "Letters", Value: 8}}},
|
||||
{Name: "Kitchen", Value: 25},
|
||||
{Name: "Garden", Children: []mujica.ChartNode{{Name: "Herbs", Value: 12}, {Name: "Vines", Value: 8}}},
|
||||
{Name: "Chapel", Children: []mujica.ChartNode{{Name: "Nave", Value: 18}, {Name: "Crypt", Value: 6}}},
|
||||
{Name: "Garden", Children: []chart.ChartNode{{Name: "Herbs", Value: 12}, {Name: "Vines", Value: 8}}},
|
||||
{Name: "Chapel", Children: []chart.ChartNode{{Name: "Nave", Value: 18}, {Name: "Crypt", Value: 6}}},
|
||||
}}
|
||||
|
||||
func c1TreemapDemo(c *ui.Context, log func(string)) {
|
||||
@@ -28,10 +29,10 @@ func c1TreemapDemo(c *ui.Context, log func(string)) {
|
||||
frame, show := c1Frame(c, "Rooms", &s.status)
|
||||
root := c1Abbey
|
||||
if !show {
|
||||
root = mujica.ChartNode{Name: "Abbey"}
|
||||
root = chart.ChartNode{Name: "Abbey"}
|
||||
s.path = nil
|
||||
}
|
||||
r := mujica.Treemap(c, &s.path, root, mujica.TreemapOptions{Frame: frame})
|
||||
r := chart.Treemap(c, &s.path, root, chart.TreemapOptions{Frame: frame})
|
||||
c1Log(log, "Treemap", r)
|
||||
if r.ValueChanged() {
|
||||
log("Treemap: path /" + strings.Join(s.path, "/"))
|
||||
@@ -54,10 +55,10 @@ func c1SunburstDemo(c *ui.Context, log func(string)) {
|
||||
frame.Height = 300
|
||||
root := c1Abbey
|
||||
if !show {
|
||||
root = mujica.ChartNode{Name: "Abbey"}
|
||||
root = chart.ChartNode{Name: "Abbey"}
|
||||
s.path = nil
|
||||
}
|
||||
r := mujica.Sunburst(c, &s.path, root, mujica.SunburstOptions{Frame: frame, Depth: int(s.depth)})
|
||||
r := chart.Sunburst(c, &s.path, root, chart.SunburstOptions{Frame: frame, Depth: int(s.depth)})
|
||||
c1Log(log, "Sunburst", r)
|
||||
if r.ValueChanged() {
|
||||
log("Sunburst: path /" + strings.Join(s.path, "/"))
|
||||
@@ -70,12 +71,12 @@ func c1SankeyDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "118", func() state { return state{status: "Ready"} })
|
||||
c1Section(c, "SankeyChart", func() {}, func() {
|
||||
frame, show := c1Frame(c, "Money", &s.status)
|
||||
nodes := []mujica.SankeyNode{{ID: "tithe", Label: "Tithes"}, {ID: "alms", Label: "Alms"}, {ID: "fund", Label: "Fund"}, {ID: "roof", Label: "Roof"}, {ID: "bread", Label: "Bread"}, {ID: "bells", Label: "Bells"}}
|
||||
links := []mujica.SankeyLink{{From: "tithe", To: "fund", Value: 60}, {From: "alms", To: "fund", Value: 30}, {From: "alms", To: "bread", Value: 10}, {From: "fund", To: "roof", Value: 50}, {From: "fund", To: "bells", Value: 40}}
|
||||
nodes := []chart.SankeyNode{{ID: "tithe", Label: "Tithes"}, {ID: "alms", Label: "Alms"}, {ID: "fund", Label: "Fund"}, {ID: "roof", Label: "Roof"}, {ID: "bread", Label: "Bread"}, {ID: "bells", Label: "Bells"}}
|
||||
links := []chart.SankeyLink{{From: "tithe", To: "fund", Value: 60}, {From: "alms", To: "fund", Value: 30}, {From: "alms", To: "bread", Value: 10}, {From: "fund", To: "roof", Value: 50}, {From: "fund", To: "bells", Value: 40}}
|
||||
if !show {
|
||||
links = nil
|
||||
}
|
||||
c1Log(log, "SankeyChart", mujica.SankeyChart(c, nodes, links, mujica.SankeyChartOptions{Frame: frame}))
|
||||
c1Log(log, "SankeyChart", chart.SankeyChart(c, nodes, links, chart.SankeyChartOptions{Frame: frame}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -83,23 +84,23 @@ func c1NetworkDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
status string
|
||||
force bool
|
||||
graph mujica.NetworkGraphState
|
||||
graph chart.NetworkGraphState
|
||||
}
|
||||
s := ui.Local(c.Root(), "119", func() state { return state{status: "Ready", force: true} })
|
||||
c1Section(c, "NetworkGraph", func() {
|
||||
ui.Checkbox(c, &s.force, "Force layout")
|
||||
if mujica.Button(c, "Reset view", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Reset view", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.graph.ResetView()
|
||||
log("NetworkGraph: reset view")
|
||||
}
|
||||
}, func() {
|
||||
frame, show := c1Frame(c, "House", &s.status)
|
||||
nodes := []mujica.GraphNode{{ID: "a", Label: "Abbot", X: 0, Y: 0}, {ID: "b", Label: "Prior", X: 10, Y: 0}, {ID: "c", Label: "Cellarer", X: 10, Y: 10}, {ID: "d", Label: "Novice", X: 0, Y: 10}, {ID: "e", Label: "Cantor", X: 5, Y: 5}}
|
||||
edges := []mujica.GraphEdge{{From: "a", To: "b"}, {From: "a", To: "c"}, {From: "c", To: "d"}, {From: "b", To: "e"}, {From: "e", To: "d"}}
|
||||
nodes := []chart.GraphNode{{ID: "a", Label: "Abbot", X: 0, Y: 0}, {ID: "b", Label: "Prior", X: 10, Y: 0}, {ID: "c", Label: "Cellarer", X: 10, Y: 10}, {ID: "d", Label: "Novice", X: 0, Y: 10}, {ID: "e", Label: "Cantor", X: 5, Y: 5}}
|
||||
edges := []chart.GraphEdge{{From: "a", To: "b"}, {From: "a", To: "c"}, {From: "c", To: "d"}, {From: "b", To: "e"}, {From: "e", To: "d"}}
|
||||
if !show {
|
||||
nodes, edges = nil, nil
|
||||
}
|
||||
c1Log(log, "NetworkGraph", mujica.NetworkGraph(c, &s.graph, nodes, edges, mujica.NetworkGraphOptions{Frame: frame, Force: s.force}))
|
||||
c1Log(log, "NetworkGraph", chart.NetworkGraph(c, &s.graph, nodes, edges, chart.NetworkGraphOptions{Frame: frame, Force: s.force}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -107,13 +108,13 @@ func c1RealtimeDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
status string
|
||||
feed bool
|
||||
buf *mujica.RealtimeBuffer
|
||||
buf *chart.RealtimeBuffer
|
||||
next time.Time
|
||||
tick int
|
||||
view mujica.RealtimeChartState
|
||||
view chart.RealtimeChartState
|
||||
}
|
||||
s := ui.Local(c.Root(), "120", func() state {
|
||||
return state{status: "Ready", feed: true, buf: mujica.NewRealtimeBuffer(600, "Bell", "Organ")}
|
||||
return state{status: "Ready", feed: true, buf: chart.NewRealtimeBuffer(600, "Bell", "Organ")}
|
||||
})
|
||||
// The demo appends a sample every 500 ms while feeding.
|
||||
if s.feed && !c.Now().Before(s.next) {
|
||||
@@ -128,7 +129,7 @@ func c1RealtimeDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
c1Section(c, "RealtimeChart", func() { ui.Checkbox(c, &s.feed, "Feed data") }, func() {
|
||||
frame, _ := c1Frame(c, "Sound", &s.status)
|
||||
r := mujica.RealtimeChart(c, &s.view, s.buf, mujica.RealtimeChartOptions{Frame: frame, Window: 20 * time.Second})
|
||||
r := chart.RealtimeChart(c, &s.view, s.buf, chart.RealtimeChartOptions{Frame: frame, Window: 20 * time.Second})
|
||||
c1Log(log, "RealtimeChart", r)
|
||||
if r.ValueChanged() {
|
||||
log(fmt.Sprintf("RealtimeChart: paused=%v", s.view.Paused))
|
||||
|
||||
@@ -7,15 +7,15 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chart"
|
||||
)
|
||||
|
||||
func c1Series(gap bool) []mujica.ChartSeries {
|
||||
func c1Series(gap bool) []chart.ChartSeries {
|
||||
a := []float64{3, 5, 4, 6, 8, 7, 9, 11}
|
||||
if gap {
|
||||
a = []float64{3, 5, math.NaN(), 6, 8, 7, 9, 11}
|
||||
}
|
||||
return []mujica.ChartSeries{
|
||||
return []chart.ChartSeries{
|
||||
{Name: "Matins", Values: a},
|
||||
{Name: "Vespers", Values: []float64{2, 3, 5, 4, 6, 9, 8, 7}},
|
||||
{Name: "Compline", Values: []float64{1, 2, 2, 3, 2, 4, 5, 4}},
|
||||
@@ -35,9 +35,9 @@ func c1LineDemo(c *ui.Context, log func(string)) {
|
||||
ui.Checkbox(c, &s.big, "100,000 points")
|
||||
}, func() {
|
||||
frame, show := c1Frame(c, "Attendance", &s.status)
|
||||
series, opts := c1Series(s.gap), mujica.LineChartOptions{Frame: frame}
|
||||
series, opts := c1Series(s.gap), chart.LineChartOptions{Frame: frame}
|
||||
if s.big {
|
||||
series = []mujica.ChartSeries{{Name: "Wave", Values: c1Wave()}}
|
||||
series = []chart.ChartSeries{{Name: "Wave", Values: c1Wave()}}
|
||||
}
|
||||
if s.time {
|
||||
t0 := time.Date(2026, 10, 5, 6, 0, 0, 0, time.UTC)
|
||||
@@ -50,7 +50,7 @@ func c1LineDemo(c *ui.Context, log func(string)) {
|
||||
series = nil
|
||||
opts.Times = nil
|
||||
}
|
||||
c1Log(log, "LineChart", mujica.LineChart(c, series, opts))
|
||||
c1Log(log, "LineChart", chart.LineChart(c, series, opts))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func c1AreaDemo(c *ui.Context, log func(string)) {
|
||||
if !show {
|
||||
series = nil
|
||||
}
|
||||
c1Log(log, "AreaChart", mujica.AreaChart(c, series, mujica.AreaChartOptions{Frame: frame, Stacked: s.stacked, Baseline: s.base}))
|
||||
c1Log(log, "AreaChart", chart.AreaChart(c, series, chart.AreaChartOptions{Frame: frame, Stacked: s.stacked, Baseline: s.base}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -101,11 +101,11 @@ func c1BarDemo(c *ui.Context, log func(string)) {
|
||||
}, func() {
|
||||
frame, show := c1Frame(c, "Tithes", &s.status)
|
||||
cats := []string{"Nave", "Choir", "Crypt", "Bells"}
|
||||
series := []mujica.ChartSeries{{Name: "Gold", Values: []float64{4, -2, 3, 5}}, {Name: "Silver", Values: []float64{2, 5, 1, -1}}}
|
||||
series := []chart.ChartSeries{{Name: "Gold", Values: []float64{4, -2, 3, 5}}, {Name: "Silver", Values: []float64{2, 5, 1, -1}}}
|
||||
if !show {
|
||||
series = nil
|
||||
}
|
||||
c1Log(log, "BarChart", mujica.BarChart(c, cats, series, mujica.BarChartOptions{Frame: frame, Horizontal: s.horizontal, Stacked: s.stacked, Labels: s.labels}))
|
||||
c1Log(log, "BarChart", chart.BarChart(c, cats, series, chart.BarChartOptions{Frame: frame, Horizontal: s.horizontal, Stacked: s.stacked, Labels: s.labels}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -122,11 +122,11 @@ func c1PieDemo(c *ui.Context, log func(string)) {
|
||||
ui.StepSlider(c, &s.min, 0, 40, 5).Width(140)
|
||||
}, func() {
|
||||
frame, show := c1Frame(c, "Stores", &s.status)
|
||||
slices := []mujica.ChartSlice{{Label: "Wax", Value: 50}, {Label: "Oil", Value: 30}, {Label: "Myrrh", Value: 12}, {Label: "Salt", Value: 4}, {Label: "Ash", Value: 4}}
|
||||
slices := []chart.ChartSlice{{Label: "Wax", Value: 50}, {Label: "Oil", Value: 30}, {Label: "Myrrh", Value: 12}, {Label: "Salt", Value: 4}, {Label: "Ash", Value: 4}}
|
||||
if !show {
|
||||
slices = nil
|
||||
}
|
||||
c1Log(log, "PieChart", mujica.PieChart(c, slices, mujica.PieChartOptions{Frame: frame, Donut: s.donut, MinAngle: s.min}))
|
||||
c1Log(log, "PieChart", chart.PieChart(c, slices, chart.PieChartOptions{Frame: frame, Donut: s.donut, MinAngle: s.min}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ func c1ScatterDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
status string
|
||||
bubbles bool
|
||||
selected []mujica.ScatterKey
|
||||
selected []chart.ScatterKey
|
||||
}
|
||||
s := ui.Local(c.Root(), "105", func() state { return state{status: "Ready", bubbles: true} })
|
||||
c1Section(c, "ScatterChart", func() {
|
||||
@@ -142,14 +142,14 @@ func c1ScatterDemo(c *ui.Context, log func(string)) {
|
||||
ui.Text(c, fmt.Sprintf("%d selected", len(s.selected)))
|
||||
}, func() {
|
||||
frame, show := c1Frame(c, "Choir", &s.status)
|
||||
groups := []mujica.ScatterGroup{
|
||||
{Name: "Monks", Points: []mujica.ScatterPoint{{X: 1, Y: 2, Size: 4}, {X: 3, Y: 5, Size: 9}, {X: 6, Y: 4, Size: 1}, {X: 7, Y: 8, Size: 6}}},
|
||||
{Name: "Nuns", Points: []mujica.ScatterPoint{{X: 2, Y: 7, Size: 16}, {X: 8, Y: 9, Size: 2}, {X: 5, Y: 2, Size: 8}}},
|
||||
groups := []chart.ScatterGroup{
|
||||
{Name: "Monks", Points: []chart.ScatterPoint{{X: 1, Y: 2, Size: 4}, {X: 3, Y: 5, Size: 9}, {X: 6, Y: 4, Size: 1}, {X: 7, Y: 8, Size: 6}}},
|
||||
{Name: "Nuns", Points: []chart.ScatterPoint{{X: 2, Y: 7, Size: 16}, {X: 8, Y: 9, Size: 2}, {X: 5, Y: 2, Size: 8}}},
|
||||
}
|
||||
if !show {
|
||||
groups = nil
|
||||
}
|
||||
r := mujica.ScatterChart(c, &s.selected, groups, mujica.ScatterChartOptions{Frame: frame, Bubbles: s.bubbles})
|
||||
r := chart.ScatterChart(c, &s.selected, groups, chart.ScatterChartOptions{Frame: frame, Bubbles: s.bubbles})
|
||||
c1Log(log, "ScatterChart", r)
|
||||
if r.ValueChanged() {
|
||||
log(fmt.Sprintf("ScatterChart: %d selected", len(s.selected)))
|
||||
@@ -162,11 +162,11 @@ func c1RadarDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "106", func() state { return state{status: "Ready"} })
|
||||
c1Section(c, "RadarChart", func() {}, func() {
|
||||
frame, show := c1Frame(c, "Skills", &s.status)
|
||||
series := []mujica.ChartSeries{{Name: "Agnès", Values: []float64{8, 6, 9, 4, 7}}, {Name: "Clare", Values: []float64{5, 9, 6, 8, 3}}}
|
||||
series := []chart.ChartSeries{{Name: "Agnès", Values: []float64{8, 6, 9, 4, 7}}, {Name: "Clare", Values: []float64{5, 9, 6, 8, 3}}}
|
||||
if !show {
|
||||
series = nil
|
||||
}
|
||||
c1Log(log, "RadarChart", mujica.RadarChart(c, []string{"Voice", "Latin", "Chant", "Organ", "Bells"}, series, mujica.RadarChartOptions{Frame: frame}))
|
||||
c1Log(log, "RadarChart", chart.RadarChart(c, []string{"Voice", "Latin", "Chant", "Organ", "Bells"}, series, chart.RadarChartOptions{Frame: frame}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -192,6 +192,6 @@ func c1HeatDemo(c *ui.Context, log func(string)) {
|
||||
if !show {
|
||||
rows, values = nil, nil
|
||||
}
|
||||
c1Log(log, "HeatmapChart", mujica.HeatmapChart(c, rows, cols, values, mujica.HeatmapChartOptions{Frame: frame}))
|
||||
c1Log(log, "HeatmapChart", chart.HeatmapChart(c, rows, cols, values, chart.HeatmapChartOptions{Frame: frame}))
|
||||
})
|
||||
}
|
||||
@@ -8,7 +8,9 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chart"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/datetime"
|
||||
)
|
||||
|
||||
func c1CalendarDemo(c *ui.Context, log func(string)) {
|
||||
@@ -20,17 +22,17 @@ func c1CalendarDemo(c *ui.Context, log func(string)) {
|
||||
c1Section(c, "CalendarHeatmap", func() { ui.Select(c, &s.start, []string{"Locale", "Sunday", "Monday", "Saturday"}) }, func() {
|
||||
frame, show := c1Frame(c, "Prayers", &s.status)
|
||||
frame.Height = 160
|
||||
values := map[mujica.Date]float64{}
|
||||
values := map[core.Date]float64{}
|
||||
if show {
|
||||
d := mujica.Date{Year: s.year, Month: time.January, Day: 1}
|
||||
d := core.Date{Year: s.year, Month: time.January, Day: 1}
|
||||
for i := range 366 {
|
||||
if (i*37)%5 != 0 {
|
||||
values[d.AddDays(i)] = float64((i * 13) % 9)
|
||||
}
|
||||
}
|
||||
}
|
||||
start := map[string]mujica.WeekStart{"Locale": mujica.WeekStartLocale, "Sunday": mujica.WeekStartSunday, "Monday": mujica.WeekStartMonday, "Saturday": mujica.WeekStartSaturday}[s.start]
|
||||
r := mujica.CalendarHeatmap(c, &s.year, values, mujica.CalendarHeatmapOptions{Frame: frame, WeekStart: start})
|
||||
start := map[string]datetime.WeekStart{"Locale": datetime.WeekStartLocale, "Sunday": datetime.WeekStartSunday, "Monday": datetime.WeekStartMonday, "Saturday": datetime.WeekStartSaturday}[s.start]
|
||||
r := chart.CalendarHeatmap(c, &s.year, values, chart.CalendarHeatmapOptions{Frame: frame, WeekStart: start})
|
||||
c1Log(log, "CalendarHeatmap", r)
|
||||
if r.ValueChanged() {
|
||||
log("CalendarHeatmap: year " + strconv.Itoa(s.year))
|
||||
@@ -62,7 +64,7 @@ func c1HistogramDemo(c *ui.Context, log func(string)) {
|
||||
if !show {
|
||||
values = nil
|
||||
}
|
||||
c1Log(log, "Histogram", mujica.Histogram(c, values, mujica.HistogramOptions{Frame: frame, Bins: int(s.bins), Cumulative: s.cumulative}))
|
||||
c1Log(log, "Histogram", chart.Histogram(c, values, chart.HistogramOptions{Frame: frame, Bins: int(s.bins), Cumulative: s.cumulative}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -71,7 +73,7 @@ func c1BoxDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "110", func() state { return state{status: "Ready"} })
|
||||
c1Section(c, "BoxPlot", func() {}, func() {
|
||||
frame, show := c1Frame(c, "Hymns", &s.status)
|
||||
groups := []mujica.BoxGroup{
|
||||
groups := []chart.BoxGroup{
|
||||
{Name: "Choir", Values: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 24}},
|
||||
{Name: "Nave", Values: []float64{4, 5, 5, 6, 7, 8}},
|
||||
{Name: "Crypt", Values: []float64{12, 14, 15, 18, 22, 25}},
|
||||
@@ -79,7 +81,7 @@ func c1BoxDemo(c *ui.Context, log func(string)) {
|
||||
if !show {
|
||||
groups = nil
|
||||
}
|
||||
c1Log(log, "BoxPlot", mujica.BoxPlot(c, groups, mujica.BoxPlotOptions{Frame: frame}))
|
||||
c1Log(log, "BoxPlot", chart.BoxPlot(c, groups, chart.BoxPlotOptions{Frame: frame}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -88,14 +90,14 @@ func c1WaterfallDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "111", func() state { return state{status: "Ready"} })
|
||||
c1Section(c, "WaterfallChart", func() {}, func() {
|
||||
frame, show := c1Frame(c, "Ledger", &s.status)
|
||||
steps := []mujica.WaterfallStep{
|
||||
{Label: "Tithes", Value: 12}, {Label: "Repairs", Value: -5}, {Label: "Q1", Kind: mujica.WaterfallSubtotal},
|
||||
{Label: "Alms", Value: 4}, {Label: "Bread", Value: -2}, {Label: "Year", Kind: mujica.WaterfallTotal},
|
||||
steps := []chart.WaterfallStep{
|
||||
{Label: "Tithes", Value: 12}, {Label: "Repairs", Value: -5}, {Label: "Q1", Kind: chart.WaterfallSubtotal},
|
||||
{Label: "Alms", Value: 4}, {Label: "Bread", Value: -2}, {Label: "Year", Kind: chart.WaterfallTotal},
|
||||
}
|
||||
if !show {
|
||||
steps = nil
|
||||
}
|
||||
c1Log(log, "WaterfallChart", mujica.WaterfallChart(c, steps, mujica.WaterfallChartOptions{Frame: frame}))
|
||||
c1Log(log, "WaterfallChart", chart.WaterfallChart(c, steps, chart.WaterfallChartOptions{Frame: frame}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -104,11 +106,11 @@ func c1FunnelDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "112", func() state { return state{status: "Ready"} })
|
||||
c1Section(c, "FunnelChart", func() {}, func() {
|
||||
frame, show := c1Frame(c, "Order", &s.status)
|
||||
stages := []mujica.ChartSlice{{Label: "Visitors", Value: 1000}, {Label: "Novices", Value: 400}, {Label: "Vows", Value: 100}, {Label: "Priors", Value: 12}}
|
||||
stages := []chart.ChartSlice{{Label: "Visitors", Value: 1000}, {Label: "Novices", Value: 400}, {Label: "Vows", Value: 100}, {Label: "Priors", Value: 12}}
|
||||
if !show {
|
||||
stages = nil
|
||||
}
|
||||
c1Log(log, "FunnelChart", mujica.FunnelChart(c, stages, mujica.FunnelChartOptions{Frame: frame}))
|
||||
c1Log(log, "FunnelChart", chart.FunnelChart(c, stages, chart.FunnelChartOptions{Frame: frame}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -129,7 +131,7 @@ func c1BulletDemo(c *ui.Context, log func(string)) {
|
||||
frame.Height = 200
|
||||
}
|
||||
target := s.target
|
||||
c1Log(log, "BulletChart", mujica.BulletChart(c, s.value, mujica.BulletChartOptions{Frame: frame, Target: &target, Bands: []float64{150, 225, 300}, Vertical: s.vertical}))
|
||||
c1Log(log, "BulletChart", chart.BulletChart(c, s.value, chart.BulletChartOptions{Frame: frame, Target: &target, Bands: []float64{150, 225, 300}, Vertical: s.vertical}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -143,10 +145,10 @@ func c1SparkDemo(c *ui.Context, log func(string)) {
|
||||
values = nil
|
||||
}
|
||||
ui.Row(c).Gap(24).Wrap().Children(func() {
|
||||
for _, k := range []mujica.SparklineKind{mujica.SparklineLine, mujica.SparklineBar, mujica.SparklineArea} {
|
||||
for _, k := range []chart.SparklineKind{chart.SparklineLine, chart.SparklineBar, chart.SparklineArea} {
|
||||
f := frame
|
||||
f.Label = fmt.Sprintf("Bells %d", k+1)
|
||||
c1Log(log, f.Label, mujica.Sparkline(c, values, mujica.SparklineOptions{Frame: f, Kind: k}))
|
||||
c1Log(log, f.Label, chart.Sparkline(c, values, chart.SparklineOptions{Frame: f, Kind: k}))
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -165,7 +167,7 @@ func c1GaugeDemo(c *ui.Context, log func(string)) {
|
||||
ui.Checkbox(c, &s.full, "Full circle")
|
||||
}, func() {
|
||||
frame, _ := c1Frame(c, "Nave", &s.status)
|
||||
segments := []mujica.GaugeSegment{{To: 60, Label: "Calm", Tone: mujica.GaugeGood}, {To: 85, Label: "Busy", Tone: mujica.GaugeWarning}, {To: 100, Label: "Crowded", Tone: mujica.GaugeDanger}}
|
||||
c1Log(log, "Gauge", mujica.Gauge(c, s.value, mujica.GaugeOptions{Frame: frame, Max: 100, Unit: "%", Segments: segments, Full: s.full}))
|
||||
segments := []chart.GaugeSegment{{To: 60, Label: "Calm", Tone: chart.GaugeGood}, {To: 85, Label: "Busy", Tone: chart.GaugeWarning}, {To: 100, Label: "Crowded", Tone: chart.GaugeDanger}}
|
||||
c1Log(log, "Gauge", chart.Gauge(c, s.value, chart.GaugeOptions{Frame: frame, Max: 100, Unit: "%", Segments: segments, Full: s.full}))
|
||||
})
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
)
|
||||
|
||||
// c1Demo runs page id's demo with an event log.
|
||||
@@ -19,7 +19,7 @@ func c1Demo(t *testing.T, id string) (*ui.Tester, *[]string) {
|
||||
}
|
||||
events := &[]string{}
|
||||
tt := ui.NewTester(func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
core.Use(c, core.Settings{})
|
||||
ui.Scroll(c).Fill().Padding(24).Children(func() {
|
||||
charts1Pages[i].Demo(c, func(s string) { *events = append(*events, s) })
|
||||
})
|
||||
|
||||
@@ -5,7 +5,9 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chart"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
const c2Category = "Charts II"
|
||||
@@ -29,7 +31,7 @@ var charts2Pages = []page{
|
||||
}
|
||||
|
||||
func c2Check(c *ui.Context, value *bool, label string, log func(string)) {
|
||||
if mujica.Checkbox(c, value, label, mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, value, label, input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprintf("%s = %v", label, *value))
|
||||
}
|
||||
}
|
||||
@@ -37,7 +39,7 @@ func c2Check(c *ui.Context, value *bool, label string, log func(string)) {
|
||||
func c2Number(c *ui.Context, value *float64, label string, lo, hi, step float64, log func(string)) {
|
||||
ui.Row(c).Gap(8).AlignItems(ui.Center).Children(func() {
|
||||
ui.Text(c, label)
|
||||
r := mujica.NumberInput(c, value, mujica.NumberInputOptions{Label: label, Min: lo, Max: hi, Step: step})
|
||||
r := input.NumberInput(c, value, input.NumberInputOptions{Label: label, Min: lo, Max: hi, Step: step})
|
||||
r.Element.Width(150)
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("%s = %g", label, *value))
|
||||
@@ -46,25 +48,25 @@ func c2Number(c *ui.Context, value *float64, label string, lo, hi, step float64,
|
||||
}
|
||||
|
||||
func c2Select(c *ui.Context, value *string, label string, choices []string, log func(string)) {
|
||||
items := make([]mujica.SelectOption[string], len(choices))
|
||||
items := make([]input.SelectOption[string], len(choices))
|
||||
for i, s := range choices {
|
||||
items[i] = mujica.SelectOption[string]{Value: s, Label: s}
|
||||
items[i] = input.SelectOption[string]{Value: s, Label: s}
|
||||
}
|
||||
r := mujica.Select(c, value, items, mujica.SelectOptions{Label: label})
|
||||
r := input.Select(c, value, items, input.SelectOptions{Label: label})
|
||||
r.Element.Width(160)
|
||||
if r.Changed() {
|
||||
log(label + " = " + *value)
|
||||
}
|
||||
}
|
||||
|
||||
func c2Frame(c *ui.Context, label string, status *string, log func(string)) (mujica.ChartFrame, bool) {
|
||||
func c2Frame(c *ui.Context, label string, status *string, log func(string)) (chart.ChartFrame, bool) {
|
||||
c2Select(c, status, "Status", c1Statuses, log)
|
||||
f := mujica.ChartFrame{Label: label}
|
||||
f := chart.ChartFrame{Label: label}
|
||||
if *status == "Loading" {
|
||||
f.Status = mujica.DataLoading
|
||||
f.Status = data.DataLoading
|
||||
}
|
||||
if *status == "Failed" {
|
||||
f.Status, f.Error = mujica.DataFailed, "The archive did not answer"
|
||||
f.Status, f.Error = data.DataFailed, "The archive did not answer"
|
||||
}
|
||||
return f, *status != "Empty"
|
||||
}
|
||||
@@ -2,25 +2,25 @@ package main
|
||||
|
||||
const c2ChartLegendCode = `hidden := map[string]bool{}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
k := mujica.Tokens(c)
|
||||
items := []mujica.ChartLegendItem{{Name: "Pews", Color: k.Info}, {Name: "Candles", Color: k.Success, Shape: 1}}
|
||||
mujica.ChartLegend(c, &hidden, items, mujica.ChartLegendOptions{MaxItems: 1})
|
||||
core.Use(c, core.Settings{})
|
||||
k := core.Tokens(c)
|
||||
items := []chart.ChartLegendItem{{Name: "Pews", Color: k.Info}, {Name: "Candles", Color: k.Success, Shape: 1}}
|
||||
chart.ChartLegend(c, &hidden, items, chart.ChartLegendOptions{MaxItems: 1})
|
||||
}
|
||||
ui.Render(view, 400, 200, 1)`
|
||||
|
||||
const c2ChartTooltipCode = `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
k := mujica.Tokens(c)
|
||||
mujica.ChartTooltip(c, 40, 40, []mujica.ChartTooltipLine{{Text: "Lauds"}, {Text: "Pews 5", Color: k.Info}, {Text: "Candles 2", Color: k.Success, Shape: 1}})
|
||||
core.Use(c, core.Settings{})
|
||||
k := core.Tokens(c)
|
||||
chart.ChartTooltip(c, 40, 40, []chart.ChartTooltipLine{{Text: "Lauds"}, {Text: "Pews 5", Color: k.Info}, {Text: "Candles 2", Color: k.Success, Shape: 1}})
|
||||
}
|
||||
ui.Render(view, 400, 200, 1)`
|
||||
|
||||
const c2ChartCrosshairCode = `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
x := mujica.ChartAxis(c, mujica.ChartAxisOptions{Max: 10})
|
||||
y := mujica.ChartAxis(c, mujica.ChartAxisOptions{Side: mujica.SideLeft, Max: 100})
|
||||
hair := mujica.ChartCrosshair(c, mujica.ChartCrosshairOptions{X: &x, Y: &y})
|
||||
core.Use(c, core.Settings{})
|
||||
x := chart.ChartAxis(c, chart.ChartAxisOptions{Max: 10})
|
||||
y := chart.ChartAxis(c, chart.ChartAxisOptions{Side: chart.SideLeft, Max: 100})
|
||||
hair := chart.ChartCrosshair(c, chart.ChartCrosshairOptions{X: &x, Y: &y})
|
||||
ui.Box(c).Fill().Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
plot := ui.Rect{X: r.X + 60, Y: r.Y + 20, W: r.W - 80, H: r.H - 60}
|
||||
x.Draw(p, plot)
|
||||
@@ -31,8 +31,8 @@ const c2ChartCrosshairCode = `view := func(c *ui.Context) {
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c2ChartAxisCode = `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
axis := mujica.ChartAxis(c, mujica.ChartAxisOptions{Side: mujica.SideLeft, Kind: mujica.AxisLog, Min: 1, Max: 1000})
|
||||
core.Use(c, core.Settings{})
|
||||
axis := chart.ChartAxis(c, chart.ChartAxisOptions{Side: chart.SideLeft, Kind: chart.AxisLog, Min: 1, Max: 1000})
|
||||
ui.Box(c).Fill().Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
axis.Draw(p, ui.Rect{X: r.X + 60, Y: r.Y + 20, W: r.W - 80, H: r.H - 60})
|
||||
})
|
||||
@@ -40,10 +40,10 @@ const c2ChartAxisCode = `view := func(c *ui.Context) {
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c2ChartGridCode = `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
x := mujica.ChartAxis(c, mujica.ChartAxisOptions{Max: 10})
|
||||
y := mujica.ChartAxis(c, mujica.ChartAxisOptions{Side: mujica.SideLeft, Max: 100})
|
||||
grid := mujica.ChartGrid(c, mujica.ChartGridOptions{X: &x, Y: &y, Minor: 2})
|
||||
core.Use(c, core.Settings{})
|
||||
x := chart.ChartAxis(c, chart.ChartAxisOptions{Max: 10})
|
||||
y := chart.ChartAxis(c, chart.ChartAxisOptions{Side: chart.SideLeft, Max: 100})
|
||||
grid := chart.ChartGrid(c, chart.ChartGridOptions{X: &x, Y: &y, Minor: 2})
|
||||
ui.Box(c).Fill().Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
plot := ui.Rect{X: r.X + 60, Y: r.Y + 20, W: r.W - 80, H: r.H - 60}
|
||||
grid.Draw(p, plot)
|
||||
@@ -55,17 +55,17 @@ ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c2ChartBrushCode = `value := [2]float64{20, 60}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ChartBrush(c, &value, mujica.ChartBrushOptions{Label: "Months", Max: 100, Step: 1, Values: []float64{2, 4, 3, 8, 5}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.ChartBrush(c, &value, chart.ChartBrushOptions{Label: "Months", Max: 100, Step: 1, Values: []float64{2, 4, 3, 8, 5}})
|
||||
}
|
||||
ui.Render(view, 400, 160, 1)`
|
||||
|
||||
const c2ChartAnnotationCode = `notes := []mujica.ChartNote{{Kind: mujica.NoteHLine, Value: 50, Text: "Goal", Draggable: true}, {Kind: mujica.NoteBand, Value: 6, End: 8, Text: "Lent"}}
|
||||
const c2ChartAnnotationCode = `notes := []chart.ChartNote{{Kind: chart.NoteHLine, Value: 50, Text: "Goal", Draggable: true}, {Kind: chart.NoteBand, Value: 6, End: 8, Text: "Lent"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
x := mujica.ChartAxis(c, mujica.ChartAxisOptions{Max: 10})
|
||||
y := mujica.ChartAxis(c, mujica.ChartAxisOptions{Side: mujica.SideLeft, Max: 100})
|
||||
annotation := mujica.ChartAnnotation(c, notes, mujica.ChartAnnotationOptions{X: &x, Y: &y})
|
||||
core.Use(c, core.Settings{})
|
||||
x := chart.ChartAxis(c, chart.ChartAxisOptions{Max: 10})
|
||||
y := chart.ChartAxis(c, chart.ChartAxisOptions{Side: chart.SideLeft, Max: 100})
|
||||
annotation := chart.ChartAnnotation(c, notes, chart.ChartAnnotationOptions{X: &x, Y: &y})
|
||||
box := ui.Box(c).Fill().Focusable().Label("Annotations")
|
||||
b := box.Bounds()
|
||||
annotation.Drag(box, ui.Rect{X: 60, Y: 20, W: b.W - 80, H: b.H - 60})
|
||||
@@ -79,10 +79,10 @@ view := func(c *ui.Context) {
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c2ChartExportCode = `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
data, err := mujica.ChartExport(c, func(c *ui.Context) {
|
||||
mujica.SparkBar(c, []float64{3, -2, 5}, mujica.SparkBarOptions{Frame: mujica.ChartFrame{Label: "Alms"}})
|
||||
}, mujica.ChartExportOptions{Width: 300, Height: 100, Scale: 2})
|
||||
core.Use(c, core.Settings{})
|
||||
data, err := chart.ChartExport(c, func(c *ui.Context) {
|
||||
chart.SparkBar(c, []float64{3, -2, 5}, chart.SparkBarOptions{Frame: chart.ChartFrame{Label: "Alms"}})
|
||||
}, chart.ChartExportOptions{Width: 300, Height: 100, Scale: 2})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -96,27 +96,27 @@ ui.Render(view, 400, 160, 1)`
|
||||
|
||||
const c2SparkBarCode = `values := []float64{3, -2, 4, 1, -3}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.SparkBar(c, values, mujica.SparkBarOptions{Frame: mujica.ChartFrame{Label: "Alms"}, BarWidth: 10})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.SparkBar(c, values, chart.SparkBarOptions{Frame: chart.ChartFrame{Label: "Alms"}, BarWidth: 10})
|
||||
}
|
||||
ui.Render(view, 400, 100, 1)`
|
||||
|
||||
const c2TrendIndicatorCode = `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.TrendIndicator(c, 12.5, mujica.TrendIndicatorOptions{Precision: 1, Unit: "%", Note: "vs Lent"})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.TrendIndicator(c, 12.5, chart.TrendIndicatorOptions{Precision: 1, Unit: "%", Note: "vs Lent"})
|
||||
}
|
||||
ui.Render(view, 400, 100, 1)`
|
||||
|
||||
const c2ChartEmptyStateCode = `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ChartEmptyState(c, mujica.DataFailed, mujica.ChartEmptyStateOptions{Height: 180, Error: "The archive did not answer"})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.ChartEmptyState(c, data.DataFailed, chart.ChartEmptyStateOptions{Height: 180, Error: "The archive did not answer"})
|
||||
}
|
||||
ui.Render(view, 400, 240, 1)`
|
||||
|
||||
const c2ViolinPlotCode = `groups := []mujica.BoxGroup{{Name: "Choir", Values: []float64{1, 2, 3, 4, 7, 9}}, {Name: "Nave", Values: []float64{2, 4, 5, 6, 8}}}
|
||||
const c2ViolinPlotCode = `groups := []chart.BoxGroup{{Name: "Choir", Values: []float64{1, 2, 3, 4, 7, 9}}, {Name: "Nave", Values: []float64{2, 4, 5, 6, 8}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ViolinPlot(c, groups, mujica.ViolinPlotOptions{Frame: mujica.ChartFrame{Label: "Hymns"}, Bandwidth: 1})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.ViolinPlot(c, groups, chart.ViolinPlotOptions{Frame: chart.ChartFrame{Label: "Hymns"}, Bandwidth: 1})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
@@ -124,22 +124,22 @@ const c2ParallelCoordinatesCode = `filters := map[string][2]float64{}
|
||||
axes := []string{"Age", "Hymns", "Vigils"}
|
||||
rows := [][]float64{{20, 3, 9}, {35, 8, 2}, {50, 5, 5}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ParallelCoordinates(c, &filters, axes, rows, mujica.ParallelCoordinatesOptions{Frame: mujica.ChartFrame{Label: "Monks"}, Names: []string{"Agnes", "Clare", "Hild"}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.ParallelCoordinates(c, &filters, axes, rows, chart.ParallelCoordinatesOptions{Frame: chart.ChartFrame{Label: "Monks"}, Names: []string{"Agnes", "Clare", "Hild"}})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c2ChordDiagramCode = `names := []string{"Nave", "Choir", "Crypt"}
|
||||
matrix := [][]float64{{0, 5, 2}, {4, 0, 1}, {1, 2, 0}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ChordDiagram(c, names, matrix, mujica.ChordDiagramOptions{Frame: mujica.ChartFrame{Label: "Paths"}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.ChordDiagram(c, names, matrix, chart.ChordDiagramOptions{Frame: chart.ChartFrame{Label: "Paths"}})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const c2DensityPlotCode = `groups := []mujica.BoxGroup{{Name: "Matins", Values: []float64{1, 2, 2, 3, 7}}, {Name: "Vespers", Values: []float64{3, 4, 5, 6, 6}}}
|
||||
const c2DensityPlotCode = `groups := []chart.BoxGroup{{Name: "Matins", Values: []float64{1, 2, 2, 3, 7}}, {Name: "Vespers", Values: []float64{3, 4, 5, 6, 6}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.DensityPlot(c, groups, mujica.DensityPlotOptions{Frame: mujica.ChartFrame{Label: "Ages"}, Bandwidth: 1, Points: 128})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.DensityPlot(c, groups, chart.DensityPlotOptions{Frame: chart.ChartFrame{Label: "Ages"}, Bandwidth: 1, Points: 128})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
@@ -5,7 +5,8 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chart"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func c2ViolinDemo(c *ui.Context, log func(string)) { c2DistributionDemo(c, log, "129", "ViolinPlot") }
|
||||
@@ -16,10 +17,10 @@ func c2DistributionDemo(c *ui.Context, log func(string), id, name string) {
|
||||
status string
|
||||
bandwidth float64
|
||||
disabled bool
|
||||
groups []mujica.BoxGroup
|
||||
groups []chart.BoxGroup
|
||||
}
|
||||
s := ui.Local(c.Root(), id, func() state {
|
||||
return state{status: "Ready", groups: []mujica.BoxGroup{
|
||||
return state{status: "Ready", groups: []chart.BoxGroup{
|
||||
{Name: "Matins", Values: []float64{1, 2, 2, 3, 4, 5, 8, 9}},
|
||||
{Name: "Vespers", Values: []float64{3, 4, 5, 6, 6, 7, 8, 11}},
|
||||
}}
|
||||
@@ -35,9 +36,9 @@ func c2DistributionDemo(c *ui.Context, log func(string), id, name string) {
|
||||
}
|
||||
ui.Column(c).Disabled(s.disabled).Children(func() {
|
||||
if id == "129" {
|
||||
c1Log(log, name, mujica.ViolinPlot(c, groups, mujica.ViolinPlotOptions{Frame: f, Bandwidth: s.bandwidth}))
|
||||
c1Log(log, name, chart.ViolinPlot(c, groups, chart.ViolinPlotOptions{Frame: f, Bandwidth: s.bandwidth}))
|
||||
} else {
|
||||
c1Log(log, name, mujica.DensityPlot(c, groups, mujica.DensityPlotOptions{Frame: f, Bandwidth: s.bandwidth}))
|
||||
c1Log(log, name, chart.DensityPlot(c, groups, chart.DensityPlotOptions{Frame: f, Bandwidth: s.bandwidth}))
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -55,7 +56,7 @@ func c2ParallelDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
c1Section(c, "ParallelCoordinates", func() {
|
||||
c2Check(c, &s.disabled, "Disabled", log)
|
||||
if mujica.Button(c, "Clear filters", mujica.ButtonOptions{Disabled: s.disabled}).Clicked() {
|
||||
if input.Button(c, "Clear filters", input.ButtonOptions{Disabled: s.disabled}).Clicked() {
|
||||
s.filters = map[string][2]float64{}
|
||||
log("ParallelCoordinates: clear filters")
|
||||
}
|
||||
@@ -68,7 +69,7 @@ func c2ParallelDemo(c *ui.Context, log func(string)) {
|
||||
names = nil
|
||||
}
|
||||
ui.Column(c).Disabled(s.disabled).Children(func() {
|
||||
c1Log(log, "ParallelCoordinates", mujica.ParallelCoordinates(c, &s.filters, []string{"Age", "Hymns", "Vigils"}, rows, mujica.ParallelCoordinatesOptions{Frame: f, Names: names}))
|
||||
c1Log(log, "ParallelCoordinates", chart.ParallelCoordinates(c, &s.filters, []string{"Age", "Hymns", "Vigils"}, rows, chart.ParallelCoordinatesOptions{Frame: f, Names: names}))
|
||||
})
|
||||
ui.Text(c, fmt.Sprintf("Filters: %v", s.filters))
|
||||
})
|
||||
@@ -99,7 +100,7 @@ func c2ChordDemo(c *ui.Context, log func(string)) {
|
||||
matrix = nil
|
||||
}
|
||||
ui.Column(c).Disabled(s.disabled).Children(func() {
|
||||
c1Log(log, "ChordDiagram", mujica.ChordDiagram(c, names, matrix, mujica.ChordDiagramOptions{Frame: f}))
|
||||
c1Log(log, "ChordDiagram", chart.ChordDiagram(c, names, matrix, chart.ChordDiagramOptions{Frame: f}))
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -6,15 +6,16 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/internal"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chart"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||
)
|
||||
|
||||
type c2PartsState struct {
|
||||
status, kind string
|
||||
disabled, crosshair, draggable, fixed bool
|
||||
minor float64
|
||||
notes []mujica.ChartNote
|
||||
notes []chart.ChartNote
|
||||
}
|
||||
|
||||
func c2TooltipDemo(c *ui.Context, log func(string)) { c2PartsDemo(c, log, "122", "ChartTooltip") }
|
||||
@@ -25,11 +26,11 @@ func c2AnnotationDemo(c *ui.Context, log func(string)) { c2PartsDemo(c, log, "12
|
||||
|
||||
func c2PartsDemo(c *ui.Context, log func(string), id, name string) {
|
||||
s := ui.Local(c.Root(), id, func() c2PartsState {
|
||||
return c2PartsState{status: "Ready", kind: "Linear", crosshair: true, minor: 2, draggable: true, notes: []mujica.ChartNote{
|
||||
{Kind: mujica.NoteHLine, Value: 50, Text: "Goal"},
|
||||
{Kind: mujica.NoteVLine, Value: 3, Text: "Advent"},
|
||||
{Kind: mujica.NoteBand, Value: 6, End: 8, Text: "Lent"},
|
||||
{Kind: mujica.NoteText, Value: 1, End: 90, Text: "Feast"},
|
||||
return c2PartsState{status: "Ready", kind: "Linear", crosshair: true, minor: 2, draggable: true, notes: []chart.ChartNote{
|
||||
{Kind: chart.NoteHLine, Value: 50, Text: "Goal"},
|
||||
{Kind: chart.NoteVLine, Value: 3, Text: "Advent"},
|
||||
{Kind: chart.NoteBand, Value: 6, End: 8, Text: "Lent"},
|
||||
{Kind: chart.NoteText, Value: 1, End: 90, Text: "Feast"},
|
||||
}}
|
||||
})
|
||||
c1Section(c, name, func() {
|
||||
@@ -56,23 +57,23 @@ func c2PartsDemo(c *ui.Context, log func(string), id, name string) {
|
||||
})
|
||||
}
|
||||
|
||||
func c2PartsChart(c *ui.Context, frame mujica.ChartFrame, show bool, s *c2PartsState, id string, log func(string)) mujica.ChartResult {
|
||||
xo := mujica.ChartAxisOptions{Max: 10}
|
||||
yo := mujica.ChartAxisOptions{Side: mujica.SideLeft, Max: 100}
|
||||
func c2PartsChart(c *ui.Context, frame chart.ChartFrame, show bool, s *c2PartsState, id string, log func(string)) chart.ChartResult {
|
||||
xo := chart.ChartAxisOptions{Max: 10}
|
||||
yo := chart.ChartAxisOptions{Side: chart.SideLeft, Max: 100}
|
||||
pts := [][2]float64{{2, 30}, {5, 80}, {8, 55}}
|
||||
if id == "124" {
|
||||
switch s.kind {
|
||||
case "Log":
|
||||
yo.Kind, yo.Min = mujica.AxisLog, 1
|
||||
yo.Kind, yo.Min = chart.AxisLog, 1
|
||||
case "Time":
|
||||
xo.Kind = mujica.AxisTime
|
||||
xo.Kind = chart.AxisTime
|
||||
xo.From = time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
xo.To = xo.From.Add(10 * time.Hour)
|
||||
for i := range pts {
|
||||
pts[i][0] = float64(xo.From.Unix()) + pts[i][0]*3600
|
||||
}
|
||||
case "Category":
|
||||
xo.Kind, xo.Categories = mujica.AxisCategory, []string{"Nave", "Choir", "Crypt"}
|
||||
xo.Kind, xo.Categories = chart.AxisCategory, []string{"Nave", "Choir", "Crypt"}
|
||||
for i := range pts {
|
||||
pts[i][0] = float64(i)
|
||||
}
|
||||
@@ -81,17 +82,17 @@ func c2PartsChart(c *ui.Context, frame mujica.ChartFrame, show bool, s *c2PartsS
|
||||
yo.Ticks = []float64{10, 50, 90}
|
||||
}
|
||||
}
|
||||
x, y := mujica.ChartAxis(c, xo), mujica.ChartAxis(c, yo)
|
||||
grid := mujica.ChartGrid(c, mujica.ChartGridOptions{Y: &y, Minor: int(s.minor)})
|
||||
hair := mujica.ChartCrosshair(c, mujica.ChartCrosshairOptions{X: &x, Y: &y})
|
||||
var notes []mujica.ChartNote
|
||||
x, y := chart.ChartAxis(c, xo), chart.ChartAxis(c, yo)
|
||||
grid := chart.ChartGrid(c, chart.ChartGridOptions{Y: &y, Minor: int(s.minor)})
|
||||
hair := chart.ChartCrosshair(c, chart.ChartCrosshairOptions{X: &x, Y: &y})
|
||||
var notes []chart.ChartNote
|
||||
if id == "127" {
|
||||
notes = s.notes
|
||||
for i := range notes {
|
||||
notes[i].Draggable = s.draggable
|
||||
}
|
||||
}
|
||||
annotation := mujica.ChartAnnotation(c, notes, mujica.ChartAnnotationOptions{X: &x, Y: &y})
|
||||
annotation := chart.ChartAnnotation(c, notes, chart.ChartAnnotationOptions{X: &x, Y: &y})
|
||||
plotOf := func(r ui.Rect) ui.Rect {
|
||||
return ui.Rect{X: r.X + 64, Y: r.Y + 16, W: max(1, r.W-100), H: max(1, r.H-56)}
|
||||
}
|
||||
@@ -103,16 +104,16 @@ func c2PartsChart(c *ui.Context, frame mujica.ChartFrame, show bool, s *c2PartsS
|
||||
if !show {
|
||||
marks = 0
|
||||
}
|
||||
colors := internal.Palette(mujica.Tokens(c))
|
||||
return mujica.Chart(c, frame, mujica.ChartSpec{
|
||||
colors := internal.Palette(core.Tokens(c))
|
||||
return chart.Chart(c, frame, chart.ChartSpec{
|
||||
Marks: marks,
|
||||
Pick: func(r ui.Rect, px, py float32) int {
|
||||
return hair.Snap(len(pts), func(i int) (float32, float32) { return at(r, i) }, px, py)
|
||||
},
|
||||
Anchor: at,
|
||||
Readout: func(i int) string { return fmt.Sprintf("x %s · y %s", x.Label(pts[i][0]), y.Label(pts[i][1])) },
|
||||
Tooltip: func(i int) []mujica.ChartTooltipLine {
|
||||
return []mujica.ChartTooltipLine{{Text: x.Label(pts[i][0])}, {Text: "Pews " + y.Label(pts[i][1]), Color: colors[0]}, {Text: "Candles " + y.Label(pts[i][1]/2), Color: colors[1], Shape: 1}}
|
||||
Tooltip: func(i int) []chart.ChartTooltipLine {
|
||||
return []chart.ChartTooltipLine{{Text: x.Label(pts[i][0])}, {Text: "Pews " + y.Label(pts[i][1]), Color: colors[0]}, {Text: "Candles " + y.Label(pts[i][1]/2), Color: colors[1], Shape: 1}}
|
||||
},
|
||||
Input: func(el *ui.Element, r ui.Rect) {
|
||||
if annotation.Drag(el, plotOf(r)) {
|
||||
|
||||
@@ -5,8 +5,10 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/internal"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chart"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||
)
|
||||
|
||||
func c2LegendDemo(c *ui.Context, log func(string)) {
|
||||
@@ -21,14 +23,14 @@ func c2LegendDemo(c *ui.Context, log func(string)) {
|
||||
c2Check(c, &s.disabled, "Disabled", log)
|
||||
c2Number(c, &s.max, "Max items", 0, 6, 1, log)
|
||||
}, func() {
|
||||
pal := internal.Palette(mujica.Tokens(c))
|
||||
pal := internal.Palette(core.Tokens(c))
|
||||
names := []string{"Matins", "Lauds", "Prime", "Terce", "Sext", "None"}
|
||||
items := make([]mujica.ChartLegendItem, len(names))
|
||||
items := make([]chart.ChartLegendItem, len(names))
|
||||
for i, n := range names {
|
||||
items[i] = mujica.ChartLegendItem{Name: n, Color: pal[i], Shape: i % 5}
|
||||
items[i] = chart.ChartLegendItem{Name: n, Color: pal[i], Shape: i % 5}
|
||||
}
|
||||
ui.Column(c).Disabled(s.disabled).Children(func() {
|
||||
r := mujica.ChartLegend(c, &s.hidden, items, mujica.ChartLegendOptions{Vertical: s.vertical, MaxItems: int(s.max)})
|
||||
r := chart.ChartLegend(c, &s.hidden, items, chart.ChartLegendOptions{Vertical: s.vertical, MaxItems: int(s.max)})
|
||||
if expanded, ok := r.Expanded(); ok {
|
||||
log(fmt.Sprintf("ChartLegend: expanded %v", expanded))
|
||||
}
|
||||
@@ -52,12 +54,12 @@ func c2BrushDemo(c *ui.Context, log func(string)) {
|
||||
c2Number(c, &s.step, "Step", 1, 10, 1, log)
|
||||
}, func() {
|
||||
vals := []float64{1, 4, 2, 7, 3, 8, 4, 9}
|
||||
r := mujica.ChartBrush(c, &s.value, mujica.ChartBrushOptions{Label: "Months", Max: 100, Step: s.step, Values: vals, Disabled: s.disabled})
|
||||
r := chart.ChartBrush(c, &s.value, chart.ChartBrushOptions{Label: "Months", Max: 100, Step: s.step, Values: vals, Disabled: s.disabled})
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("ChartBrush: range %g–%g", s.value[0], s.value[1]))
|
||||
}
|
||||
x := mujica.ChartAxis(c, mujica.ChartAxisOptions{Min: s.value[0], Max: s.value[1]})
|
||||
y := mujica.ChartAxis(c, mujica.ChartAxisOptions{Side: mujica.SideLeft, Max: 10})
|
||||
x := chart.ChartAxis(c, chart.ChartAxisOptions{Min: s.value[0], Max: s.value[1]})
|
||||
y := chart.ChartAxis(c, chart.ChartAxisOptions{Side: chart.SideLeft, Max: 10})
|
||||
xs := []float64{0, 15, 30, 45, 60, 75, 90, 100}
|
||||
var visible []int
|
||||
for i, v := range xs {
|
||||
@@ -70,7 +72,7 @@ func c2BrushDemo(c *ui.Context, log func(string)) {
|
||||
plot := plotOf(r)
|
||||
return x.Map(plot, xs[visible[i]]), y.Map(plot, vals[visible[i]])
|
||||
}
|
||||
c1Log(log, "Zoom preview", mujica.Chart(c, mujica.ChartFrame{Label: "Zoom preview", Height: 140}, mujica.ChartSpec{
|
||||
c1Log(log, "Zoom preview", chart.Chart(c, chart.ChartFrame{Label: "Zoom preview", Height: 140}, chart.ChartSpec{
|
||||
Marks: len(visible), Anchor: at,
|
||||
Pick: func(r ui.Rect, px, py float32) int {
|
||||
return internal.Nearest(len(visible), func(i int) (float32, float32) { return at(r, i) }, px, py, 24, false)
|
||||
@@ -82,7 +84,7 @@ func c2BrushDemo(c *ui.Context, log func(string)) {
|
||||
y.Draw(p, plot)
|
||||
for i := range visible {
|
||||
px, py := at(r, i)
|
||||
internal.Marker(p, 0, px, py, 8, mujica.Tokens(c).AccentText)
|
||||
internal.Marker(p, 0, px, py, 8, core.Tokens(c).AccentText)
|
||||
}
|
||||
},
|
||||
}))
|
||||
@@ -104,10 +106,10 @@ func c2ExportDemo(c *ui.Context, log func(string)) {
|
||||
c2Number(c, &s.scale, "Scale", 1, 3, 0.5, log)
|
||||
c2Check(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
if mujica.Button(c, "Export PNG", mujica.ButtonOptions{Disabled: s.disabled}).Clicked() {
|
||||
data, err := mujica.ChartExport(c, func(c *ui.Context) {
|
||||
mujica.BarChart(c, []string{"Nave", "Choir", "Crypt"}, []mujica.ChartSeries{{Name: "Pews", Values: []float64{3, 5, 2}}}, mujica.BarChartOptions{Frame: mujica.ChartFrame{Label: "Exported chart", Height: float32(s.height) - 80}})
|
||||
}, mujica.ChartExportOptions{Width: int(s.width), Height: int(s.height), Scale: float32(s.scale)})
|
||||
if input.Button(c, "Export PNG", input.ButtonOptions{Disabled: s.disabled}).Clicked() {
|
||||
data, err := chart.ChartExport(c, func(c *ui.Context) {
|
||||
chart.BarChart(c, []string{"Nave", "Choir", "Crypt"}, []chart.ChartSeries{{Name: "Pews", Values: []float64{3, 5, 2}}}, chart.BarChartOptions{Frame: chart.ChartFrame{Label: "Exported chart", Height: float32(s.height) - 80}})
|
||||
}, chart.ChartExportOptions{Width: int(s.width), Height: int(s.height), Scale: float32(s.scale)})
|
||||
if err == nil {
|
||||
s.bitmap, err = ui.DecodeBitmap(data)
|
||||
}
|
||||
@@ -121,7 +123,7 @@ func c2ExportDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
}
|
||||
if s.error != "" {
|
||||
ui.Text(c, s.error).TextColor(mujica.Tokens(c).Danger).Role(ui.RoleStatus)
|
||||
ui.Text(c, s.error).TextColor(core.Tokens(c).Danger).Role(ui.RoleStatus)
|
||||
}
|
||||
if s.bitmap != nil {
|
||||
ui.Image(c, s.bitmap).Size(float32(s.width), float32(s.height))
|
||||
@@ -147,7 +149,7 @@ func c2SparkBarDemo(c *ui.Context, log func(string)) {
|
||||
vals = nil
|
||||
}
|
||||
ui.Column(c).Disabled(s.disabled).Children(func() {
|
||||
c1Log(log, "SparkBar", mujica.SparkBar(c, vals, mujica.SparkBarOptions{Frame: f, BarWidth: float32(s.width)}))
|
||||
c1Log(log, "SparkBar", chart.SparkBar(c, vals, chart.SparkBarOptions{Frame: f, BarWidth: float32(s.width)}))
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -163,11 +165,11 @@ func c2TrendDemo(c *ui.Context, log func(string)) {
|
||||
c2Number(c, &s.precision, "Precision", 0, 3, 1, log)
|
||||
c2Check(c, &s.invert, "Invert", log)
|
||||
}, func() {
|
||||
mujica.TrendIndicator(c, s.change, mujica.TrendIndicatorOptions{Precision: int(s.precision), Unit: "%", Note: "vs Lent", Invert: s.invert})
|
||||
chart.TrendIndicator(c, s.change, chart.TrendIndicatorOptions{Precision: int(s.precision), Unit: "%", Note: "vs Lent", Invert: s.invert})
|
||||
ui.Row(c).Gap(24).Children(func() {
|
||||
mujica.TrendIndicator(c, 3, mujica.TrendIndicatorOptions{})
|
||||
mujica.TrendIndicator(c, -3, mujica.TrendIndicatorOptions{})
|
||||
mujica.TrendIndicator(c, 0, mujica.TrendIndicatorOptions{})
|
||||
chart.TrendIndicator(c, 3, chart.TrendIndicatorOptions{})
|
||||
chart.TrendIndicator(c, -3, chart.TrendIndicatorOptions{})
|
||||
chart.TrendIndicator(c, 0, chart.TrendIndicatorOptions{})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -184,8 +186,8 @@ func c2EmptyDemo(c *ui.Context, log func(string)) {
|
||||
c2Check(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
f, _ := c2Frame(c, "ChartEmptyState", &s.status, log)
|
||||
ui.Column(c).Disabled(s.disabled).Border(1, mujica.Tokens(c).Border).Children(func() {
|
||||
if mujica.ChartEmptyState(c, f.Status, mujica.ChartEmptyStateOptions{Height: float32(s.height), Error: f.Error}).Changed() {
|
||||
ui.Column(c).Disabled(s.disabled).Border(1, core.Tokens(c).Border).Children(func() {
|
||||
if chart.ChartEmptyState(c, f.Status, chart.ChartEmptyStateOptions{Height: float32(s.height), Error: f.Error}).Changed() {
|
||||
log("ChartEmptyState: retry")
|
||||
s.status = "Loading"
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
)
|
||||
|
||||
func c2Demo(t *testing.T, id string) (*ui.Tester, *[]string) {
|
||||
@@ -18,7 +18,7 @@ func c2Demo(t *testing.T, id string) (*ui.Tester, *[]string) {
|
||||
}
|
||||
events := &[]string{}
|
||||
tt := ui.NewTester(func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
core.Use(c, core.Settings{})
|
||||
ui.Scroll(c).Fill().Padding(24).Children(func() { charts2Pages[i].Demo(c, func(s string) { *events = append(*events, s) }) })
|
||||
}, 900, 760)
|
||||
tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1})
|
||||
|
||||
@@ -5,62 +5,62 @@ const chatinputCategory = "Chat Input"
|
||||
var chatinputPages = []page{
|
||||
{ID: "221", Name: "PromptComposer", ZhName: "提示编辑器", Category: chatinputCategory, Demo: chatinputDemoPromptComposer,
|
||||
Keys: "Enter 发送,Shift+Enter 换行;CmdEnterSends 下使用 Command/Ctrl+Enter;组合保护期间 Enter 留给输入法。",
|
||||
Code: "var prompt string\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tr := mujica.PromptComposer(c, &prompt, mujica.PromptComposerOptions{Placeholder: \"Ask the herald\"})\n\tif r.Submitted() {\n\t\tfmt.Println(\"send\", prompt)\n\t\tprompt = \"\"\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "var prompt string\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tr := chat.PromptComposer(c, &prompt, chat.PromptComposerOptions{Placeholder: \"Ask the herald\"})\n\tif r.Submitted() {\n\t\tfmt.Println(\"send\", prompt)\n\t\tprompt = \"\"\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "222", Name: "AttachmentChip", ZhName: "附件标签", Category: chatinputCategory, Demo: chatinputDemoAttachmentChip,
|
||||
Keys: "Tab 到移除与重试;Enter、Space 激活。",
|
||||
Code: "view := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tr := mujica.AttachmentChip(c, mujica.Attachment{Name: \"guests.csv\", Size: 4096, Uploading: true, Progress: 0.4})\n\tif r.Removed() {\n\t\tfmt.Println(\"cancel the upload\")\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "view := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tr := chat.AttachmentChip(c, chat.Attachment{Name: \"guests.csv\", Size: 4096, Uploading: true, Progress: 0.4})\n\tif r.Removed() {\n\t\tfmt.Println(\"cancel the upload\")\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "223", Name: "PasteImagePreview", ZhName: "图片预览", Category: chatinputCategory, Demo: chatinputDemoPasteImagePreview,
|
||||
Keys: "Tab 到移除按钮;Enter、Space 移除。",
|
||||
Code: "pic := ui.MustParseSVG([]byte(`<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"4\" height=\"3\"/>`))\nimages := []mujica.PastedImage{{ID: \"p1\", Source: pic, Name: \"screenshot\"}}\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tif id, ok := mujica.PasteImagePreview(c, images).Removed(); ok {\n\t\tfmt.Println(\"remove\", id)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "pic := ui.MustParseSVG([]byte(`<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"4\" height=\"3\"/>`))\nimages := []chat.PastedImage{{ID: \"p1\", Source: pic, Name: \"screenshot\"}}\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tif id, ok := chat.PasteImagePreview(c, images).Removed(); ok {\n\t\tfmt.Println(\"remove\", id)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "224", Name: "DragDropOverlay", ZhName: "拖放遮罩", Category: chatinputCategory, Demo: chatinputDemoDragDropOverlay,
|
||||
Keys: "内容中的操作按其自身键盘规则访问;文件拖入由操作系统驱动。",
|
||||
Code: "view := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\timages := func(path string) bool { return strings.HasSuffix(path, \".png\") }\n\tr := mujica.DragDropOverlay(c, mujica.DragDropOverlayOptions{Accept: images}, func() {\n\t\tui.Text(c, \"Drag images into the chat\")\n\t})\n\tfor _, path := range r.Accepted() {\n\t\tfmt.Println(\"attach\", path)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "view := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\timages := func(path string) bool { return strings.HasSuffix(path, \".png\") }\n\tr := chat.DragDropOverlay(c, chat.DragDropOverlayOptions{Accept: images}, func() {\n\t\tui.Text(c, \"Drag images into the chat\")\n\t})\n\tfor _, path := range r.Accepted() {\n\t\tfmt.Println(\"attach\", path)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "225", Name: "VoiceInputButton", ZhName: "语音输入按钮", Category: chatinputCategory, Demo: chatinputDemoVoiceInputButton,
|
||||
Keys: "Tab 到按钮;Enter、Space 开始或停止。",
|
||||
Code: "var rec mujica.VoiceInputOptions\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tif mujica.VoiceInputButton(c, rec).Changed() {\n\t\trec = mujica.VoiceInputOptions{Recording: !rec.Recording, Started: time.Now()}\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "var rec chat.VoiceInputOptions\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tif chat.VoiceInputButton(c, rec).Changed() {\n\t\trec = chat.VoiceInputOptions{Recording: !rec.Recording, Started: time.Now()}\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "226", Name: "VoiceWaveform", ZhName: "输入波形", Category: chatinputCategory, Demo: chatinputDemoVoiceWaveform,
|
||||
Keys: "展示组件,沿用外层 Tab 顺序。",
|
||||
Code: "levels := []float32{0.2, 0.6, 0.9, 0.4, 0.1}\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tmujica.VoiceWaveform(c, levels, mujica.VoiceWaveformOptions{})\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "levels := []float32{0.2, 0.6, 0.9, 0.4, 0.1}\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tchat.VoiceWaveform(c, levels, chat.VoiceWaveformOptions{})\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "227", Name: "SlashCommandMenu", ZhName: "斜杠命令菜单", Category: chatinputCategory, Demo: chatinputDemoSlashCommandMenu,
|
||||
Keys: "编辑器内 Up/Down 选择,Enter 选中,Tab 补全,Escape 关闭。",
|
||||
Code: "var prompt string\ncommands := []mujica.SlashCommand{{Name: \"summarize\", Description: \"Summarize the thread\"}}\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tcomposer := mujica.PromptComposer(c, &prompt, mujica.PromptComposerOptions{})\n\tif cmd, ok := mujica.SlashCommandMenu(c, composer, &prompt, commands).Chosen(); ok {\n\t\tfmt.Println(\"run\", cmd.Name)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "var prompt string\ncommands := []chat.SlashCommand{{Name: \"summarize\", Description: \"Summarize the thread\"}}\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tcomposer := chat.PromptComposer(c, &prompt, chat.PromptComposerOptions{})\n\tif cmd, ok := chat.SlashCommandMenu(c, composer, &prompt, commands).Chosen(); ok {\n\t\tfmt.Println(\"run\", cmd.Name)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "228", Name: "ContextMentionMenu", ZhName: "上下文提及菜单", Category: chatinputCategory, Demo: chatinputDemoContextMentionMenu,
|
||||
Keys: "编辑器内 Up/Down 选择,Enter 或 Tab 附加,Escape 关闭。",
|
||||
Code: "var prompt string\nvar found []mujica.ContextItem\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tcomposer := mujica.PromptComposer(c, &prompt, mujica.PromptComposerOptions{})\n\tr := mujica.ContextMentionMenu(c, composer, &prompt, found, mujica.ContextMentionOptions{})\n\tif r.QueryChanged() {\n\t\tfound = []mujica.ContextItem{{ID: \"f1\", Label: r.Query() + \".md\", Kind: mujica.ContextFile}}\n\t}\n\tif item, ok := r.Chosen(); ok {\n\t\tfmt.Println(\"attach\", item.ID)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "var prompt string\nvar found []chat.ContextItem\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tcomposer := chat.PromptComposer(c, &prompt, chat.PromptComposerOptions{})\n\tr := chat.ContextMentionMenu(c, composer, &prompt, found, chat.ContextMentionOptions{})\n\tif r.QueryChanged() {\n\t\tfound = []chat.ContextItem{{ID: \"f1\", Label: r.Query() + \".md\", Kind: chat.ContextFile}}\n\t}\n\tif item, ok := r.Chosen(); ok {\n\t\tfmt.Println(\"attach\", item.ID)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "229", Name: "ContextChips", ZhName: "上下文标签", Category: chatinputCategory, Demo: chatinputDemoContextChips,
|
||||
Keys: "Tab 到移除与展开;Enter、Space 激活。",
|
||||
Code: "items := []mujica.ContextItem{{ID: \"f1\", Label: \"guests.csv\", Kind: mujica.ContextFile}}\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tif id, ok := mujica.ContextChips(c, items, mujica.ContextChipsOptions{}).Removed(); ok {\n\t\tfmt.Println(\"detach\", id)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "items := []chat.ContextItem{{ID: \"f1\", Label: \"guests.csv\", Kind: chat.ContextFile}}\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tif id, ok := chat.ContextChips(c, items, chat.ContextChipsOptions{}).Removed(); ok {\n\t\tfmt.Println(\"detach\", id)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "230", Name: "ModelSelector", ZhName: "模型选择器", Category: chatinputCategory, Demo: chatinputDemoModelSelector,
|
||||
Keys: "Tab 到选择器;Up/Down、Home/End 选择,Enter/Space 确认,Escape 关闭。",
|
||||
Code: "model := \"herald\"\nmodels := []mujica.ModelOption{{ID: \"herald\", Name: \"Herald 2\", Capabilities: []string{\"Vision\"}, ContextTokens: 200000}}\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tif mujica.ModelSelector(c, &model, models, mujica.ModelSelectorOptions{}).Changed() {\n\t\tfmt.Println(\"model\", model)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "model := \"herald\"\nmodels := []chat.ModelOption{{ID: \"herald\", Name: \"Herald 2\", Capabilities: []string{\"Vision\"}, ContextTokens: 200000}}\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tif chat.ModelSelector(c, &model, models, chat.ModelSelectorOptions{}).Changed() {\n\t\tfmt.Println(\"model\", model)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "231", Name: "ModeSelector", ZhName: "模式选择器", Category: chatinputCategory, Demo: chatinputDemoModeSelector,
|
||||
Keys: "Tab 进入模式组;Left/Right 切换模式。",
|
||||
Code: "mode := mujica.ModeChat\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tif mujica.ModeSelector(c, &mode, mujica.ModeSelectorOptions{}).Changed() {\n\t\tfmt.Println(\"mode\", mode)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "mode := chat.ModeChat\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tif chat.ModeSelector(c, &mode, chat.ModeSelectorOptions{}).Changed() {\n\t\tfmt.Println(\"mode\", mode)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "232", Name: "ToolToggleMenu", ZhName: "工具开关菜单", Category: chatinputCategory, Demo: chatinputDemoToolToggleMenu,
|
||||
Keys: "Tab 到菜单按钮;Enter/Space 打开;Tab 到开关与授权;Escape 关闭。",
|
||||
Code: "web := true\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\ttools := []mujica.ToolItem{{ID: \"web\", Label: \"Web search\", On: &web}}\n\tif id, ok := mujica.ToolToggleMenu(c, tools).Changed(); ok {\n\t\tfmt.Println(\"toggled\", id)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "web := true\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\ttools := []chat.ToolItem{{ID: \"web\", Label: \"Web search\", On: &web}}\n\tif id, ok := chat.ToolToggleMenu(c, tools).Changed(); ok {\n\t\tfmt.Println(\"toggled\", id)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "233", Name: "ParameterPanel", ZhName: "参数面板", Category: chatinputCategory, Demo: chatinputDemoParameterPanel,
|
||||
Keys: "Tab 到滑块与重置;方向键按 Step 调整,Home/End 到范围端点;Enter/Space 重置。",
|
||||
Code: "temperature := 1.0\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tparams := []mujica.ModelParam{{Label: \"Temperature\", Value: &temperature, Min: 0, Max: 2, Step: 0.1, Default: 1}}\n\tmujica.ParameterPanel(c, params)\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "temperature := 1.0\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tparams := []chat.ModelParam{{Label: \"Temperature\", Value: &temperature, Min: 0, Max: 2, Step: 0.1, Default: 1}}\n\tchat.ParameterPanel(c, params)\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "234", Name: "TokenCounter", ZhName: "Token计数", Category: chatinputCategory, Demo: chatinputDemoTokenCounter,
|
||||
Keys: "展示组件,沿用外层 Tab 顺序。",
|
||||
Code: "view := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tmujica.TokenCounter(c, 1234, 8000)\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "view := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tchat.TokenCounter(c, 1234, 8000)\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "235", Name: "ContextWindowMeter", ZhName: "上下文容量", Category: chatinputCategory, Demo: chatinputDemoContextWindowMeter,
|
||||
Keys: "展示组件,沿用外层 Tab 顺序。",
|
||||
Code: "segments := []mujica.ContextSegment{{Label: \"System\", Tokens: 1200}, {Label: \"History\", Tokens: 58000}}\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tmujica.ContextWindowMeter(c, segments, 128000, mujica.ContextWindowMeterOptions{})\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "segments := []chat.ContextSegment{{Label: \"System\", Tokens: 1200}, {Label: \"History\", Tokens: 58000}}\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tchat.ContextWindowMeter(c, segments, 128000, chat.ContextWindowMeterOptions{})\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "236", Name: "SendButton", ZhName: "发送按钮", Category: chatinputCategory, Demo: chatinputDemoSendButton,
|
||||
Keys: "Tab 到按钮;Enter、Space 激活;快捷键提示由调用方绑定。",
|
||||
Code: "streaming := false\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tr := mujica.SendButton(c, mujica.SendButtonOptions{Streaming: streaming, Shortcut: \"Enter\"})\n\tif r.Sent() || r.Stopped() {\n\t\tstreaming = r.Sent()\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "streaming := false\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tr := chat.SendButton(c, chat.SendButtonOptions{Streaming: streaming, Shortcut: \"Enter\"})\n\tif r.Sent() || r.Stopped() {\n\t\tstreaming = r.Sent()\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "237", Name: "CitationBadge", ZhName: "引用编号", Category: chatinputCategory, Demo: chatinputDemoCitationBadge,
|
||||
Keys: "Tab 到引用;Enter、Space 定位;悬停或键盘焦点 500 毫秒后预览;Escape 关闭预览。",
|
||||
Code: "var cites mujica.CitationState\nsrc := mujica.Source{Title: \"The Palace of Versailles\", URL: \"https://example.com/versailles\"}\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tif mujica.CitationBadge(c, 1, src, &cites).Changed() {\n\t\tfmt.Println(\"show source\", cites.Active)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "var cites chat.CitationState\nsrc := chat.Source{Title: \"The Palace of Versailles\", URL: \"https://example.com/versailles\"}\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tif chat.CitationBadge(c, 1, src, &cites).Changed() {\n\t\tfmt.Println(\"show source\", cites.Active)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "238", Name: "SourceCard", ZhName: "来源卡片", Category: chatinputCategory, Demo: chatinputDemoSourceCard,
|
||||
Keys: "Tab 到卡片;Enter、Space 请求打开。",
|
||||
Code: "var cites mujica.CitationState\nsrc := mujica.Source{Title: \"Hall of Mirrors\", URL: \"https://example.org/hall\", Snippet: \"Seventeen arches.\"}\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tif mujica.SourceCard(c, 1, src, &cites).Clicked() {\n\t\tfmt.Println(\"open\", src.URL)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "var cites chat.CitationState\nsrc := chat.Source{Title: \"Hall of Mirrors\", URL: \"https://example.org/hall\", Snippet: \"Seventeen arches.\"}\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tif chat.SourceCard(c, 1, src, &cites).Clicked() {\n\t\tfmt.Println(\"open\", src.URL)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "239", Name: "SourcesPanel", ZhName: "来源面板", Category: chatinputCategory, Demo: chatinputDemoSourcesPanel,
|
||||
Keys: "Tab 到可见来源;Enter、Space 请求打开;滚轮滚动;Active 变化定位来源。",
|
||||
Code: "var cites mujica.CitationState\nsources := []mujica.Source{{Title: \"Hall of Mirrors\", URL: \"https://example.org/hall\"}}\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tif n, ok := mujica.SourcesPanel(c, sources, &cites).Opened(); ok {\n\t\tfmt.Println(\"open\", sources[n-1].URL)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "var cites chat.CitationState\nsources := []chat.Source{{Title: \"Hall of Mirrors\", URL: \"https://example.org/hall\"}}\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tif n, ok := chat.SourcesPanel(c, sources, &cites).Opened(); ok {\n\t\tfmt.Println(\"open\", sources[n-1].URL)\n\t}\n}\nui.Render(view, 400, 300, 1)"},
|
||||
{ID: "240", Name: "SearchProgress", ZhName: "搜索进度", Category: chatinputCategory, Demo: chatinputDemoSearchProgress,
|
||||
Keys: "Tab 到折叠标题;Enter、Space 展开或收起。",
|
||||
Code: "view := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tstages := []mujica.SearchStage{{Label: \"Searching the web\", Done: true}, {Label: \"Reading sources\"}}\n\tmujica.SearchProgress(c, mujica.SearchProgressOptions{Query: \"Versailles\", Read: 3, Total: 8, Stages: stages})\n}\nui.Render(view, 400, 300, 1)"},
|
||||
Code: "view := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tstages := []chat.SearchStage{{Label: \"Searching the web\", Done: true}, {Label: \"Reading sources\"}}\n\tchat.SearchProgress(c, chat.SearchProgressOptions{Query: \"Versailles\", Read: 3, Total: 8, Stages: stages})\n}\nui.Render(view, 400, 300, 1)"},
|
||||
}
|
||||
@@ -7,7 +7,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chat"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func chatinputDemoPromptComposer(c *ui.Context, log func(string)) {
|
||||
@@ -23,20 +26,20 @@ func chatinputDemoPromptComposer(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.attached, "Attachment", log)
|
||||
}, func() {
|
||||
ui.Column(c).Width(520).Children(func() {
|
||||
r := mujica.PromptComposer(c, &s.text, mujica.PromptComposerOptions{Label: "Prompt", Placeholder: "Ask the herald", Disabled: s.disabled, CmdEnterSends: s.cmd, Composing: s.composing,
|
||||
r := chat.PromptComposer(c, &s.text, chat.PromptComposerOptions{Label: "Prompt", Placeholder: "Ask the herald", Disabled: s.disabled, CmdEnterSends: s.cmd, Composing: s.composing,
|
||||
Attachments: func() {
|
||||
if s.attached && mujica.AttachmentChip(c, mujica.Attachment{Name: "guests.csv", Size: 4096}).Removed() {
|
||||
if s.attached && chat.AttachmentChip(c, chat.Attachment{Name: "guests.csv", Size: 4096}).Removed() {
|
||||
s.attached = false
|
||||
log("remove attachment")
|
||||
}
|
||||
},
|
||||
Tools: func() {
|
||||
if _, ok := mujica.ToolToggleMenu(c, []mujica.ToolItem{{ID: "web", Label: "Web search", On: &s.web}}).Changed(); ok {
|
||||
if _, ok := chat.ToolToggleMenu(c, []chat.ToolItem{{ID: "web", Label: "Web search", On: &s.web}}).Changed(); ok {
|
||||
log("toggle web search")
|
||||
}
|
||||
},
|
||||
Actions: func() {
|
||||
r := mujica.SendButton(c, mujica.SendButtonOptions{Streaming: s.streaming, Disabled: strings.TrimSpace(s.text) == "" || s.composing, Shortcut: "Enter"})
|
||||
r := chat.SendButton(c, chat.SendButtonOptions{Streaming: s.streaming, Disabled: strings.TrimSpace(s.text) == "" || s.composing, Shortcut: "Enter"})
|
||||
if r.Sent() {
|
||||
s.streaming = true
|
||||
s.text = ""
|
||||
@@ -70,10 +73,10 @@ func chatinputDemoAttachmentChip(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.upload, "Uploading", log)
|
||||
choiceFlag(c, &s.fail, "Failed", log)
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
if mujica.Slider(c, &s.progress, mujica.SliderOptions{Min: 0, Max: 1, Step: 0.1, Label: "Upload progress"}).Changed() {
|
||||
if input.Slider(c, &s.progress, input.SliderOptions{Min: 0, Max: 1, Step: 0.1, Label: "Upload progress"}).Changed() {
|
||||
log(fmt.Sprint("progress ", s.progress))
|
||||
}
|
||||
if mujica.Button(c, "Restore attachment", mujica.ButtonOptions{}).Clicked() {
|
||||
if input.Button(c, "Restore attachment", input.ButtonOptions{}).Clicked() {
|
||||
s.removed = false
|
||||
log("restore")
|
||||
}
|
||||
@@ -83,11 +86,11 @@ func chatinputDemoAttachmentChip(c *ui.Context, log func(string)) {
|
||||
ui.Text(c, "Attachment removed")
|
||||
return
|
||||
}
|
||||
a := mujica.Attachment{Name: "guests.csv", Size: 4096, Uploading: s.upload, Progress: s.progress}
|
||||
a := chat.Attachment{Name: "guests.csv", Size: 4096, Uploading: s.upload, Progress: s.progress}
|
||||
if s.fail {
|
||||
a.Err = errors.New("upload rejected")
|
||||
}
|
||||
r := mujica.AttachmentChip(c, a)
|
||||
r := chat.AttachmentChip(c, a)
|
||||
if r.Removed() {
|
||||
s.removed = true
|
||||
log("remove")
|
||||
@@ -102,30 +105,30 @@ func chatinputDemoAttachmentChip(c *ui.Context, log func(string)) {
|
||||
})
|
||||
}
|
||||
|
||||
func chatinputPictures() []mujica.PastedImage {
|
||||
var out []mujica.PastedImage
|
||||
func chatinputPictures() []chat.PastedImage {
|
||||
var out []chat.PastedImage
|
||||
for i, col := range []string{"#7D2034", "#355E80"} {
|
||||
svg := fmt.Sprintf(`<svg xmlns="http://www.w3.org/2000/svg" width="72" height="72"><rect width="72" height="72" fill="%s"/><circle cx="36" cy="36" r="18" fill="#F2EBDD"/></svg>`, col)
|
||||
out = append(out, mujica.PastedImage{ID: fmt.Sprint(i), Name: fmt.Sprintf("Picture %d", i+1), Source: ui.MustParseSVG([]byte(svg))})
|
||||
out = append(out, chat.PastedImage{ID: fmt.Sprint(i), Name: fmt.Sprintf("Picture %d", i+1), Source: ui.MustParseSVG([]byte(svg))})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func chatinputDemoPasteImagePreview(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
images []mujica.PastedImage
|
||||
images []chat.PastedImage
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "223", func() state { return state{images: chatinputPictures()} })
|
||||
choiceDemo(c, func() {
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
if mujica.Button(c, "Restore images", mujica.ButtonOptions{}).Clicked() {
|
||||
if input.Button(c, "Restore images", input.ButtonOptions{}).Clicked() {
|
||||
s.images = chatinputPictures()
|
||||
log("restore images")
|
||||
}
|
||||
}, func() {
|
||||
ui.Column(c).Padding(12).Disabled(s.disabled).Children(func() {
|
||||
if id, ok := mujica.PasteImagePreview(c, s.images).Removed(); ok {
|
||||
if id, ok := chat.PasteImagePreview(c, s.images).Removed(); ok {
|
||||
for i, im := range s.images {
|
||||
if im.ID == id {
|
||||
s.images = append(s.images[:i], s.images[i+1:]...)
|
||||
@@ -150,8 +153,8 @@ func chatinputDemoDragDropOverlay(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
ui.Column(c).Width(520).Height(180).Disabled(s.disabled).Children(func() {
|
||||
r := mujica.DragDropOverlay(c, mujica.DragDropOverlayOptions{Accept: func(p string) bool { return !s.imagesOnly || strings.EqualFold(filepath.Ext(p), ".png") }}, func() {
|
||||
ui.Box(c).Fill().Border(1, mujica.Tokens(c).ControlBorder).Center().Children(func() { ui.Text(c, "Drop files here") })
|
||||
r := chat.DragDropOverlay(c, chat.DragDropOverlayOptions{Accept: func(p string) bool { return !s.imagesOnly || strings.EqualFold(filepath.Ext(p), ".png") }}, func() {
|
||||
ui.Box(c).Fill().Border(1, core.Tokens(c).ControlBorder).Center().Children(func() { ui.Text(c, "Drop files here") })
|
||||
})
|
||||
r.Element.Fill()
|
||||
if n := len(r.Accepted()); n > 0 {
|
||||
@@ -168,16 +171,16 @@ func chatinputDemoDragDropOverlay(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
func chatinputDemoVoiceInputButton(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "225", func() mujica.VoiceInputOptions { return mujica.VoiceInputOptions{} })
|
||||
s := ui.Local(c.Root(), "225", func() chat.VoiceInputOptions { return chat.VoiceInputOptions{} })
|
||||
choiceDemo(c, func() {
|
||||
choiceFlag(c, &s.Disabled, "Disabled", log)
|
||||
if mujica.Button(c, "Permission failure", mujica.ButtonOptions{}).Clicked() {
|
||||
if input.Button(c, "Permission failure", input.ButtonOptions{}).Clicked() {
|
||||
s.Recording = false
|
||||
s.Denied = true
|
||||
log("permission denied")
|
||||
}
|
||||
}, func() {
|
||||
if mujica.VoiceInputButton(c, *s).Changed() {
|
||||
if chat.VoiceInputButton(c, *s).Changed() {
|
||||
s.Recording = !s.Recording
|
||||
s.Denied = false
|
||||
s.Started = c.Now()
|
||||
@@ -195,7 +198,7 @@ func chatinputDemoVoiceWaveform(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "226", func() state { return state{level: 0.6} })
|
||||
choiceDemo(c, func() {
|
||||
choiceFlag(c, &s.silent, "Silent", log)
|
||||
if mujica.Slider(c, &s.level, mujica.SliderOptions{Min: 0, Max: 1, Step: 0.1, Label: "Input level"}).Changed() {
|
||||
if input.Slider(c, &s.level, input.SliderOptions{Min: 0, Max: 1, Step: 0.1, Label: "Input level"}).Changed() {
|
||||
log(fmt.Sprint("level ", s.level))
|
||||
}
|
||||
}, func() {
|
||||
@@ -205,6 +208,6 @@ func chatinputDemoVoiceWaveform(c *ui.Context, log func(string)) {
|
||||
levels[i] = float32(s.level) * float32(i%7+1) / 7
|
||||
}
|
||||
}
|
||||
mujica.VoiceWaveform(c, levels, mujica.VoiceWaveformOptions{})
|
||||
chat.VoiceWaveform(c, levels, chat.VoiceWaveformOptions{})
|
||||
})
|
||||
}
|
||||
@@ -2,9 +2,12 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
"strings"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chat"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func chatinputDemoSlashCommandMenu(c *ui.Context, log func(string)) {
|
||||
@@ -18,13 +21,13 @@ func chatinputDemoSlashCommandMenu(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.empty, "Empty commands", log)
|
||||
}, func() {
|
||||
ui.Column(c).Width(520).Children(func() {
|
||||
p := mujica.PromptComposer(c, &s.text, mujica.PromptComposerOptions{Label: "Command prompt", Disabled: s.disabled})
|
||||
commands := []mujica.SlashCommand{{Name: "summarize", Description: "Summarize the thread"}, {Name: "search", Description: "Search the web"}, {Name: "translate", Description: "Translate the message"}}
|
||||
p := chat.PromptComposer(c, &s.text, chat.PromptComposerOptions{Label: "Command prompt", Disabled: s.disabled})
|
||||
commands := []chat.SlashCommand{{Name: "summarize", Description: "Summarize the thread"}, {Name: "search", Description: "Search the web"}, {Name: "translate", Description: "Translate the message"}}
|
||||
if s.empty {
|
||||
commands = nil
|
||||
}
|
||||
before := s.text
|
||||
if cmd, ok := mujica.SlashCommandMenu(c, p, &s.text, commands).Chosen(); ok {
|
||||
if cmd, ok := chat.SlashCommandMenu(c, p, &s.text, commands).Chosen(); ok {
|
||||
log("command " + cmd.Name)
|
||||
} else if before != s.text {
|
||||
log("complete command")
|
||||
@@ -39,14 +42,14 @@ func chatinputDemoSlashCommandMenu(c *ui.Context, log func(string)) {
|
||||
})
|
||||
}
|
||||
|
||||
func chatinputContextItems() []mujica.ContextItem {
|
||||
return []mujica.ContextItem{{ID: "file", Label: "guests.csv", Kind: mujica.ContextFile}, {ID: "doc", Label: "Court protocol", Kind: mujica.ContextDoc}, {ID: "web", Label: "Versailles", Detail: "example.com", Kind: mujica.ContextWeb}}
|
||||
func chatinputContextItems() []chat.ContextItem {
|
||||
return []chat.ContextItem{{ID: "file", Label: "guests.csv", Kind: chat.ContextFile}, {ID: "doc", Label: "Court protocol", Kind: chat.ContextDoc}, {ID: "web", Label: "Versailles", Detail: "example.com", Kind: chat.ContextWeb}}
|
||||
}
|
||||
|
||||
func chatinputDemoContextMentionMenu(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
text, query string
|
||||
items []mujica.ContextItem
|
||||
items []chat.ContextItem
|
||||
loading, disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "228", func() state { return state{text: "@", items: chatinputContextItems()} })
|
||||
@@ -55,12 +58,12 @@ func chatinputDemoContextMentionMenu(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
ui.Column(c).Width(520).Children(func() {
|
||||
p := mujica.PromptComposer(c, &s.text, mujica.PromptComposerOptions{Label: "Context prompt", Disabled: s.disabled})
|
||||
p := chat.PromptComposer(c, &s.text, chat.PromptComposerOptions{Label: "Context prompt", Disabled: s.disabled})
|
||||
items := s.items
|
||||
if s.loading {
|
||||
items = nil
|
||||
}
|
||||
r := mujica.ContextMentionMenu(c, p, &s.text, items, mujica.ContextMentionOptions{Loading: s.loading})
|
||||
r := chat.ContextMentionMenu(c, p, &s.text, items, chat.ContextMentionOptions{Loading: s.loading})
|
||||
if r.QueryChanged() {
|
||||
s.query = r.Query()
|
||||
s.items = nil
|
||||
@@ -87,23 +90,23 @@ func chatinputDemoContextMentionMenu(c *ui.Context, log func(string)) {
|
||||
|
||||
func chatinputDemoContextChips(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
items []mujica.ContextItem
|
||||
items []chat.ContextItem
|
||||
max float64
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "229", func() state { return state{items: chatinputContextItems(), max: 2} })
|
||||
choiceDemo(c, func() {
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
if mujica.NumberInput(c, &s.max, mujica.NumberInputOptions{Min: 1, Max: 5, Step: 1, Label: "Visible chips"}).Changed() {
|
||||
if input.NumberInput(c, &s.max, input.NumberInputOptions{Min: 1, Max: 5, Step: 1, Label: "Visible chips"}).Changed() {
|
||||
log(fmt.Sprint("max ", s.max))
|
||||
}
|
||||
if mujica.Button(c, "Restore context", mujica.ButtonOptions{}).Clicked() {
|
||||
if input.Button(c, "Restore context", input.ButtonOptions{}).Clicked() {
|
||||
s.items = chatinputContextItems()
|
||||
log("restore context")
|
||||
}
|
||||
}, func() {
|
||||
ui.Column(c).Width(520).Disabled(s.disabled).Children(func() {
|
||||
if id, ok := mujica.ContextChips(c, s.items, mujica.ContextChipsOptions{Max: int(s.max)}).Removed(); ok {
|
||||
if id, ok := chat.ContextChips(c, s.items, chat.ContextChipsOptions{Max: int(s.max)}).Removed(); ok {
|
||||
for i, it := range s.items {
|
||||
if it.ID == id {
|
||||
s.items = append(s.items[:i], s.items[i+1:]...)
|
||||
@@ -126,11 +129,11 @@ func chatinputDemoModelSelector(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
choiceFlag(c, &s.unavailable, "Restricted model", log)
|
||||
}, func() {
|
||||
models := []mujica.ModelOption{{ID: "herald", Name: "Herald 2", Capabilities: []string{"Vision", "Tools"}, ContextTokens: 200000}, {ID: "page", Name: "Page Mini", Capabilities: []string{"Fast"}, ContextTokens: 32000}, {ID: "oracle", Name: "Oracle", ContextTokens: 1000000}}
|
||||
models := []chat.ModelOption{{ID: "herald", Name: "Herald 2", Capabilities: []string{"Vision", "Tools"}, ContextTokens: 200000}, {ID: "page", Name: "Page Mini", Capabilities: []string{"Fast"}, ContextTokens: 32000}, {ID: "oracle", Name: "Oracle", ContextTokens: 1000000}}
|
||||
if s.unavailable {
|
||||
models[2].Unavailable = "Needs the Court plan"
|
||||
}
|
||||
if mujica.ModelSelector(c, &s.model, models, mujica.ModelSelectorOptions{Disabled: s.disabled}).Changed() {
|
||||
if chat.ModelSelector(c, &s.model, models, chat.ModelSelectorOptions{Disabled: s.disabled}).Changed() {
|
||||
log("model " + s.model)
|
||||
}
|
||||
})
|
||||
@@ -138,12 +141,12 @@ func chatinputDemoModelSelector(c *ui.Context, log func(string)) {
|
||||
|
||||
func chatinputDemoModeSelector(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
mode mujica.ChatMode
|
||||
mode chat.ChatMode
|
||||
icons, disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "231", func() state { return state{} })
|
||||
choiceDemo(c, func() { choiceFlag(c, &s.icons, "Icons only", log); choiceFlag(c, &s.disabled, "Disabled", log) }, func() {
|
||||
if mujica.ModeSelector(c, &s.mode, mujica.ModeSelectorOptions{IconsOnly: s.icons, Disabled: s.disabled}).Changed() {
|
||||
if chat.ModeSelector(c, &s.mode, chat.ModeSelectorOptions{IconsOnly: s.icons, Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprint("mode ", s.mode))
|
||||
}
|
||||
})
|
||||
@@ -154,7 +157,7 @@ func chatinputDemoToolToggleMenu(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "232", func() state { return state{web: true} })
|
||||
choiceDemo(c, func() { choiceFlag(c, &s.authorized, "Authorized", log); choiceFlag(c, &s.disabled, "Disabled", log) }, func() {
|
||||
ui.Column(c).Disabled(s.disabled).Children(func() {
|
||||
r := mujica.ToolToggleMenu(c, []mujica.ToolItem{{ID: "web", Label: "Web search", On: &s.web}, {ID: "code", Label: "Code execution", On: &s.code}, {ID: "mail", Label: "Mail", On: &s.mail, NeedsAuth: true, Authorized: s.authorized}})
|
||||
r := chat.ToolToggleMenu(c, []chat.ToolItem{{ID: "web", Label: "Web search", On: &s.web}, {ID: "code", Label: "Code execution", On: &s.code}, {ID: "mail", Label: "Mail", On: &s.mail, NeedsAuth: true, Authorized: s.authorized}})
|
||||
if id, ok := r.Changed(); ok {
|
||||
log("toggle " + id)
|
||||
}
|
||||
@@ -174,7 +177,7 @@ func chatinputDemoParameterPanel(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "233", func() state { return state{temp: 0.7, top: 1, length: 2048} })
|
||||
choiceDemo(c, func() { choiceFlag(c, &s.disabled, "Disabled", log) }, func() {
|
||||
ui.Column(c).Width(420).Disabled(s.disabled).Children(func() {
|
||||
if mujica.ParameterPanel(c, []mujica.ModelParam{{Label: "Temperature", Value: &s.temp, Min: 0, Max: 2, Step: 0.1, Default: 1}, {Label: "Top-p", Value: &s.top, Min: 0, Max: 1, Step: 0.05, Default: 1}, {Label: "Max tokens", Value: &s.length, Min: 256, Max: 8192, Step: 256, Default: 2048}}).Changed() {
|
||||
if chat.ParameterPanel(c, []chat.ModelParam{{Label: "Temperature", Value: &s.temp, Min: 0, Max: 2, Step: 0.1, Default: 1}, {Label: "Top-p", Value: &s.top, Min: 0, Max: 1, Step: 0.05, Default: 1}, {Label: "Max tokens", Value: &s.length, Min: 256, Max: 8192, Step: 256, Default: 2048}}).Changed() {
|
||||
log(fmt.Sprintf("parameters %.1f / %.2f / %.0f", s.temp, s.top, s.length))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2,44 +2,47 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
"time"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chat"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func chatinputDemoTokenCounter(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "234", func() float64 { return 1234 })
|
||||
choiceDemo(c, func() {
|
||||
if mujica.NumberInput(c, s, mujica.NumberInputOptions{Min: 0, Max: 12000, Step: 100, Label: "Estimated tokens"}).Changed() {
|
||||
if input.NumberInput(c, s, input.NumberInputOptions{Min: 0, Max: 12000, Step: 100, Label: "Estimated tokens"}).Changed() {
|
||||
log(fmt.Sprint("estimate ", *s))
|
||||
}
|
||||
}, func() {
|
||||
mujica.TokenCounter(c, int(*s), 8000)
|
||||
mujica.TokenCounter(c, 7500, 8000)
|
||||
mujica.TokenCounter(c, 9100, 8000)
|
||||
chat.TokenCounter(c, int(*s), 8000)
|
||||
chat.TokenCounter(c, 7500, 8000)
|
||||
chat.TokenCounter(c, 9100, 8000)
|
||||
})
|
||||
}
|
||||
|
||||
func chatinputDemoContextWindowMeter(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "235", func() float64 { return 58000 })
|
||||
choiceDemo(c, func() {
|
||||
if mujica.NumberInput(c, s, mujica.NumberInputOptions{Min: 0, Max: 150000, Step: 1000, Label: "History tokens"}).Changed() {
|
||||
if input.NumberInput(c, s, input.NumberInputOptions{Min: 0, Max: 150000, Step: 1000, Label: "History tokens"}).Changed() {
|
||||
log(fmt.Sprint("history ", *s))
|
||||
}
|
||||
}, func() {
|
||||
ui.Column(c).Width(520).Children(func() {
|
||||
mujica.ContextWindowMeter(c, []mujica.ContextSegment{{Label: "System", Tokens: 1200}, {Label: "History", Tokens: int(*s)}, {Label: "Attachments", Tokens: 24000}}, 128000, mujica.ContextWindowMeterOptions{})
|
||||
chat.ContextWindowMeter(c, []chat.ContextSegment{{Label: "System", Tokens: 1200}, {Label: "History", Tokens: int(*s)}, {Label: "Attachments", Tokens: 24000}}, 128000, chat.ContextWindowMeterOptions{})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func chatinputDemoSendButton(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "236", func() mujica.SendButtonOptions { return mujica.SendButtonOptions{Shortcut: "Enter"} })
|
||||
s := ui.Local(c.Root(), "236", func() chat.SendButtonOptions { return chat.SendButtonOptions{Shortcut: "Enter"} })
|
||||
choiceDemo(c, func() {
|
||||
choiceFlag(c, &s.Streaming, "Streaming", log)
|
||||
choiceFlag(c, &s.Disabled, "Disabled send", log)
|
||||
}, func() {
|
||||
r := mujica.SendButton(c, *s)
|
||||
r := chat.SendButton(c, *s)
|
||||
if r.Sent() {
|
||||
s.Streaming = true
|
||||
log("send")
|
||||
@@ -51,13 +54,13 @@ func chatinputDemoSendButton(c *ui.Context, log func(string)) {
|
||||
})
|
||||
}
|
||||
|
||||
func chatinputSources() []mujica.Source {
|
||||
return []mujica.Source{{Title: "The Palace of Versailles", URL: "https://example.com/versailles", Site: "example.com", Snippet: "Seat of the court from 1682.", Published: time.Date(2025, 5, 4, 0, 0, 0, 0, time.UTC)}, {Title: "Hall of Mirrors", URL: "https://example.org/hall", Site: "example.org", Snippet: "Seventeen arches face the garden."}, {Title: "Court etiquette", URL: "https://example.net/etiquette", Snippet: "The ranks of the court."}}
|
||||
func chatinputSources() []chat.Source {
|
||||
return []chat.Source{{Title: "The Palace of Versailles", URL: "https://example.com/versailles", Site: "example.com", Snippet: "Seat of the court from 1682.", Published: time.Date(2025, 5, 4, 0, 0, 0, 0, time.UTC)}, {Title: "Hall of Mirrors", URL: "https://example.org/hall", Site: "example.org", Snippet: "Seventeen arches face the garden."}, {Title: "Court etiquette", URL: "https://example.net/etiquette", Snippet: "The ranks of the court."}}
|
||||
}
|
||||
|
||||
func chatinputDemoCitationBadge(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
cites mujica.CitationState
|
||||
cites chat.CitationState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "237", func() state { return state{} })
|
||||
@@ -65,13 +68,13 @@ func chatinputDemoCitationBadge(c *ui.Context, log func(string)) {
|
||||
ui.Row(c).Disabled(s.disabled).Gap(8).AlignItems(ui.Center).Children(func() {
|
||||
ui.Text(c, "The court moved in 1682")
|
||||
for i, src := range chatinputSources() {
|
||||
if mujica.CitationBadge(c, i+1, src, &s.cites).Changed() {
|
||||
if chat.CitationBadge(c, i+1, src, &s.cites).Changed() {
|
||||
log(fmt.Sprint("locate source ", s.cites.Active))
|
||||
}
|
||||
}
|
||||
})
|
||||
ui.Column(c).Width(360).Height(260).Disabled(s.disabled).Children(func() {
|
||||
if n, ok := mujica.SourcesPanel(c, chatinputSources(), &s.cites).Opened(); ok {
|
||||
if n, ok := chat.SourcesPanel(c, chatinputSources(), &s.cites).Opened(); ok {
|
||||
log(fmt.Sprint("open source ", n))
|
||||
}
|
||||
})
|
||||
@@ -80,7 +83,7 @@ func chatinputDemoCitationBadge(c *ui.Context, log func(string)) {
|
||||
|
||||
func chatinputDemoSourceCard(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
cites mujica.CitationState
|
||||
cites chat.CitationState
|
||||
selected, disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "238", func() state { return state{} })
|
||||
@@ -90,7 +93,7 @@ func chatinputDemoSourceCard(c *ui.Context, log func(string)) {
|
||||
s.cites.Active = 1
|
||||
}
|
||||
ui.Column(c).Width(400).Disabled(s.disabled).Children(func() {
|
||||
if mujica.SourceCard(c, 1, chatinputSources()[0], &s.cites).Clicked() {
|
||||
if chat.SourceCard(c, 1, chatinputSources()[0], &s.cites).Clicked() {
|
||||
log("open source 1")
|
||||
}
|
||||
})
|
||||
@@ -99,26 +102,26 @@ func chatinputDemoSourceCard(c *ui.Context, log func(string)) {
|
||||
|
||||
func chatinputDemoSourcesPanel(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
cites mujica.CitationState
|
||||
sources []mujica.Source
|
||||
cites chat.CitationState
|
||||
sources []chat.Source
|
||||
disabled, empty, many bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "239", func() state { return state{sources: chatinputSources()} })
|
||||
choiceDemo(c, func() {
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
choiceFlag(c, &s.empty, "Empty", log)
|
||||
if mujica.Switch(c, &s.many, "100,000 sources", mujica.SwitchOptions{}).Changed() {
|
||||
if input.Switch(c, &s.many, "100,000 sources", input.SwitchOptions{}).Changed() {
|
||||
s.sources = chatinputSources()
|
||||
s.cites.Active = 0
|
||||
if s.many {
|
||||
s.sources = make([]mujica.Source, 100000)
|
||||
s.sources = make([]chat.Source, 100000)
|
||||
for i := range s.sources {
|
||||
s.sources[i] = mujica.Source{Title: fmt.Sprintf("Archive %d", i+1), URL: "https://example.com/archive"}
|
||||
s.sources[i] = chat.Source{Title: fmt.Sprintf("Archive %d", i+1), URL: "https://example.com/archive"}
|
||||
}
|
||||
}
|
||||
log(fmt.Sprint("sources ", len(s.sources)))
|
||||
}
|
||||
if mujica.Button(c, "Locate last source", mujica.ButtonOptions{Disabled: s.empty}).Clicked() {
|
||||
if input.Button(c, "Locate last source", input.ButtonOptions{Disabled: s.empty}).Clicked() {
|
||||
s.cites.Active = len(s.sources)
|
||||
log("locate last source")
|
||||
}
|
||||
@@ -129,7 +132,7 @@ func chatinputDemoSourcesPanel(c *ui.Context, log func(string)) {
|
||||
s.cites.Active = 0
|
||||
}
|
||||
ui.Column(c).Width(400).Height(320).Disabled(s.disabled).Children(func() {
|
||||
if n, ok := mujica.SourcesPanel(c, sources, &s.cites).Opened(); ok {
|
||||
if n, ok := chat.SourcesPanel(c, sources, &s.cites).Opened(); ok {
|
||||
log(fmt.Sprint("open source ", n))
|
||||
}
|
||||
})
|
||||
@@ -145,12 +148,12 @@ func chatinputDemoSearchProgress(c *ui.Context, log func(string)) {
|
||||
choiceDemo(c, func() {
|
||||
choiceFlag(c, &s.done, "Done", log)
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
if mujica.NumberInput(c, &s.read, mujica.NumberInputOptions{Min: 0, Max: 8, Step: 1, Label: "Read sources"}).Changed() {
|
||||
if input.NumberInput(c, &s.read, input.NumberInputOptions{Min: 0, Max: 8, Step: 1, Label: "Read sources"}).Changed() {
|
||||
log(fmt.Sprint("read ", s.read))
|
||||
}
|
||||
}, func() {
|
||||
ui.Column(c).Width(440).Disabled(s.disabled).Children(func() {
|
||||
if mujica.SearchProgress(c, mujica.SearchProgressOptions{Query: "Versailles 1682", Read: int(s.read), Total: 8, Done: s.done, Stages: []mujica.SearchStage{{Label: "Planning queries", Done: true}, {Label: "Searching the web", Done: true}, {Label: "Reading sources", Done: s.done}}}).Changed() {
|
||||
if chat.SearchProgress(c, chat.SearchProgressOptions{Query: "Versailles 1682", Read: int(s.read), Total: 8, Done: s.done, Stages: []chat.SearchStage{{Label: "Planning queries", Done: true}, {Label: "Searching the web", Done: true}, {Label: "Reading sources", Done: s.done}}}).Changed() {
|
||||
log("toggle search details")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
)
|
||||
|
||||
func chatinputDemoTester(t *testing.T, id string, mode mujica.Mode) (*ui.Tester, *[]string) {
|
||||
func chatinputDemoTester(t *testing.T, id string, mode core.Mode) (*ui.Tester, *[]string) {
|
||||
t.Helper()
|
||||
var p *page
|
||||
for i := range chatinputPages {
|
||||
@@ -25,7 +25,7 @@ func chatinputDemoTester(t *testing.T, id string, mode mujica.Mode) (*ui.Tester,
|
||||
}
|
||||
events := new([]string)
|
||||
tt := ui.NewTester(func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{Mode: mode})
|
||||
core.Use(c, core.Settings{Mode: mode})
|
||||
ui.Scroll(c).Fill().Padding(24).Gap(16).Children(func() { p.Demo(c, func(s string) { *events = append(*events, s) }) })
|
||||
}, 900, 800)
|
||||
tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1})
|
||||
@@ -40,7 +40,7 @@ func TestChatinputDemosRender(t *testing.T) {
|
||||
if p.Category != "Chat Input" || p.Code == "" || p.Keys == "" {
|
||||
t.Errorf("incomplete page %s", p.ID)
|
||||
}
|
||||
for _, mode := range []mujica.Mode{mujica.Light, mujica.Dark} {
|
||||
for _, mode := range []core.Mode{core.Light, core.Dark} {
|
||||
tt, _ := chatinputDemoTester(t, p.ID, mode)
|
||||
if tt.HasText("panic") {
|
||||
t.Errorf("%s: panic shown", p.ID)
|
||||
@@ -69,7 +69,7 @@ func TestChatinputDemosEvents(t *testing.T) {
|
||||
{"237", "Source 1: The Palace of Versailles"}, {"238", "Open The Palace of Versailles"},
|
||||
{"239", "Locate last source"}, {"240", "Searching for “Versailles 1682”"},
|
||||
} {
|
||||
tt, events := chatinputDemoTester(t, tc.id, mujica.Light)
|
||||
tt, events := chatinputDemoTester(t, tc.id, core.Light)
|
||||
if err := tt.Click(tc.click); err != nil {
|
||||
t.Fatalf("%s: %v", tc.id, err)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chat"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/display"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
const chatmsgCategory = "Chat Messages"
|
||||
@@ -26,7 +29,7 @@ var chatmsgReplies = []string{
|
||||
type chatmsgDemo struct {
|
||||
ids []string
|
||||
texts []string
|
||||
list mujica.MessageListState
|
||||
list chat.MessageListState
|
||||
next int
|
||||
sidebar bool
|
||||
dated bool
|
||||
@@ -54,27 +57,27 @@ func (d *chatmsgDemo) day(i int) time.Time {
|
||||
|
||||
// row shows message i as a bubble, users and the assistant taking turns.
|
||||
func (d *chatmsgDemo) row(c *ui.Context, i int) {
|
||||
role := mujica.MessageAssistant
|
||||
role := chat.MessageAssistant
|
||||
if i%2 == 0 {
|
||||
role = mujica.MessageUser
|
||||
role = chat.MessageUser
|
||||
}
|
||||
mujica.MessageBubble(c, role, mujica.MessageBubbleOptions{}, func() { mujica.MarkdownView(c, d.texts[i]) })
|
||||
chat.MessageBubble(c, role, chat.MessageBubbleOptions{}, func() { chat.MarkdownView(c, d.texts[i]) })
|
||||
}
|
||||
|
||||
// chatmsgControls adds messages to a demo chat and logs it.
|
||||
func chatmsgControls(c *ui.Context, log func(string), d *chatmsgDemo) {
|
||||
ui.Row(c).Gap(8).Children(func() {
|
||||
if mujica.Button(c, "Add message", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Add message", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
d.add()
|
||||
log(fmt.Sprintf("message %s added", d.ids[len(d.ids)-1]))
|
||||
}
|
||||
if mujica.Button(c, "Add 10", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Add 10", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
for range 10 {
|
||||
d.add()
|
||||
}
|
||||
log(fmt.Sprintf("10 messages added, %d in all", len(d.ids)))
|
||||
}
|
||||
if mujica.Button(c, "Jump to latest", mujica.ButtonOptions{Variant: mujica.Ghost}).Clicked() {
|
||||
if input.Button(c, "Jump to latest", input.ButtonOptions{Variant: input.Ghost}).Clicked() {
|
||||
d.list.ScrollToEnd()
|
||||
log("scrolled to end")
|
||||
}
|
||||
@@ -84,8 +87,8 @@ func chatmsgControls(c *ui.Context, log func(string), d *chatmsgDemo) {
|
||||
var chatmsgListPages = []page{
|
||||
{ID: "201", Name: "ChatContainer", ZhName: "对话容器", Category: chatmsgCategory,
|
||||
Keys: "Tab 在侧栏、消息与输入区之间移动;消息区滚轮与 PageUp/PageDown 滚动;上翻后出现回到最新按钮。",
|
||||
Code: `mujica.ChatContainer(c, &chat, len(msgs), mujica.ChatContainerOptions{
|
||||
List: mujica.MessageListOptions{ID: func(i int) string { return msgs[i] }},
|
||||
Code: `chat.ChatContainer(c, &thread, len(msgs), chat.ChatContainerOptions{
|
||||
List: chat.MessageListOptions{ID: func(i int) string { return msgs[i] }},
|
||||
Input: func() { ui.Text(c, "Composer goes here") },
|
||||
}, func(i int) { ui.Text(c, msgs[i]) })`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
@@ -96,11 +99,11 @@ var chatmsgListPages = []page{
|
||||
chatmsgControls(c, log, d)
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
ui.Box(c).Height(420).Border(1, mujica.Tokens(c).Border).Clip().Children(func() {
|
||||
opts := mujica.ChatContainerOptions{
|
||||
List: mujica.MessageListOptions{ID: func(i int) string { return d.ids[i] }},
|
||||
ui.Box(c).Height(420).Border(1, core.Tokens(c).Border).Clip().Children(func() {
|
||||
opts := chat.ChatContainerOptions{
|
||||
List: chat.MessageListOptions{ID: func(i int) string { return d.ids[i] }},
|
||||
Input: func() {
|
||||
r := mujica.TextInput(c, draft, mujica.TextInputOptions{Label: "Message", Placeholder: "Write to the herald"})
|
||||
r := input.TextInput(c, draft, input.TextInputOptions{Label: "Message", Placeholder: "Write to the herald"})
|
||||
if r.Submitted() && strings.TrimSpace(*draft) != "" {
|
||||
d.add()
|
||||
d.texts[len(d.texts)-1] = *draft
|
||||
@@ -118,7 +121,7 @@ var chatmsgListPages = []page{
|
||||
})
|
||||
}
|
||||
}
|
||||
r := mujica.ChatContainer(c, &d.list, len(d.ids), opts, func(i int) { d.row(c, i) })
|
||||
r := chat.ChatContainer(c, &d.list, len(d.ids), opts, func(i int) { d.row(c, i) })
|
||||
if r.Unread() > 0 {
|
||||
ui.Text(c, fmt.Sprintf("%d unread", r.Unread())).Absolute().Top(8).Right(12)
|
||||
}
|
||||
@@ -127,7 +130,7 @@ var chatmsgListPages = []page{
|
||||
}},
|
||||
{ID: "202", Name: "MessageList", ZhName: "消息列表", Category: chatmsgCategory,
|
||||
Keys: "滚轮与方向键滚动;上翻后停止跟随;Tab 到回到最新按钮,Enter 回到底部。",
|
||||
Code: `r := mujica.MessageList(c, &list, len(ids), mujica.MessageListOptions{
|
||||
Code: `r := chat.MessageList(c, &list, len(ids), chat.MessageListOptions{
|
||||
ID: func(i int) string { return ids[i] },
|
||||
}, func(i int) { ui.Text(c, "Message "+ids[i]) })
|
||||
fmt.Println("unread", r.Unread())`,
|
||||
@@ -137,7 +140,7 @@ fmt.Println("unread", r.Unread())`,
|
||||
inputFlags(c, log, map[string]*bool{"Date separators": &d.dated}, "Date separators")
|
||||
chatmsgControls(c, log, d)
|
||||
ui.Row(c).Children(func() {
|
||||
if mujica.Button(c, "Load 100,000", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Load 100,000", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
for len(d.ids) < 100000 {
|
||||
d.add()
|
||||
}
|
||||
@@ -146,77 +149,77 @@ fmt.Println("unread", r.Unread())`,
|
||||
})
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
opts := mujica.MessageListOptions{ID: func(i int) string { return d.ids[i] }, Label: func(i int) string { return d.texts[i] }}
|
||||
opts := chat.MessageListOptions{ID: func(i int) string { return d.ids[i] }, Label: func(i int) string { return d.texts[i] }}
|
||||
if d.dated {
|
||||
opts.Date = d.day
|
||||
}
|
||||
ui.Column(c).Height(360).Border(1, mujica.Tokens(c).Border).Children(func() {
|
||||
r := mujica.MessageList(c, &d.list, len(d.ids), opts, func(i int) { d.row(c, i) })
|
||||
ui.Column(c).Height(360).Border(1, core.Tokens(c).Border).Children(func() {
|
||||
r := chat.MessageList(c, &d.list, len(d.ids), opts, func(i int) { d.row(c, i) })
|
||||
ui.Text(c, fmt.Sprintf("%d messages, %d unread, at end %v", len(d.ids), r.Unread(), d.list.AtEnd())).Padding(8)
|
||||
})
|
||||
})
|
||||
}},
|
||||
{ID: "203", Name: "MessageBubble", ZhName: "消息气泡", Category: chatmsgCategory,
|
||||
Keys: "气泡不获取焦点;内容中的链接与按钮按 Tab 顺序访问。",
|
||||
Code: `mujica.MessageBubble(c, mujica.MessageAssistant, mujica.MessageBubbleOptions{Name: "Herald"}, func() {
|
||||
Code: `chat.MessageBubble(c, chat.MessageAssistant, chat.MessageBubbleOptions{Name: "Herald"}, func() {
|
||||
ui.Text(c, "The ball begins at nine.")
|
||||
})`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
role mujica.MessageRole
|
||||
role chat.MessageRole
|
||||
avatar, named bool
|
||||
width float64
|
||||
}
|
||||
s := ui.Local(c.Root(), "203", func() state { return state{role: mujica.MessageAssistant, avatar: true, named: true, width: 360} })
|
||||
roles := []mujica.RadioItem[mujica.MessageRole]{{Value: mujica.MessageUser, Label: "User"}, {Value: mujica.MessageAssistant, Label: "Assistant"},
|
||||
{Value: mujica.MessageSystem, Label: "System"}, {Value: mujica.MessageError, Label: "Error"}}
|
||||
s := ui.Local(c.Root(), "203", func() state { return state{role: chat.MessageAssistant, avatar: true, named: true, width: 360} })
|
||||
roles := []input.RadioItem[chat.MessageRole]{{Value: chat.MessageUser, Label: "User"}, {Value: chat.MessageAssistant, Label: "Assistant"},
|
||||
{Value: chat.MessageSystem, Label: "System"}, {Value: chat.MessageError, Label: "Error"}}
|
||||
inputSection(c, "Parameters", func() {
|
||||
if mujica.RadioGroup(c, &s.role, roles, mujica.RadioGroupOptions{Horizontal: true, Label: "Role"}).Changed() {
|
||||
if input.RadioGroup(c, &s.role, roles, input.RadioGroupOptions{Horizontal: true, Label: "Role"}).Changed() {
|
||||
log(fmt.Sprintf("role %d", s.role))
|
||||
}
|
||||
inputFlags(c, log, map[string]*bool{"Avatar": &s.avatar, "Name": &s.named}, "Avatar", "Name")
|
||||
if mujica.NumberInput(c, &s.width, mujica.NumberInputOptions{Min: 160, Max: 640, Step: 40, Label: "MaxWidth"}).Changed() {
|
||||
if input.NumberInput(c, &s.width, input.NumberInputOptions{Min: 160, Max: 640, Step: 40, Label: "MaxWidth"}).Changed() {
|
||||
log(fmt.Sprintf("MaxWidth %v", s.width))
|
||||
}
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
opts := mujica.MessageBubbleOptions{MaxWidth: float32(s.width)}
|
||||
opts := chat.MessageBubbleOptions{MaxWidth: float32(s.width)}
|
||||
if s.named {
|
||||
opts.Name = "Herald"
|
||||
}
|
||||
if s.avatar {
|
||||
opts.Avatar = func() { mujica.Avatar(c, "Herald", mujica.AvatarOptions{}) }
|
||||
opts.Avatar = func() { display.Avatar(c, "Herald", display.AvatarOptions{}) }
|
||||
}
|
||||
mujica.MessageBubble(c, s.role, opts, func() { ui.Text(c, chatmsgReplies[0]+" "+chatmsgReplies[2]) })
|
||||
chat.MessageBubble(c, s.role, opts, func() { ui.Text(c, chatmsgReplies[0]+" "+chatmsgReplies[2]) })
|
||||
})
|
||||
inputSection(c, "States", func() {
|
||||
for _, r := range roles {
|
||||
mujica.MessageBubble(c, r.Value, mujica.MessageBubbleOptions{Name: r.Label}, func() { ui.Text(c, r.Label+" message") })
|
||||
chat.MessageBubble(c, r.Value, chat.MessageBubbleOptions{Name: r.Label}, func() { ui.Text(c, r.Label+" message") })
|
||||
}
|
||||
})
|
||||
}},
|
||||
{ID: "204", Name: "MessageHeader", ZhName: "消息头", Category: chatmsgCategory, Keys: "不获取焦点。",
|
||||
Code: `mujica.MessageHeader(c, mujica.MessageHeaderOptions{Model: "herald-2", Time: time.Now(), Status: mujica.MessageSent})`,
|
||||
Code: `chat.MessageHeader(c, chat.MessageHeaderOptions{Model: "herald-2", Time: time.Now(), Status: chat.MessageSent})`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
status := ui.Local(c.Root(), "204", func() mujica.MessageStatus { return mujica.MessageSent })
|
||||
items := []mujica.RadioItem[mujica.MessageStatus]{{Value: mujica.MessageNoStatus, Label: "None"}, {Value: mujica.MessageSending, Label: "Sending"},
|
||||
{Value: mujica.MessageStreaming, Label: "Streaming"}, {Value: mujica.MessageSent, Label: "Sent"}, {Value: mujica.MessageFailed, Label: "Failed"}}
|
||||
status := ui.Local(c.Root(), "204", func() chat.MessageStatus { return chat.MessageSent })
|
||||
items := []input.RadioItem[chat.MessageStatus]{{Value: chat.MessageNoStatus, Label: "None"}, {Value: chat.MessageSending, Label: "Sending"},
|
||||
{Value: chat.MessageStreaming, Label: "Streaming"}, {Value: chat.MessageSent, Label: "Sent"}, {Value: chat.MessageFailed, Label: "Failed"}}
|
||||
inputSection(c, "Parameters", func() {
|
||||
if mujica.RadioGroup(c, status, items, mujica.RadioGroupOptions{Horizontal: true, Label: "Status"}).Changed() {
|
||||
if input.RadioGroup(c, status, items, input.RadioGroupOptions{Horizontal: true, Label: "Status"}).Changed() {
|
||||
log(fmt.Sprintf("status %d", *status))
|
||||
}
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
mujica.MessageHeader(c, mujica.MessageHeaderOptions{Model: "herald-2", Time: time.Now(), Status: *status})
|
||||
chat.MessageHeader(c, chat.MessageHeaderOptions{Model: "herald-2", Time: time.Now(), Status: *status})
|
||||
})
|
||||
inputSection(c, "States", func() {
|
||||
for _, it := range items[1:] {
|
||||
mujica.MessageHeader(c, mujica.MessageHeaderOptions{Model: "herald-2", Time: time.Now(), Status: it.Value})
|
||||
chat.MessageHeader(c, chat.MessageHeaderOptions{Model: "herald-2", Time: time.Now(), Status: it.Value})
|
||||
}
|
||||
})
|
||||
}},
|
||||
{ID: "205", Name: "StreamingText", ZhName: "流式文本", Category: chatmsgCategory, Keys: "不获取焦点;减少动画时直接显示全部文本。",
|
||||
Code: `mujica.StreamingText(c, reply, mujica.StreamingTextOptions{Streaming: true, Smooth: true})`,
|
||||
Code: `chat.StreamingText(c, reply, chat.StreamingTextOptions{Streaming: true, Smooth: true})`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
text string
|
||||
@@ -228,11 +231,11 @@ fmt.Println("unread", r.Unread())`,
|
||||
const full = "By order of the Queen, the ball begins at nine. Guests arrive by the east door, and carriages wait in the lower court until midnight."
|
||||
inputSection(c, "Parameters", func() {
|
||||
inputFlags(c, log, map[string]*bool{"Smooth": &s.smooth, "Streaming cursor": &s.streaming, "Stream": &s.auto}, "Smooth", "Streaming cursor", "Stream")
|
||||
if mujica.NumberInput(c, &s.rate, mujica.NumberInputOptions{Min: 10, Max: 400, Step: 10, Label: "Rate"}).Changed() {
|
||||
if input.NumberInput(c, &s.rate, input.NumberInputOptions{Min: 10, Max: 400, Step: 10, Label: "Rate"}).Changed() {
|
||||
log(fmt.Sprintf("Rate %v", s.rate))
|
||||
}
|
||||
ui.Row(c).Children(func() {
|
||||
if mujica.Button(c, "Restart", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Restart", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.text = ""
|
||||
log("stream restarted")
|
||||
}
|
||||
@@ -255,7 +258,7 @@ fmt.Println("unread", r.Unread())`,
|
||||
c.After(150 * time.Millisecond)
|
||||
}
|
||||
inputSection(c, "Live", func() {
|
||||
mujica.StreamingText(c, s.text, mujica.StreamingTextOptions{Streaming: s.streaming && len(s.text) < len(full), Smooth: s.smooth, Rate: s.rate})
|
||||
chat.StreamingText(c, s.text, chat.StreamingTextOptions{Streaming: s.streaming && len(s.text) < len(full), Smooth: s.smooth, Rate: s.rate})
|
||||
})
|
||||
}},
|
||||
}
|
||||
@@ -6,12 +6,14 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chat"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
var chatmsgActionPages = []page{
|
||||
{ID: "211", Name: "AudioMessage", ZhName: "语音消息", Category: chatmsgCategory, Keys: "Tab 到播放按钮与波形;波形上 Left/Right 跳转,Home、End 到首尾。",
|
||||
Code: `r := mujica.AudioMessage(c, mujica.AudioMessageOptions{Duration: time.Minute, Position: pos})
|
||||
Code: `r := chat.AudioMessage(c, chat.AudioMessageOptions{Duration: time.Minute, Position: pos})
|
||||
if d, ok := r.Seek(); ok {
|
||||
pos = d
|
||||
}`,
|
||||
@@ -34,7 +36,7 @@ if d, ok := r.Seek(); ok {
|
||||
}
|
||||
s.at = c.Now()
|
||||
inputSection(c, "Live", func() {
|
||||
r := mujica.AudioMessage(c, mujica.AudioMessageOptions{Duration: length, Position: s.pos, Playing: s.playing, Waveform: chatmsgWave})
|
||||
r := chat.AudioMessage(c, chat.AudioMessageOptions{Duration: length, Position: s.pos, Playing: s.playing, Waveform: chatmsgWave})
|
||||
if r.Toggled() {
|
||||
s.playing = !s.playing
|
||||
if s.pos >= length {
|
||||
@@ -49,13 +51,13 @@ if d, ok := r.Seek(); ok {
|
||||
})
|
||||
inputSection(c, "States", func() {
|
||||
ui.Column(c).Disabled(true).Children(func() {
|
||||
mujica.AudioMessage(c, mujica.AudioMessageOptions{Duration: length, Waveform: chatmsgWave})
|
||||
chat.AudioMessage(c, chat.AudioMessageOptions{Duration: length, Waveform: chatmsgWave})
|
||||
})
|
||||
})
|
||||
}},
|
||||
{ID: "212", Name: "LinkPreviewCard", ZhName: "链接预览卡片", Category: chatmsgCategory, Keys: "Tab 聚焦卡片,Enter 或 Space 打开。",
|
||||
Code: `p := mujica.LinkPreview{URL: "https://example.com", Title: "The Palace", Site: "example.com"}
|
||||
if mujica.LinkPreviewCard(c, p).Clicked() {
|
||||
Code: `p := chat.LinkPreview{URL: "https://example.com", Title: "The Palace", Site: "example.com"}
|
||||
if chat.LinkPreviewCard(c, p).Clicked() {
|
||||
fmt.Println("open", p.URL)
|
||||
}`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
@@ -63,66 +65,66 @@ if mujica.LinkPreviewCard(c, p).Clicked() {
|
||||
inputSection(c, "Parameters", func() {
|
||||
inputFlags(c, log, map[string]*bool{"Thumbnail": thumb}, "Thumbnail")
|
||||
})
|
||||
p := mujica.LinkPreview{URL: "https://example.com/versailles", Title: "The Palace of Versailles", Site: "example.com",
|
||||
p := chat.LinkPreview{URL: "https://example.com/versailles", Title: "The Palace of Versailles", Site: "example.com",
|
||||
Description: "A royal château in the Île-de-France region, seat of the French court from 1682 until the Revolution."}
|
||||
if *thumb {
|
||||
p.Thumbnail = chatmsgPicture("#AD9164")
|
||||
}
|
||||
inputSection(c, "Live", func() {
|
||||
if mujica.LinkPreviewCard(c, p).Clicked() {
|
||||
if chat.LinkPreviewCard(c, p).Clicked() {
|
||||
log("open " + p.URL)
|
||||
}
|
||||
if mujica.LinkPreviewCard(c, mujica.LinkPreview{URL: "https://example.com/hall", Title: "Hall of Mirrors"}).Clicked() {
|
||||
if chat.LinkPreviewCard(c, chat.LinkPreview{URL: "https://example.com/hall", Title: "Hall of Mirrors"}).Clicked() {
|
||||
log("open https://example.com/hall")
|
||||
}
|
||||
})
|
||||
}},
|
||||
{ID: "213", Name: "QuoteReply", ZhName: "引用回复", Category: chatmsgCategory, Keys: "Tab 聚焦,Enter 或 Space 跳转。",
|
||||
Code: `if mujica.QuoteReply(c, "Marie", "Let them eat cake.").Clicked() {
|
||||
Code: `if chat.QuoteReply(c, "Marie", "Let them eat cake.").Clicked() {
|
||||
fmt.Println("jump to the quoted message")
|
||||
}`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
inputSection(c, "Live", func() {
|
||||
for _, q := range [][2]string{{"Marie", "Let them eat cake."}, {"Herald", chatmsgMarkdown}} {
|
||||
if mujica.QuoteReply(c, q[0], q[1]).Clicked() {
|
||||
if chat.QuoteReply(c, q[0], q[1]).Clicked() {
|
||||
log("jump to " + q[0] + ": " + chatmsgSnippet(q[1], 24))
|
||||
}
|
||||
}
|
||||
})
|
||||
}},
|
||||
{ID: "214", Name: "MessageActions", ZhName: "消息操作", Category: chatmsgCategory, Keys: "Tab 进入工具栏,Left/Right 移动,Enter 或 Space 触发。",
|
||||
Code: `if a, ok := mujica.MessageActions(c, mujica.MessageAssistant, mujica.MessageActionsOptions{Feedback: &rating}).Action(); ok {
|
||||
Code: `if a, ok := chat.MessageActions(c, chat.MessageAssistant, chat.MessageActionsOptions{Feedback: &rating}).Action(); ok {
|
||||
fmt.Println("action", a)
|
||||
}`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
role mujica.MessageRole
|
||||
rating mujica.MessageFeedback
|
||||
role chat.MessageRole
|
||||
rating chat.MessageFeedback
|
||||
rated bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "214", func() state { return state{role: mujica.MessageAssistant, rated: true} })
|
||||
names := map[mujica.MessageAction]string{mujica.ActionCopy: "copy", mujica.ActionRegenerate: "regenerate", mujica.ActionEdit: "edit",
|
||||
mujica.ActionLike: "like", mujica.ActionDislike: "dislike", mujica.ActionSpeak: "speak", mujica.ActionShare: "share"}
|
||||
s := ui.Local(c.Root(), "214", func() state { return state{role: chat.MessageAssistant, rated: true} })
|
||||
names := map[chat.MessageAction]string{chat.ActionCopy: "copy", chat.ActionRegenerate: "regenerate", chat.ActionEdit: "edit",
|
||||
chat.ActionLike: "like", chat.ActionDislike: "dislike", chat.ActionSpeak: "speak", chat.ActionShare: "share"}
|
||||
inputSection(c, "Parameters", func() {
|
||||
roles := []mujica.RadioItem[mujica.MessageRole]{{Value: mujica.MessageUser, Label: "User"}, {Value: mujica.MessageAssistant, Label: "Assistant"},
|
||||
{Value: mujica.MessageSystem, Label: "System"}, {Value: mujica.MessageError, Label: "Error"}}
|
||||
if mujica.RadioGroup(c, &s.role, roles, mujica.RadioGroupOptions{Horizontal: true, Label: "Role"}).Changed() {
|
||||
roles := []input.RadioItem[chat.MessageRole]{{Value: chat.MessageUser, Label: "User"}, {Value: chat.MessageAssistant, Label: "Assistant"},
|
||||
{Value: chat.MessageSystem, Label: "System"}, {Value: chat.MessageError, Label: "Error"}}
|
||||
if input.RadioGroup(c, &s.role, roles, input.RadioGroupOptions{Horizontal: true, Label: "Role"}).Changed() {
|
||||
log(fmt.Sprintf("role %d", s.role))
|
||||
}
|
||||
inputFlags(c, log, map[string]*bool{"Rating": &s.rated}, "Rating")
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
opts := mujica.MessageActionsOptions{}
|
||||
opts := chat.MessageActionsOptions{}
|
||||
if s.rated {
|
||||
opts.Feedback = &s.rating
|
||||
}
|
||||
if a, ok := mujica.MessageActions(c, s.role, opts).Action(); ok {
|
||||
if a, ok := chat.MessageActions(c, s.role, opts).Action(); ok {
|
||||
log(fmt.Sprintf("action %s, rating %d", names[a], s.rating))
|
||||
}
|
||||
})
|
||||
}},
|
||||
{ID: "215", Name: "MessageEditor", ZhName: "消息编辑器", Category: chatmsgCategory, Keys: "Cmd/Ctrl+Enter 保存,Escape 取消。",
|
||||
Code: `if mujica.MessageEditor(c, &draft, mujica.MessageEditorOptions{Original: "Hello"}).Saved() {
|
||||
Code: `if chat.MessageEditor(c, &draft, chat.MessageEditorOptions{Original: "Hello"}).Saved() {
|
||||
fmt.Println("new branch from", draft)
|
||||
}`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
@@ -137,19 +139,19 @@ if mujica.LinkPreviewCard(c, p).Clicked() {
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
if !s.editing {
|
||||
mujica.MessageBubble(c, mujica.MessageUser, mujica.MessageBubbleOptions{}, func() { ui.Text(c, s.branches[s.index]) })
|
||||
chat.MessageBubble(c, chat.MessageUser, chat.MessageBubbleOptions{}, func() { ui.Text(c, s.branches[s.index]) })
|
||||
ui.Row(c).Gap(8).Justify(ui.End).Children(func() {
|
||||
if mujica.BranchNavigator(c, &s.index, len(s.branches)).Changed() {
|
||||
if chat.BranchNavigator(c, &s.index, len(s.branches)).Changed() {
|
||||
log(fmt.Sprintf("branch %d", s.index+1))
|
||||
}
|
||||
if mujica.Button(c, "Edit", mujica.ButtonOptions{Variant: mujica.Ghost}).Clicked() {
|
||||
if input.Button(c, "Edit", input.ButtonOptions{Variant: input.Ghost}).Clicked() {
|
||||
s.editing, s.draft = true, s.branches[s.index]
|
||||
log("editing")
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
r := mujica.MessageEditor(c, &s.draft, mujica.MessageEditorOptions{Original: s.branches[s.index]})
|
||||
r := chat.MessageEditor(c, &s.draft, chat.MessageEditorOptions{Original: s.branches[s.index]})
|
||||
if r.Saved() {
|
||||
s.branches = append(s.branches, s.draft)
|
||||
s.index, s.editing = len(s.branches)-1, false
|
||||
@@ -162,7 +164,7 @@ if mujica.LinkPreviewCard(c, p).Clicked() {
|
||||
})
|
||||
}},
|
||||
{ID: "216", Name: "BranchNavigator", ZhName: "分支切换", Category: chatmsgCategory, Keys: "Tab 聚焦,Left/Right 切换,Home、End 到首末。",
|
||||
Code: `if mujica.BranchNavigator(c, &branch, 3).Changed() {
|
||||
Code: `if chat.BranchNavigator(c, &branch, 3).Changed() {
|
||||
fmt.Println("show branch", branch)
|
||||
}`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
@@ -173,7 +175,7 @@ if mujica.LinkPreviewCard(c, p).Clicked() {
|
||||
}
|
||||
s := ui.Local(c.Root(), "216", func() state { return state{index: 1, count: 3} })
|
||||
inputSection(c, "Parameters", func() {
|
||||
if mujica.NumberInput(c, &s.count, mujica.NumberInputOptions{Min: 1, Max: 9, StepperOnly: true, Label: "Branches"}).Changed() {
|
||||
if input.NumberInput(c, &s.count, input.NumberInputOptions{Min: 1, Max: 9, StepperOnly: true, Label: "Branches"}).Changed() {
|
||||
s.index = min(s.index, int(s.count)-1)
|
||||
log(fmt.Sprintf("%v branches", s.count))
|
||||
}
|
||||
@@ -181,39 +183,39 @@ if mujica.LinkPreviewCard(c, p).Clicked() {
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
ui.Row(c).Disabled(s.disabled).Children(func() {
|
||||
if mujica.BranchNavigator(c, &s.index, int(s.count)).Changed() {
|
||||
if chat.BranchNavigator(c, &s.index, int(s.count)).Changed() {
|
||||
log(fmt.Sprintf("branch %d of %v", s.index+1, s.count))
|
||||
}
|
||||
})
|
||||
})
|
||||
}},
|
||||
{ID: "217", Name: "ErrorMessage", ZhName: "错误消息", Category: chatmsgCategory, Keys: "Tab 到错误文本(可选择复制)与重试按钮。",
|
||||
Code: `opts := mujica.ErrorMessageOptions{RetryAt: time.Now().Add(30 * time.Second)}
|
||||
if mujica.ErrorMessage(c, "429 Too Many Requests", opts).Retried() {
|
||||
Code: `opts := chat.ErrorMessageOptions{RetryAt: time.Now().Add(30 * time.Second)}
|
||||
if chat.ErrorMessage(c, "429 Too Many Requests", opts).Retried() {
|
||||
fmt.Println("send again")
|
||||
}`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
until := ui.Local(c.Root(), "217", func() time.Time { return time.Time{} })
|
||||
inputSection(c, "Parameters", func() {
|
||||
ui.Row(c).Children(func() {
|
||||
if mujica.Button(c, "Rate limit for 10 s", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Rate limit for 10 s", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
*until = c.Now().Add(10 * time.Second)
|
||||
log("rate limited for 10 s")
|
||||
}
|
||||
})
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
opts := mujica.ErrorMessageOptions{RetryAt: *until}
|
||||
if mujica.ErrorMessage(c, `429 Too Many Requests: {"type":"rate_limit_error","message":"Number of requests exceeded"}`, opts).Retried() {
|
||||
opts := chat.ErrorMessageOptions{RetryAt: *until}
|
||||
if chat.ErrorMessage(c, `429 Too Many Requests: {"type":"rate_limit_error","message":"Number of requests exceeded"}`, opts).Retried() {
|
||||
log("retry")
|
||||
}
|
||||
if mujica.ErrorMessage(c, "dial tcp 10.0.0.7:443: connection refused", mujica.ErrorMessageOptions{Title: "Could not reach the server"}).Retried() {
|
||||
if chat.ErrorMessage(c, "dial tcp 10.0.0.7:443: connection refused", chat.ErrorMessageOptions{Title: "Could not reach the server"}).Retried() {
|
||||
log("retry connection")
|
||||
}
|
||||
})
|
||||
}},
|
||||
{ID: "218", Name: "ThinkingBlock", ZhName: "思考过程", Category: chatmsgCategory, Keys: "Tab 聚焦标题,Enter 或 Space 展开与折叠。",
|
||||
Code: `mujica.ThinkingBlock(c, &open, mujica.ThinkingBlockOptions{Elapsed: 12 * time.Second}, func() {
|
||||
Code: `chat.ThinkingBlock(c, &open, chat.ThinkingBlockOptions{Elapsed: 12 * time.Second}, func() {
|
||||
ui.Text(c, "Weigh the guest list.")
|
||||
})`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
@@ -229,7 +231,7 @@ if mujica.ErrorMessage(c, "429 Too Many Requests", opts).Retried() {
|
||||
label = "Finish thinking"
|
||||
}
|
||||
ui.Row(c).Children(func() {
|
||||
if mujica.Button(c, label, mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, label, input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
if s.thinking {
|
||||
s.elapsed = c.Now().Sub(s.started)
|
||||
} else {
|
||||
@@ -241,8 +243,8 @@ if mujica.ErrorMessage(c, "429 Too Many Requests", opts).Retried() {
|
||||
})
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
opts := mujica.ThinkingBlockOptions{Thinking: s.thinking, Started: s.started, Elapsed: s.elapsed}
|
||||
if mujica.ThinkingBlock(c, &s.open, opts, func() {
|
||||
opts := chat.ThinkingBlockOptions{Thinking: s.thinking, Started: s.started, Elapsed: s.elapsed}
|
||||
if chat.ThinkingBlock(c, &s.open, opts, func() {
|
||||
ui.Text(c, "Forty guests, thirty-six seats. The envoys outrank the counts; four counts dine in the antechamber.")
|
||||
}).Changed() {
|
||||
log(fmt.Sprintf("open %v", s.open))
|
||||
@@ -251,7 +253,7 @@ if mujica.ErrorMessage(c, "429 Too Many Requests", opts).Retried() {
|
||||
}},
|
||||
{ID: "219", Name: "ScrollToBottomButton", ZhName: "回到底部按钮", Category: chatmsgCategory, Keys: "Tab 聚焦,Enter 或 Space 回到底部。",
|
||||
Code: `ui.Box(c).Fill().Children(func() {
|
||||
if mujica.ScrollToBottomButton(c, !list.AtEnd(), 3).Clicked() {
|
||||
if chat.ScrollToBottomButton(c, !list.AtEnd(), 3).Clicked() {
|
||||
list.ScrollToEnd()
|
||||
}
|
||||
})`,
|
||||
@@ -263,13 +265,13 @@ if mujica.ErrorMessage(c, "429 Too Many Requests", opts).Retried() {
|
||||
s := ui.Local(c.Root(), "219", func() state { return state{away: true, unread: 3} })
|
||||
inputSection(c, "Parameters", func() {
|
||||
inputFlags(c, log, map[string]*bool{"Away from bottom": &s.away}, "Away from bottom")
|
||||
if mujica.NumberInput(c, &s.unread, mujica.NumberInputOptions{Min: 0, Max: 99, Label: "Unread"}).Changed() {
|
||||
if input.NumberInput(c, &s.unread, input.NumberInputOptions{Min: 0, Max: 99, Label: "Unread"}).Changed() {
|
||||
log(fmt.Sprintf("unread %v", s.unread))
|
||||
}
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
ui.Box(c).Height(120).Background(mujica.Tokens(c).Surface).Border(1, mujica.Tokens(c).Border).Children(func() {
|
||||
if mujica.ScrollToBottomButton(c, s.away, int(s.unread)).Clicked() {
|
||||
ui.Box(c).Height(120).Background(core.Tokens(c).Surface).Border(1, core.Tokens(c).Border).Children(func() {
|
||||
if chat.ScrollToBottomButton(c, s.away, int(s.unread)).Clicked() {
|
||||
s.away, s.unread = false, 0
|
||||
log("back to the bottom")
|
||||
}
|
||||
@@ -277,7 +279,7 @@ if mujica.ErrorMessage(c, "429 Too Many Requests", opts).Retried() {
|
||||
})
|
||||
}},
|
||||
{ID: "220", Name: "TypingIndicator", ZhName: "正在输入", Category: chatmsgCategory, Keys: "不获取焦点;减少动画时显示静态文字。",
|
||||
Code: `mujica.TypingIndicator(c, "Herald")`,
|
||||
Code: `chat.TypingIndicator(c, "Herald")`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
typing := ui.Local(c.Root(), "220", func() bool { return true })
|
||||
inputSection(c, "Parameters", func() {
|
||||
@@ -285,7 +287,7 @@ if mujica.ErrorMessage(c, "429 Too Many Requests", opts).Retried() {
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
if *typing {
|
||||
mujica.TypingIndicator(c, "Herald")
|
||||
chat.TypingIndicator(c, "Herald")
|
||||
}
|
||||
})
|
||||
}},
|
||||
|
||||
@@ -8,7 +8,8 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chat"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
const chatmsgMarkdown = "## The Decree\n\nBy order of **the Queen**, *all* guests use `the east door`. See [the protocol](https://example.com/protocol).\n\n" +
|
||||
@@ -24,7 +25,7 @@ var chatmsgColors = []string{"#7D2034", "#AD9164", "#355E80", "#326247", "#80571
|
||||
|
||||
var chatmsgBlockPages = []page{
|
||||
{ID: "206", Name: "MarkdownView", ZhName: "Markdown 视图", Category: chatmsgCategory, Keys: "Tab 到链接,Enter 激活;代码块与表格按钮按 Tab 顺序访问。",
|
||||
Code: `if url, ok := mujica.MarkdownView(c, "# Decree\n\nSee **the guide** at [the court](https://example.com).").Link(); ok {
|
||||
Code: `if url, ok := chat.MarkdownView(c, "# Decree\n\nSee **the guide** at [the court](https://example.com).").Link(); ok {
|
||||
fmt.Println("open", url)
|
||||
}`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
@@ -36,12 +37,12 @@ var chatmsgBlockPages = []page{
|
||||
s := ui.Local(c.Root(), "206", func() state { return state{src: chatmsgMarkdown} })
|
||||
inputSection(c, "Parameters", func() {
|
||||
ui.Row(c).Children(func() {
|
||||
if mujica.Button(c, "Stream it again", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Stream it again", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.src, s.stream = "", true
|
||||
log("streaming started")
|
||||
}
|
||||
})
|
||||
mujica.TextArea(c, &s.src, mujica.TextAreaOptions{Label: "Markdown source", MaxHeight: 160})
|
||||
input.TextArea(c, &s.src, input.TextAreaOptions{Label: "Markdown source", MaxHeight: 160})
|
||||
})
|
||||
if s.stream && len(s.src) < len(chatmsgMarkdown) && c.Now().Sub(s.at) > 40*time.Millisecond {
|
||||
s.src = chatmsgMarkdown[:min(len(chatmsgMarkdown), len(s.src)+6)]
|
||||
@@ -55,13 +56,13 @@ var chatmsgBlockPages = []page{
|
||||
c.After(40 * time.Millisecond)
|
||||
}
|
||||
inputSection(c, "Live", func() {
|
||||
if url, ok := mujica.MarkdownView(c, s.src).Link(); ok {
|
||||
if url, ok := chat.MarkdownView(c, s.src).Link(); ok {
|
||||
log("link " + url)
|
||||
}
|
||||
})
|
||||
}},
|
||||
{ID: "207", Name: "CodeBlock", ZhName: "代码块", Category: chatmsgCategory, Keys: "Tab 到换行、复制、折叠按钮;Space 或 Enter 触发。",
|
||||
Code: `mujica.CodeBlock(c, "func bow() {}", mujica.CodeBlockOptions{Language: "go", LineNumbers: true})`,
|
||||
Code: `chat.CodeBlock(c, "func bow() {}", chat.CodeBlockOptions{Language: "go", LineNumbers: true})`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
lang string
|
||||
@@ -74,25 +75,25 @@ var chatmsgBlockPages = []page{
|
||||
"sql": "SELECT name, seat FROM guests WHERE rank > 2 ORDER BY seat LIMIT 10; -- the envoys",
|
||||
}
|
||||
inputSection(c, "Parameters", func() {
|
||||
langs := []mujica.RadioItem[string]{{Value: "go", Label: "Go"}, {Value: "python", Label: "Python"}, {Value: "sql", Label: "SQL"}}
|
||||
if mujica.RadioGroup(c, &s.lang, langs, mujica.RadioGroupOptions{Horizontal: true, Label: "Language"}).Changed() {
|
||||
langs := []input.RadioItem[string]{{Value: "go", Label: "Go"}, {Value: "python", Label: "Python"}, {Value: "sql", Label: "SQL"}}
|
||||
if input.RadioGroup(c, &s.lang, langs, input.RadioGroupOptions{Horizontal: true, Label: "Language"}).Changed() {
|
||||
log("language " + s.lang)
|
||||
}
|
||||
inputFlags(c, log, map[string]*bool{"LineNumbers": &s.numbers, "Wrap at first": &s.wrap}, "LineNumbers", "Wrap at first")
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
ui.Column(c).Key(s.lang + fmt.Sprint(s.wrap)).Children(func() {
|
||||
if mujica.CodeBlock(c, samples[s.lang], mujica.CodeBlockOptions{Language: s.lang, LineNumbers: s.numbers, Wrap: s.wrap}).Copied() {
|
||||
if chat.CodeBlock(c, samples[s.lang], chat.CodeBlockOptions{Language: s.lang, LineNumbers: s.numbers, Wrap: s.wrap}).Copied() {
|
||||
log("code copied")
|
||||
}
|
||||
})
|
||||
})
|
||||
inputSection(c, "States", func() {
|
||||
mujica.CodeBlock(c, samples["go"], mujica.CodeBlockOptions{Language: "go", Collapsed: true})
|
||||
chat.CodeBlock(c, samples["go"], chat.CodeBlockOptions{Language: "go", Collapsed: true})
|
||||
})
|
||||
}},
|
||||
{ID: "208", Name: "TableBlock", ZhName: "表格块", Category: chatmsgCategory, Keys: "Tab 到复制按钮,Enter 或 Space 复制为 TSV。",
|
||||
Code: `if mujica.TableBlock(c, []string{"Guest", "Seat"}, [][]string{{"Marie", "1"}}, mujica.TableBlockOptions{}).Copied() {
|
||||
Code: `if chat.TableBlock(c, []string{"Guest", "Seat"}, [][]string{{"Marie", "1"}}, chat.TableBlockOptions{}).Copied() {
|
||||
fmt.Println("copied as TSV")
|
||||
}`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
@@ -110,14 +111,14 @@ var chatmsgBlockPages = []page{
|
||||
}
|
||||
inputSection(c, "Live", func() {
|
||||
ui.Column(c).MaxWidth(520).Children(func() {
|
||||
if mujica.TableBlock(c, header, rows, mujica.TableBlockOptions{Align: []ui.Align{ui.Start, ui.Start, ui.End}}).Copied() {
|
||||
if chat.TableBlock(c, header, rows, chat.TableBlockOptions{Align: []ui.Align{ui.Start, ui.Start, ui.End}}).Copied() {
|
||||
log("table copied as TSV")
|
||||
}
|
||||
})
|
||||
})
|
||||
}},
|
||||
{ID: "209", Name: "ImageGrid", ZhName: "图片网格", Category: chatmsgCategory, Keys: "Tab 聚焦图片,Enter 打开查看器;查看器内 Left/Right 切换,Escape 关闭。",
|
||||
Code: `if i, ok := mujica.ImageGrid(c, []mujica.GridImage{{Source: pic, Alt: "Portrait"}}).Retried(); ok {
|
||||
Code: `if i, ok := chat.ImageGrid(c, []chat.GridImage{{Source: pic, Alt: "Portrait"}}).Retried(); ok {
|
||||
fmt.Println("reload", i)
|
||||
}`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
@@ -127,30 +128,30 @@ var chatmsgBlockPages = []page{
|
||||
}
|
||||
s := ui.Local(c.Root(), "209", func() state { return state{n: 5} })
|
||||
inputSection(c, "Parameters", func() {
|
||||
if mujica.NumberInput(c, &s.n, mujica.NumberInputOptions{Min: 1, Max: 9, Label: "Images", StepperOnly: true}).Changed() {
|
||||
if input.NumberInput(c, &s.n, input.NumberInputOptions{Min: 1, Max: 9, Label: "Images", StepperOnly: true}).Changed() {
|
||||
log(fmt.Sprintf("%v images", s.n))
|
||||
}
|
||||
inputFlags(c, log, map[string]*bool{"Last loading": &s.loading, "Second failed": &s.fail}, "Last loading", "Second failed")
|
||||
})
|
||||
imgs := make([]mujica.GridImage, int(s.n))
|
||||
imgs := make([]chat.GridImage, int(s.n))
|
||||
for i := range imgs {
|
||||
imgs[i] = mujica.GridImage{Source: chatmsgPicture(chatmsgColors[i]), Alt: fmt.Sprintf("Portrait %d", i+1)}
|
||||
imgs[i] = chat.GridImage{Source: chatmsgPicture(chatmsgColors[i]), Alt: fmt.Sprintf("Portrait %d", i+1)}
|
||||
}
|
||||
if s.loading {
|
||||
imgs[len(imgs)-1] = mujica.GridImage{Alt: "Loading portrait", Loading: true}
|
||||
imgs[len(imgs)-1] = chat.GridImage{Alt: "Loading portrait", Loading: true}
|
||||
}
|
||||
if s.fail && len(imgs) > 1 {
|
||||
imgs[1] = mujica.GridImage{Alt: "Lost portrait", Err: errors.New("404")}
|
||||
imgs[1] = chat.GridImage{Alt: "Lost portrait", Err: errors.New("404")}
|
||||
}
|
||||
inputSection(c, "Live", func() {
|
||||
if i, ok := mujica.ImageGrid(c, imgs).Retried(); ok {
|
||||
if i, ok := chat.ImageGrid(c, imgs).Retried(); ok {
|
||||
s.fail = false
|
||||
log(fmt.Sprintf("retry image %d", i))
|
||||
}
|
||||
})
|
||||
}},
|
||||
{ID: "210", Name: "FileMessage", ZhName: "文件消息", Category: chatmsgCategory, Keys: "Tab 到文件与下载按钮,Enter 或 Space 触发。",
|
||||
Code: `r := mujica.FileMessage(c, mujica.FileMessageOptions{Name: "treaty.pdf", Size: 2621440})
|
||||
Code: `r := chat.FileMessage(c, chat.FileMessageOptions{Name: "treaty.pdf", Size: 2621440})
|
||||
if r.Downloaded() {
|
||||
fmt.Println("save treaty.pdf")
|
||||
}`,
|
||||
@@ -161,9 +162,9 @@ if r.Downloaded() {
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
ui.Column(c).Gap(8).Disabled(*disabled).Children(func() {
|
||||
for _, f := range []mujica.FileMessageOptions{{Name: "treaty.pdf", Size: 2621440}, {Name: "portrait.png", Size: 812000},
|
||||
for _, f := range []chat.FileMessageOptions{{Name: "treaty.pdf", Size: 2621440}, {Name: "portrait.png", Size: 812000},
|
||||
{Name: "ledger.zip", Size: 73400320}, {Name: "decree.go", Size: 1840}, {Name: "notes", Size: 12}} {
|
||||
r := mujica.FileMessage(c, f)
|
||||
r := chat.FileMessage(c, f)
|
||||
if r.Opened() {
|
||||
log("open " + f.Name)
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
)
|
||||
|
||||
// chatmsgDemoTester runs the demo of page id and returns its tester and event log.
|
||||
func chatmsgDemoTester(t *testing.T, id string, mode mujica.Mode) (*ui.Tester, *[]string) {
|
||||
func chatmsgDemoTester(t *testing.T, id string, mode core.Mode) (*ui.Tester, *[]string) {
|
||||
t.Helper()
|
||||
i := slices.IndexFunc(chatmsgPages, func(p page) bool { return p.ID == id })
|
||||
if i < 0 {
|
||||
@@ -21,7 +21,7 @@ func chatmsgDemoTester(t *testing.T, id string, mode mujica.Mode) (*ui.Tester, *
|
||||
}
|
||||
events := &[]string{}
|
||||
tt := ui.NewTester(func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{Mode: mode})
|
||||
core.Use(c, core.Settings{Mode: mode})
|
||||
ui.Scroll(c).Fill().Padding(24).Gap(16).Children(func() {
|
||||
chatmsgPages[i].Demo(c, func(s string) { *events = append(*events, s) })
|
||||
})
|
||||
@@ -37,7 +37,7 @@ func TestChatmsgDemosRender(t *testing.T) {
|
||||
}
|
||||
dir := os.Getenv("CHATMSG_DEMO_DIR")
|
||||
for _, p := range chatmsgPages {
|
||||
for _, m := range []mujica.Mode{mujica.Light, mujica.Dark} {
|
||||
for _, m := range []core.Mode{core.Light, core.Dark} {
|
||||
tt, _ := chatmsgDemoTester(t, p.ID, m)
|
||||
if len(tt.Texts()) == 0 {
|
||||
t.Errorf("%s: demo shows nothing", p.ID)
|
||||
@@ -61,7 +61,7 @@ func TestChatmsgDemosLogEvents(t *testing.T) {
|
||||
for id, click := range map[string]string{
|
||||
"202": "Add message", "207": "Copy code", "214": "Regenerate", "216": "Next branch", "217": "Retry",
|
||||
} {
|
||||
tt, events := chatmsgDemoTester(t, id, mujica.Light)
|
||||
tt, events := chatmsgDemoTester(t, id, core.Light)
|
||||
if err := tt.Click(click); err != nil {
|
||||
t.Fatalf("%s: %v", id, err)
|
||||
}
|
||||
|
||||
@@ -4,23 +4,23 @@ const chatsessionCategory = "Chat Sessions"
|
||||
|
||||
var chatsessionPages = []page{
|
||||
{ID: "241", Name: "ConversationList", ZhName: "会话列表", Category: chatsessionCategory, Demo: chatDemoConversationList,
|
||||
Keys: "Up/Down choose a conversation, skipping group headers; typing jumps; Enter opens.", Code: `var state mujica.ConversationListState
|
||||
convs := []mujica.ChatConversation{
|
||||
Keys: "Up/Down choose a conversation, skipping group headers; typing jumps; Enter opens.", Code: `var state chat.ConversationListState
|
||||
convs := []chat.ChatConversation{
|
||||
{ID: "1", Title: "Court etiquette", Updated: time.Now()},
|
||||
{ID: "2", Title: "Royal letters", Updated: time.Now().AddDate(0, 0, -30), Pinned: true},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.ConversationList(c, &state, convs, mujica.ConversationListOptions{Label: "History"}, nil).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if chat.ConversationList(c, &state, convs, chat.ConversationListOptions{Label: "History"}, nil).Changed() {
|
||||
log.Println("open", state.Selected)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "242", Name: "ConversationItem", ZhName: "会话条目", Category: chatsessionCategory, Demo: chatDemoConversationItem,
|
||||
Keys: "Enter opens; F2 renames in place (Enter keeps, Escape cancels); Delete asks to delete; the ellipsis opens the menu.", Code: `conv := mujica.ChatConversation{ID: "1", Title: "Court etiquette", Updated: time.Now()}
|
||||
Keys: "Enter opens; F2 renames in place (Enter keeps, Escape cancels); Delete asks to delete; the ellipsis opens the menu.", Code: `conv := chat.ChatConversation{ID: "1", Title: "Court etiquette", Updated: time.Now()}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
v := mujica.ConversationItem(c, &conv, mujica.ConversationItemOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
v := chat.ConversationItem(c, &conv, chat.ConversationItemOptions{})
|
||||
if v.Deleted {
|
||||
log.Println("delete", conv.ID)
|
||||
}
|
||||
@@ -28,43 +28,43 @@ view := func(c *ui.Context) {
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "243", Name: "ConversationSearch", ZhName: "会话搜索", Category: chatsessionCategory, Demo: chatDemoConversationSearch,
|
||||
Keys: "Type to search; Down moves into the results; Up/Down move; Enter opens; Escape clears.", Code: `query := "seat"
|
||||
hits := []mujica.ConversationSearchHit{{ID: "1", Title: "Ballroom seating plan"}}
|
||||
hits := []chat.ConversationSearchHit{{ID: "1", Title: "Ballroom seating plan"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
v := mujica.ConversationSearch(c, &query, hits, mujica.ConversationSearchOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
v := chat.ConversationSearch(c, &query, hits, chat.ConversationSearchOptions{})
|
||||
if v.QueryChanged {
|
||||
log.Println("search", query)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "244", Name: "ProjectList", ZhName: "项目列表", Category: chatsessionCategory, Demo: chatDemoProjectList,
|
||||
Keys: "Up/Down move through folders, projects and conversations; Right/Left show or hide a project's conversations; the ellipsis menu moves a conversation.", Code: `var state mujica.ProjectListState
|
||||
projects := []mujica.ChatProject{{ID: "p1", Name: "Coronation", Folder: "Court"}}
|
||||
convs := func(id string) []mujica.ChatConversation {
|
||||
return []mujica.ChatConversation{{ID: "1", Title: "Menu"}}
|
||||
Keys: "Up/Down move through folders, projects and conversations; Right/Left show or hide a project's conversations; the ellipsis menu moves a conversation.", Code: `var state chat.ProjectListState
|
||||
projects := []chat.ChatProject{{ID: "p1", Name: "Coronation", Folder: "Court"}}
|
||||
convs := func(id string) []chat.ChatConversation {
|
||||
return []chat.ChatConversation{{ID: "1", Title: "Menu"}}
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if v := mujica.ProjectList(c, &state, projects, convs, mujica.ProjectListOptions{}); v.Moved {
|
||||
core.Use(c, core.Settings{})
|
||||
if v := chat.ProjectList(c, &state, projects, convs, chat.ProjectListOptions{}); v.Moved {
|
||||
log.Println("move", v.MoveID, "to", v.MoveTo)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "245", Name: "ProjectKnowledgePanel", ZhName: "项目知识面板", Category: chatsessionCategory, Demo: chatDemoProjectKnowledgePanel,
|
||||
Keys: "Tab reaches Add files and the file list; Up/Down move between remove buttons; Enter removes.", Code: `files := []mujica.KnowledgeFile{{ID: "1", Name: "etiquette.pdf", Tokens: 12000}}
|
||||
Keys: "Tab reaches Add files and the file list; Up/Down move between remove buttons; Enter removes.", Code: `files := []chat.KnowledgeFile{{ID: "1", Name: "etiquette.pdf", Tokens: 12000}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
v := mujica.ProjectKnowledgePanel(c, files, mujica.ProjectKnowledgePanelOptions{Capacity: 200000})
|
||||
core.Use(c, core.Settings{})
|
||||
v := chat.ProjectKnowledgePanel(c, files, chat.ProjectKnowledgePanelOptions{Capacity: 200000})
|
||||
if v.Add {
|
||||
log.Println("pick files")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "246", Name: "WelcomeScreen", ZhName: "欢迎页", Category: chatsessionCategory, Demo: chatDemoWelcomeScreen,
|
||||
Keys: "Tab moves through the cards' examples and the prompt slot; Enter picks.", Code: `cards := []mujica.CapabilityCard{{Title: "Write", Examples: []string{"Draft a decree"}}}
|
||||
Keys: "Tab moves through the cards' examples and the prompt slot; Enter picks.", Code: `cards := []chat.CapabilityCard{{Title: "Write", Examples: []string{"Draft a decree"}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if v := mujica.WelcomeScreen(c, mujica.WelcomeScreenOptions{Greeting: "Good evening", Cards: cards}); v.Example != "" {
|
||||
core.Use(c, core.Settings{})
|
||||
if v := chat.WelcomeScreen(c, chat.WelcomeScreenOptions{Greeting: "Good evening", Cards: cards}); v.Example != "" {
|
||||
log.Println("ask", v.Example)
|
||||
}
|
||||
}
|
||||
@@ -72,55 +72,55 @@ ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "247", Name: "SuggestionChips", ZhName: "建议标签", Category: chatsessionCategory, Demo: chatDemoSuggestionChips,
|
||||
Keys: "Tab enters the chips; Left/Right move; Enter or Space picks.", Code: `input := ""
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.SuggestionChips(c, &input, []string{"Plan a ball", "Write a sonnet"}, mujica.SuggestionChipsOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
chat.SuggestionChips(c, &input, []string{"Plan a ball", "Write a sonnet"}, chat.SuggestionChipsOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "248", Name: "PromptLibrary", ZhName: "提示模板库", Category: chatsessionCategory, Demo: chatDemoPromptLibrary,
|
||||
Keys: "Tab moves between categories, search, templates and fields; Up/Down move in a column; Enter picks.", Code: `var state mujica.PromptLibraryState
|
||||
templates := []mujica.PromptTemplate{{ID: "1", Category: "Letters", Title: "Invitation", Body: "Invite {{guest}}."}}
|
||||
Keys: "Tab moves between categories, search, templates and fields; Up/Down move in a column; Enter picks.", Code: `var state chat.PromptLibraryState
|
||||
templates := []chat.PromptTemplate{{ID: "1", Category: "Letters", Title: "Invitation", Body: "Invite {{guest}}."}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if v := mujica.PromptLibrary(c, &state, templates, mujica.PromptLibraryOptions{}); v.Inserted != "" {
|
||||
core.Use(c, core.Settings{})
|
||||
if v := chat.PromptLibrary(c, &state, templates, chat.PromptLibraryOptions{}); v.Inserted != "" {
|
||||
log.Println("insert", v.Inserted)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "249", Name: "CapabilityCards", ZhName: "能力卡片", Category: chatsessionCategory, Demo: chatDemoCapabilityCards,
|
||||
Keys: "Tab moves between cards; Up/Down move among a card's examples; Enter picks.", Code: `cards := []mujica.CapabilityCard{{Title: "Plan", Icon: icons.Must("calendar"), Examples: []string{"Plan a ball"}}}
|
||||
Keys: "Tab moves between cards; Up/Down move among a card's examples; Enter picks.", Code: `cards := []chat.CapabilityCard{{Title: "Plan", Icon: icons.Must("calendar"), Examples: []string{"Plan a ball"}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if v := mujica.CapabilityCards(c, cards, mujica.CapabilityCardsOptions{}); v.Example != "" {
|
||||
core.Use(c, core.Settings{})
|
||||
if v := chat.CapabilityCards(c, cards, chat.CapabilityCardsOptions{}); v.Example != "" {
|
||||
log.Println("ask", v.Example)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "250", Name: "FeedbackForm", ZhName: "反馈表单", Category: chatsessionCategory, Demo: chatDemoFeedbackForm,
|
||||
Keys: "Left/Right move among reasons, Space toggles; Escape cancels.", Code: `var value mujica.FeedbackValue
|
||||
Keys: "Left/Right move among reasons, Space toggles; Escape cancels.", Code: `var value chat.FeedbackValue
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.FeedbackForm(c, &value, mujica.FeedbackFormOptions{Rating: mujica.FeedbackDown}).Submitted {
|
||||
core.Use(c, core.Settings{})
|
||||
if chat.FeedbackForm(c, &value, chat.FeedbackFormOptions{Rating: chat.FeedbackDown}).Submitted {
|
||||
log.Println("reasons", value.Reasons)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "251", Name: "DateSeparator", ZhName: "日期分隔", Category: chatsessionCategory, Demo: chatDemoDateSeparator,
|
||||
Keys: "Not focusable; read as the date.", Code: `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.DateSeparator(c, mujica.DateOf(time.Now()), mujica.DateSeparatorOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
chat.DateSeparator(c, core.DateOf(time.Now()), chat.DateSeparatorOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "252", Name: "ThinkingIndicator", ZhName: "思考指示", Category: chatsessionCategory, Demo: chatDemoThinkingIndicator,
|
||||
Keys: "Not focusable; read as a status.", Code: `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ThinkingIndicator(c, mujica.ThinkingIndicatorOptions{Phase: "Searching the archives"})
|
||||
core.Use(c, core.Settings{})
|
||||
chat.ThinkingIndicator(c, chat.ThinkingIndicatorOptions{Phase: "Searching the archives"})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "253", Name: "StopGeneratingButton", ZhName: "停止生成按钮", Category: chatsessionCategory, Demo: chatDemoStopGeneratingButton,
|
||||
Keys: "Enter or Space stops; Cmd+. (Ctrl+. elsewhere) stops from anywhere.", Code: `generating := true
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.StopGeneratingButton(c, mujica.StopGeneratingButtonOptions{Generating: generating}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if chat.StopGeneratingButton(c, chat.StopGeneratingButtonOptions{Generating: generating}).Changed() {
|
||||
generating = false
|
||||
}
|
||||
}
|
||||
@@ -128,57 +128,57 @@ ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "254", Name: "SystemPromptEditor", ZhName: "系统提示编辑器", Category: chatsessionCategory, Demo: chatDemoSystemPromptEditor,
|
||||
Keys: "Cmd+S (Ctrl+S elsewhere) saves.", Code: `prompt, saved := "You are helpful.", "You are helpful."
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.SystemPromptEditor(c, &prompt, mujica.SystemPromptEditorOptions{Default: "You are helpful.", Saved: saved}).Save {
|
||||
core.Use(c, core.Settings{})
|
||||
if chat.SystemPromptEditor(c, &prompt, chat.SystemPromptEditorOptions{Default: "You are helpful.", Saved: saved}).Save {
|
||||
saved = prompt
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "255", Name: "CostEstimator", ZhName: "费用估算", Category: chatsessionCategory, Demo: chatDemoCostEstimator,
|
||||
Keys: "Not focusable; read as the estimated cost.", Code: `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.CostEstimator(c, mujica.CostEstimatorOptions{InputTokens: 12000, OutputTokens: 3400, InputPrice: 3, OutputPrice: 15})
|
||||
core.Use(c, core.Settings{})
|
||||
chat.CostEstimator(c, chat.CostEstimatorOptions{InputTokens: 12000, OutputTokens: 3400, InputPrice: 3, OutputPrice: 15})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "256", Name: "SharedConversationView", ZhName: "分享会话视图", Category: chatsessionCategory, Demo: chatDemoSharedConversationView,
|
||||
Keys: "Tab reaches Copy link and the messages; message text is selectable.", Code: `msgs := []mujica.SessionMessage{{Role: mujica.SessionUser, Text: "Hello"}, {Role: mujica.SessionAssistant, Text: "Good day."}}
|
||||
Keys: "Tab reaches Copy link and the messages; message text is selectable.", Code: `msgs := []chat.SessionMessage{{Role: chat.SessionUser, Text: "Hello"}, {Role: chat.SessionAssistant, Text: "Good day."}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.SharedConversationView(c, msgs, mujica.SharedConversationViewOptions{Title: "Greeting", SharedBy: "Ann"}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if chat.SharedConversationView(c, msgs, chat.SharedConversationViewOptions{Title: "Greeting", SharedBy: "Ann"}).Changed() {
|
||||
c.WriteClipboard("https://example.com/share/1")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "257", Name: "ConversationExport", ZhName: "会话导出", Category: chatsessionCategory, Demo: chatDemoConversationExport,
|
||||
Keys: "Arrows choose a format and range; Tab reaches Export.", Code: `var state mujica.ConversationExportState
|
||||
msgs := []mujica.SessionMessage{{Role: mujica.SessionUser, Text: "Hello"}}
|
||||
Keys: "Arrows choose a format and range; Tab reaches Export.", Code: `var state chat.ConversationExportState
|
||||
msgs := []chat.SessionMessage{{Role: chat.SessionUser, Text: "Hello"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if v := mujica.ConversationExport(c, &state, msgs, mujica.ConversationExportOptions{Title: "Greeting"}); v.Exported {
|
||||
core.Use(c, core.Settings{})
|
||||
if v := chat.ConversationExport(c, &state, msgs, chat.ConversationExportOptions{Title: "Greeting"}); v.Exported {
|
||||
log.Println(len(v.Content), "bytes to save")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "258", Name: "RegenerateMenu", ZhName: "重新生成菜单", Category: chatsessionCategory, Demo: chatDemoRegenerateMenu,
|
||||
Keys: "Enter, Space or Down open; Up/Down move; Right opens the model list; Escape closes.", Code: `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if v := mujica.RegenerateMenu(c, mujica.RegenerateMenuOptions{Models: []string{"Opus", "Sonnet"}, Current: "Opus"}); v.Chosen {
|
||||
core.Use(c, core.Settings{})
|
||||
if v := chat.RegenerateMenu(c, chat.RegenerateMenuOptions{Models: []string{"Opus", "Sonnet"}, Current: "Opus"}); v.Chosen {
|
||||
log.Println("retry", v.Choice.Kind, v.Choice.Model, v.Choice.Variant)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "259", Name: "ArtifactCard", ZhName: "产物卡片", Category: chatsessionCategory, Demo: chatDemoArtifactCard,
|
||||
Keys: "Enter or Space opens the artifact panel.", Code: `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.ArtifactCard(c, mujica.ArtifactCardOptions{Title: "seating.go", Detail: "Go · 120 lines"}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if chat.ArtifactCard(c, chat.ArtifactCardOptions{Title: "seating.go", Detail: "Go · 120 lines"}).Changed() {
|
||||
log.Println("open artifact panel")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "260", Name: "ChatEmptyState", ZhName: "会话空状态", Category: chatsessionCategory, Demo: chatDemoChatEmptyState,
|
||||
Keys: "Tab reaches the action; Enter runs it.", Code: `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.ChatEmptyState(c, mujica.ChatEmptyStateOptions{Kind: mujica.ChatEmptyOffline}).Action {
|
||||
core.Use(c, core.Settings{})
|
||||
if chat.ChatEmptyState(c, chat.ChatEmptyStateOptions{Kind: chat.ChatEmptyOffline}).Action {
|
||||
log.Println("reconnect")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,15 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chat"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
// chatHistory is the gallery's sample conversations, dated from now.
|
||||
func chatHistory(now time.Time) []mujica.ChatConversation {
|
||||
func chatHistory(now time.Time) []chat.ChatConversation {
|
||||
day := 24 * time.Hour
|
||||
return []mujica.ChatConversation{
|
||||
return []chat.ChatConversation{
|
||||
{ID: "a", Title: "Court etiquette notes", Updated: now.Add(-2 * time.Hour)},
|
||||
{ID: "b", Title: "Ballroom seating plan", Updated: now.Add(-day)},
|
||||
{ID: "c", Title: "Harpsichord repair", Updated: now.Add(-4 * day)},
|
||||
@@ -24,17 +25,17 @@ func chatHistory(now time.Time) []mujica.ChatConversation {
|
||||
}
|
||||
}
|
||||
|
||||
func chatSampleMessages(now time.Time) []mujica.SessionMessage {
|
||||
return []mujica.SessionMessage{
|
||||
{Role: mujica.SessionUser, Text: "How should guests be seated at the banquet?", Time: now.Add(-10 * time.Minute)},
|
||||
{Role: mujica.SessionAssistant, Text: "Seat by rank: the host at the head, honored guests to the right.", Time: now.Add(-9 * time.Minute)},
|
||||
{Role: mujica.SessionUser, Author: "Lady Ann", Text: "And the musicians?", Time: now.Add(-5 * time.Minute)},
|
||||
{Role: mujica.SessionAssistant, Text: "Place the quartet behind a screen near the gallery.", Time: now.Add(-4 * time.Minute)},
|
||||
func chatSampleMessages(now time.Time) []chat.SessionMessage {
|
||||
return []chat.SessionMessage{
|
||||
{Role: chat.SessionUser, Text: "How should guests be seated at the banquet?", Time: now.Add(-10 * time.Minute)},
|
||||
{Role: chat.SessionAssistant, Text: "Seat by rank: the host at the head, honored guests to the right.", Time: now.Add(-9 * time.Minute)},
|
||||
{Role: chat.SessionUser, Author: "Lady Ann", Text: "And the musicians?", Time: now.Add(-5 * time.Minute)},
|
||||
{Role: chat.SessionAssistant, Text: "Place the quartet behind a screen near the gallery.", Time: now.Add(-4 * time.Minute)},
|
||||
}
|
||||
}
|
||||
|
||||
func chatSampleCards() []mujica.CapabilityCard {
|
||||
return []mujica.CapabilityCard{
|
||||
func chatSampleCards() []chat.CapabilityCard {
|
||||
return []chat.CapabilityCard{
|
||||
{Title: "Write", Description: "Letters, speeches and verse.", Icon: icons.Must("file-text"), Examples: []string{"Draft a royal decree", "Write a sonnet"}},
|
||||
{Title: "Plan", Description: "Banquets, seating and schedules.", Icon: icons.Must("calendar"), Examples: []string{"Plan a ball for 200 guests"}},
|
||||
{Title: "Research", Description: "History, heraldry and music.", Icon: icons.Must("search"), Examples: []string{"Explain the minuet"}},
|
||||
@@ -43,19 +44,19 @@ func chatSampleCards() []mujica.CapabilityCard {
|
||||
|
||||
func chatDemoConversationList(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
list mujica.ConversationListState
|
||||
items []mujica.ChatConversation
|
||||
list chat.ConversationListState
|
||||
items []chat.ChatConversation
|
||||
many, rich bool
|
||||
disabled bool
|
||||
}
|
||||
s := *ui.Local(c.Root(), "241", func() *state { return &state{items: chatHistory(time.Now())} })
|
||||
choiceDemo(c, func() {
|
||||
if mujica.Switch(c, &s.many, "100,000 conversations", mujica.SwitchOptions{}).Changed() {
|
||||
if input.Switch(c, &s.many, "100,000 conversations", input.SwitchOptions{}).Changed() {
|
||||
s.items = chatHistory(time.Now())
|
||||
if s.many {
|
||||
now := time.Now()
|
||||
for i := range 100000 {
|
||||
s.items = append(s.items, mujica.ChatConversation{ID: fmt.Sprint("n", i), Title: fmt.Sprintf("Archive %d", i), Updated: now.Add(-time.Duration(i) * 7 * time.Minute)})
|
||||
s.items = append(s.items, chat.ChatConversation{ID: fmt.Sprint("n", i), Title: fmt.Sprintf("Archive %d", i), Updated: now.Add(-time.Duration(i) * 7 * time.Minute)})
|
||||
}
|
||||
}
|
||||
log(fmt.Sprintf("conversations = %d", len(s.items)))
|
||||
@@ -63,17 +64,17 @@ func chatDemoConversationList(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.rich, "ConversationItem rows", log)
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
var row func(conv mujica.ChatConversation)
|
||||
var row func(conv chat.ChatConversation)
|
||||
if s.rich {
|
||||
row = func(conv mujica.ChatConversation) {
|
||||
v := mujica.ConversationItem(c, &conv, mujica.ConversationItemOptions{Selected: conv.ID == s.list.Selected})
|
||||
row = func(conv chat.ChatConversation) {
|
||||
v := chat.ConversationItem(c, &conv, chat.ConversationItemOptions{Selected: conv.ID == s.list.Selected})
|
||||
if v.Opened {
|
||||
s.list.Selected = conv.ID
|
||||
log("open " + conv.Title)
|
||||
}
|
||||
}
|
||||
}
|
||||
v := mujica.ConversationList(c, &s.list, s.items, mujica.ConversationListOptions{Label: "History", Disabled: s.disabled}, row)
|
||||
v := chat.ConversationList(c, &s.list, s.items, chat.ConversationListOptions{Label: "History", Disabled: s.disabled}, row)
|
||||
v.Element.Width(380).Height(380)
|
||||
if v.Changed() {
|
||||
log("select " + s.list.Selected)
|
||||
@@ -86,7 +87,7 @@ func chatDemoConversationList(c *ui.Context, log func(string)) {
|
||||
|
||||
func chatDemoConversationItem(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
convs []mujica.ChatConversation
|
||||
convs []chat.ChatConversation
|
||||
selected, disabled bool
|
||||
}
|
||||
s := *ui.Local(c.Root(), "242", func() *state { return &state{convs: chatHistory(time.Now())[:3]} })
|
||||
@@ -98,7 +99,7 @@ func chatDemoConversationItem(c *ui.Context, log func(string)) {
|
||||
for i := range s.convs {
|
||||
conv := &s.convs[i]
|
||||
ui.Row(c).Key(conv.ID).Children(func() {
|
||||
v := mujica.ConversationItem(c, conv, mujica.ConversationItemOptions{Selected: s.selected && i == 0, Disabled: s.disabled})
|
||||
v := chat.ConversationItem(c, conv, chat.ConversationItemOptions{Selected: s.selected && i == 0, Disabled: s.disabled})
|
||||
switch {
|
||||
case v.Opened:
|
||||
log("open " + conv.Title)
|
||||
@@ -122,7 +123,7 @@ func chatDemoConversationSearch(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
query, asked string
|
||||
at time.Time
|
||||
hits []mujica.ConversationSearchHit
|
||||
hits []chat.ConversationSearchHit
|
||||
fail, slow bool
|
||||
disabled bool
|
||||
}
|
||||
@@ -137,7 +138,7 @@ func chatDemoConversationSearch(c *ui.Context, log func(string)) {
|
||||
s.asked, s.hits, loading = s.query, nil, false
|
||||
for _, conv := range chatHistory(time.Now()) {
|
||||
if q := strings.ToLower(s.query); q != "" && strings.Contains(strings.ToLower(conv.Title), q) {
|
||||
s.hits = append(s.hits, mujica.ConversationSearchHit{ID: conv.ID, Title: conv.Title, Snippet: "…mentions " + strings.ToLower(conv.Title) + "…"})
|
||||
s.hits = append(s.hits, chat.ConversationSearchHit{ID: conv.ID, Title: conv.Title, Snippet: "…mentions " + strings.ToLower(conv.Title) + "…"})
|
||||
}
|
||||
}
|
||||
log(fmt.Sprintf("results for %q: %d", s.query, len(s.hits)))
|
||||
@@ -147,7 +148,7 @@ func chatDemoConversationSearch(c *ui.Context, log func(string)) {
|
||||
errText = "The archive did not answer."
|
||||
}
|
||||
ui.Column(c).Width(420).Children(func() {
|
||||
v := mujica.ConversationSearch(c, &s.query, s.hits, mujica.ConversationSearchOptions{Loading: loading, Error: errText, Disabled: s.disabled, Label: "Search conversations"})
|
||||
v := chat.ConversationSearch(c, &s.query, s.hits, chat.ConversationSearchOptions{Loading: loading, Error: errText, Disabled: s.disabled, Label: "Search conversations"})
|
||||
if v.QueryChanged {
|
||||
s.at = c.Now()
|
||||
log(fmt.Sprintf("query %q", s.query))
|
||||
@@ -165,21 +166,21 @@ func chatDemoConversationSearch(c *ui.Context, log func(string)) {
|
||||
|
||||
func chatDemoProjectList(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
list mujica.ProjectListState
|
||||
list chat.ProjectListState
|
||||
owner map[string]string
|
||||
disabled bool
|
||||
}
|
||||
s := *ui.Local(c.Root(), "244", func() *state {
|
||||
return &state{owner: map[string]string{"a": "p1", "b": "p1", "c": "p3", "f": "p2"}}
|
||||
})
|
||||
projects := []mujica.ChatProject{{ID: "p1", Name: "Coronation", Folder: "Court"}, {ID: "p2", Name: "Gardens", Folder: "Court"}, {ID: "p3", Name: "Music"}}
|
||||
projects := []chat.ChatProject{{ID: "p1", Name: "Coronation", Folder: "Court"}, {ID: "p2", Name: "Gardens", Folder: "Court"}, {ID: "p3", Name: "Music"}}
|
||||
choiceDemo(c, func() {
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
ui.Row(c).Gap(24).AlignItems(ui.Start).Children(func() {
|
||||
ui.Column(c).Width(380).Children(func() {
|
||||
convs := func(id string) []mujica.ChatConversation {
|
||||
var out []mujica.ChatConversation
|
||||
convs := func(id string) []chat.ChatConversation {
|
||||
var out []chat.ChatConversation
|
||||
for _, conv := range chatHistory(time.Now()) {
|
||||
if s.owner[conv.ID] == id {
|
||||
out = append(out, conv)
|
||||
@@ -187,7 +188,7 @@ func chatDemoProjectList(c *ui.Context, log func(string)) {
|
||||
}
|
||||
return out
|
||||
}
|
||||
v := mujica.ProjectList(c, &s.list, projects, convs, mujica.ProjectListOptions{Label: "Projects", Disabled: s.disabled})
|
||||
v := chat.ProjectList(c, &s.list, projects, convs, chat.ProjectListOptions{Label: "Projects", Disabled: s.disabled})
|
||||
switch {
|
||||
case v.Moved:
|
||||
s.owner[v.MoveID] = v.MoveTo
|
||||
@@ -200,7 +201,7 @@ func chatDemoProjectList(c *ui.Context, log func(string)) {
|
||||
})
|
||||
choiceLabeled(c, "Drag onto a project", func() {
|
||||
conv := chatHistory(time.Now())[3]
|
||||
v := mujica.ConversationItem(c, &conv, mujica.ConversationItemOptions{Draggable: true})
|
||||
v := chat.ConversationItem(c, &conv, chat.ConversationItemOptions{Draggable: true})
|
||||
v.Element.Width(300)
|
||||
if v.Opened {
|
||||
log("open " + conv.Title)
|
||||
@@ -212,13 +213,13 @@ func chatDemoProjectList(c *ui.Context, log func(string)) {
|
||||
|
||||
func chatDemoProjectKnowledgePanel(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
files []mujica.KnowledgeFile
|
||||
files []chat.KnowledgeFile
|
||||
next int
|
||||
small bool
|
||||
disabled bool
|
||||
}
|
||||
s := *ui.Local(c.Root(), "245", func() *state {
|
||||
return &state{files: []mujica.KnowledgeFile{{ID: "1", Name: "etiquette.pdf", Tokens: 120000}, {ID: "2", Name: "menu.md", Tokens: 30000}}}
|
||||
return &state{files: []chat.KnowledgeFile{{ID: "1", Name: "etiquette.pdf", Tokens: 120000}, {ID: "2", Name: "menu.md", Tokens: 30000}}}
|
||||
})
|
||||
choiceDemo(c, func() {
|
||||
choiceFlag(c, &s.small, "Capacity 150,000", log)
|
||||
@@ -229,10 +230,10 @@ func chatDemoProjectKnowledgePanel(c *ui.Context, log func(string)) {
|
||||
capacity = 150000
|
||||
}
|
||||
ui.Column(c).Width(440).Children(func() {
|
||||
v := mujica.ProjectKnowledgePanel(c, s.files, mujica.ProjectKnowledgePanelOptions{Capacity: capacity, Disabled: s.disabled})
|
||||
v := chat.ProjectKnowledgePanel(c, s.files, chat.ProjectKnowledgePanelOptions{Capacity: capacity, Disabled: s.disabled})
|
||||
if v.Add {
|
||||
s.next++
|
||||
s.files = append(s.files, mujica.KnowledgeFile{ID: fmt.Sprint("n", s.next), Name: fmt.Sprintf("notes-%d.txt", s.next), Tokens: 15000})
|
||||
s.files = append(s.files, chat.KnowledgeFile{ID: fmt.Sprint("n", s.next), Name: fmt.Sprintf("notes-%d.txt", s.next), Tokens: 15000})
|
||||
log("add (the demo invents a file)")
|
||||
}
|
||||
if v.Removed != "" {
|
||||
|
||||
@@ -6,7 +6,10 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/agent"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chat"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func chatDemoWelcomeScreen(c *ui.Context, log func(string)) {
|
||||
@@ -19,14 +22,14 @@ func chatDemoWelcomeScreen(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.cards, "Capability cards", log)
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
var cards []mujica.CapabilityCard
|
||||
var cards []chat.CapabilityCard
|
||||
if s.cards {
|
||||
cards = chatSampleCards()
|
||||
}
|
||||
ui.Column(c).Width(860).Children(func() {
|
||||
v := mujica.WelcomeScreen(c, mujica.WelcomeScreenOptions{Greeting: "Good afternoon", Subtitle: "How may I serve the court?", Cards: cards, Disabled: s.disabled,
|
||||
v := chat.WelcomeScreen(c, chat.WelcomeScreenOptions{Greeting: "Good afternoon", Subtitle: "How may I serve the court?", Cards: cards, Disabled: s.disabled,
|
||||
Prompts: func() {
|
||||
if mujica.SuggestionChips(c, &s.input, []string{"Plan a ball", "Write a sonnet"}, mujica.SuggestionChipsOptions{Disabled: s.disabled}).Chosen != "" {
|
||||
if chat.SuggestionChips(c, &s.input, []string{"Plan a ball", "Write a sonnet"}, chat.SuggestionChipsOptions{Disabled: s.disabled}).Chosen != "" {
|
||||
log(fmt.Sprintf("fill %q", s.input))
|
||||
}
|
||||
}})
|
||||
@@ -47,8 +50,8 @@ func chatDemoSuggestionChips(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.send, "Send directly", log)
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
v := mujica.SuggestionChips(c, &s.input, []string{"Plan a ball", "Write a sonnet", "Explain the minuet"},
|
||||
mujica.SuggestionChipsOptions{Send: s.send, Label: "Suggestions", Disabled: s.disabled})
|
||||
v := chat.SuggestionChips(c, &s.input, []string{"Plan a ball", "Write a sonnet", "Explain the minuet"},
|
||||
chat.SuggestionChipsOptions{Send: s.send, Label: "Suggestions", Disabled: s.disabled})
|
||||
switch {
|
||||
case v.Sent:
|
||||
log(fmt.Sprintf("send %q", v.Chosen))
|
||||
@@ -56,18 +59,18 @@ func chatDemoSuggestionChips(c *ui.Context, log func(string)) {
|
||||
log(fmt.Sprintf("fill %q", v.Chosen))
|
||||
}
|
||||
ui.Column(c).Width(420).Children(func() {
|
||||
mujica.TextInput(c, &s.input, mujica.TextInputOptions{Label: "Message", Placeholder: "Message"})
|
||||
input.TextInput(c, &s.input, input.TextInputOptions{Label: "Message", Placeholder: "Message"})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func chatDemoPromptLibrary(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
lib mujica.PromptLibraryState
|
||||
lib chat.PromptLibraryState
|
||||
disabled bool
|
||||
}
|
||||
s := *ui.Local(c.Root(), "248", func() *state { return &state{} })
|
||||
templates := []mujica.PromptTemplate{
|
||||
templates := []chat.PromptTemplate{
|
||||
{ID: "t1", Category: "Letters", Title: "Invitation", Body: "Write an invitation to {{guest}} for the {{event}}."},
|
||||
{ID: "t2", Category: "Letters", Title: "Thank-you note", Body: "Thank {{host}} for the evening."},
|
||||
{ID: "t3", Category: "Music", Title: "Recital program", Body: "List pieces for a {{instrument}} recital."},
|
||||
@@ -77,7 +80,7 @@ func chatDemoPromptLibrary(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
before := s.lib.Selected
|
||||
v := mujica.PromptLibrary(c, &s.lib, templates, mujica.PromptLibraryOptions{Label: "Prompt library", Disabled: s.disabled})
|
||||
v := chat.PromptLibrary(c, &s.lib, templates, chat.PromptLibraryOptions{Label: "Prompt library", Disabled: s.disabled})
|
||||
if s.lib.Selected != before {
|
||||
log("preview " + s.lib.Selected)
|
||||
}
|
||||
@@ -101,7 +104,7 @@ func chatDemoCapabilityCards(c *ui.Context, log func(string)) {
|
||||
cols = 1
|
||||
}
|
||||
ui.Column(c).Width(860).Children(func() {
|
||||
if v := mujica.CapabilityCards(c, chatSampleCards(), mujica.CapabilityCardsOptions{Columns: cols, Disabled: s.disabled}); v.Example != "" {
|
||||
if v := chat.CapabilityCards(c, chatSampleCards(), chat.CapabilityCardsOptions{Columns: cols, Disabled: s.disabled}); v.Example != "" {
|
||||
log(fmt.Sprintf("ask %q", v.Example))
|
||||
}
|
||||
})
|
||||
@@ -110,7 +113,7 @@ func chatDemoCapabilityCards(c *ui.Context, log func(string)) {
|
||||
|
||||
func chatDemoFeedbackForm(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
value mujica.FeedbackValue
|
||||
value chat.FeedbackValue
|
||||
down, submitting bool
|
||||
disabled bool
|
||||
at time.Time
|
||||
@@ -120,22 +123,22 @@ func chatDemoFeedbackForm(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.down, "Thumbs down", log)
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
rating := mujica.FeedbackUp
|
||||
rating := chat.FeedbackUp
|
||||
if s.down {
|
||||
rating = mujica.FeedbackDown
|
||||
rating = chat.FeedbackDown
|
||||
}
|
||||
if s.submitting && choiceDue(c, s.at) {
|
||||
s.submitting, s.value = false, mujica.FeedbackValue{}
|
||||
s.submitting, s.value = false, chat.FeedbackValue{}
|
||||
log("feedback sent")
|
||||
}
|
||||
ui.Column(c).Width(480).Children(func() {
|
||||
v := mujica.FeedbackForm(c, &s.value, mujica.FeedbackFormOptions{Rating: rating, Submitting: s.submitting, Disabled: s.disabled})
|
||||
v := chat.FeedbackForm(c, &s.value, chat.FeedbackFormOptions{Rating: rating, Submitting: s.submitting, Disabled: s.disabled})
|
||||
if v.Submitted {
|
||||
s.submitting, s.at = true, c.Now()
|
||||
log(fmt.Sprintf("submit reasons %q, %d characters", s.value.Reasons, len(s.value.Comment)))
|
||||
}
|
||||
if v.Cancelled {
|
||||
s.value = mujica.FeedbackValue{}
|
||||
s.value = chat.FeedbackValue{}
|
||||
log("cancel")
|
||||
}
|
||||
})
|
||||
@@ -143,13 +146,13 @@ func chatDemoFeedbackForm(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
func chatDemoDateSeparator(c *ui.Context, log func(string)) {
|
||||
today := mujica.DateOf(time.Now())
|
||||
today := core.DateOf(time.Now())
|
||||
choiceDemo(c, func() {
|
||||
ui.Text(c, "No parameters; the language follows the window.")
|
||||
}, func() {
|
||||
ui.Column(c).Width(520).Children(func() {
|
||||
for _, d := range []int{0, -1, -12} {
|
||||
mujica.DateSeparator(c, today.AddDays(d), mujica.DateSeparatorOptions{})
|
||||
chat.DateSeparator(c, today.AddDays(d), chat.DateSeparatorOptions{})
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -160,12 +163,12 @@ func chatDemoThinkingIndicator(c *ui.Context, log func(string)) {
|
||||
s := *ui.Local(c.Root(), "252", func() *state { return &state{} })
|
||||
phases := []string{"", "Searching the archives", "Reading 3 letters", "Composing a reply"}
|
||||
choiceDemo(c, func() {
|
||||
if mujica.Button(c, "Next phase", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Next phase", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.phase = (s.phase + 1) % len(phases)
|
||||
log(fmt.Sprintf("phase %q", phases[s.phase]))
|
||||
}
|
||||
}, func() {
|
||||
mujica.ThinkingIndicator(c, mujica.ThinkingIndicatorOptions{Phase: phases[s.phase]})
|
||||
chat.ThinkingIndicator(c, chat.ThinkingIndicatorOptions{Phase: phases[s.phase]})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -176,7 +179,7 @@ func chatDemoStopGeneratingButton(c *ui.Context, log func(string)) {
|
||||
}
|
||||
s := *ui.Local(c.Root(), "253", func() *state { return &state{} })
|
||||
choiceDemo(c, func() {
|
||||
if mujica.Button(c, "Generate", mujica.ButtonOptions{Disabled: s.generating}).Clicked() {
|
||||
if input.Button(c, "Generate", input.ButtonOptions{Disabled: s.generating}).Clicked() {
|
||||
s.generating, s.at = true, c.Now()
|
||||
log("generate")
|
||||
}
|
||||
@@ -188,12 +191,12 @@ func chatDemoStopGeneratingButton(c *ui.Context, log func(string)) {
|
||||
} else if s.generating {
|
||||
c.After(time.Second)
|
||||
}
|
||||
if mujica.StopGeneratingButton(c, mujica.StopGeneratingButtonOptions{Generating: s.generating, Disabled: s.disabled}).Changed() {
|
||||
if chat.StopGeneratingButton(c, chat.StopGeneratingButtonOptions{Generating: s.generating, Disabled: s.disabled}).Changed() {
|
||||
s.generating = false
|
||||
log("stop")
|
||||
}
|
||||
if !s.generating {
|
||||
ui.Text(c, "Idle: the button is hidden.").TextColor(mujica.Tokens(c).TextMuted)
|
||||
ui.Text(c, "Idle: the button is hidden.").TextColor(core.Tokens(c).TextMuted)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -221,7 +224,7 @@ func chatDemoSystemPromptEditor(c *ui.Context, log func(string)) {
|
||||
limit = 60
|
||||
}
|
||||
ui.Column(c).Width(520).Children(func() {
|
||||
v := mujica.SystemPromptEditor(c, &s.value, mujica.SystemPromptEditorOptions{Default: "You are helpful.", Saved: s.saved,
|
||||
v := chat.SystemPromptEditor(c, &s.value, chat.SystemPromptEditorOptions{Default: "You are helpful.", Saved: s.saved,
|
||||
MaxLength: limit, Saving: s.saving, Disabled: s.disabled})
|
||||
if v.Save {
|
||||
s.saving, s.at = true, c.Now()
|
||||
@@ -243,7 +246,7 @@ func chatDemoCostEstimator(c *ui.Context, log func(string)) {
|
||||
label string
|
||||
}{{&s.in, "Input tokens"}, {&s.out, "Output tokens"}, {&s.inPrice, "Input $/M"}, {&s.outPrice, "Output $/M"}} {
|
||||
choiceLabeled(c, f.label, func() {
|
||||
r := mujica.NumberInput(c, f.v, mujica.NumberInputOptions{Min: 0, Max: 1e7, Step: 100, Label: f.label})
|
||||
r := input.NumberInput(c, f.v, input.NumberInputOptions{Min: 0, Max: 1e7, Step: 100, Label: f.label})
|
||||
r.Element.Width(150)
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("%s = %v", f.label, *f.v))
|
||||
@@ -252,7 +255,7 @@ func chatDemoCostEstimator(c *ui.Context, log func(string)) {
|
||||
}
|
||||
}, func() {
|
||||
ui.Column(c).Width(420).Children(func() {
|
||||
mujica.CostEstimator(c, mujica.CostEstimatorOptions{InputTokens: int(s.in), OutputTokens: int(s.out), InputPrice: s.inPrice, OutputPrice: s.outPrice})
|
||||
chat.CostEstimator(c, chat.CostEstimatorOptions{InputTokens: int(s.in), OutputTokens: int(s.out), InputPrice: s.inPrice, OutputPrice: s.outPrice})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -264,7 +267,7 @@ func chatDemoSharedConversationView(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
ui.Column(c).Width(600).Height(420).Children(func() {
|
||||
if mujica.SharedConversationView(c, chatSampleMessages(time.Now()), mujica.SharedConversationViewOptions{Title: "Banquet seating",
|
||||
if chat.SharedConversationView(c, chatSampleMessages(time.Now()), chat.SharedConversationViewOptions{Title: "Banquet seating",
|
||||
SharedBy: "Lady Ann", SharedAt: time.Now(), Disabled: s.disabled}).Changed() {
|
||||
c.WriteClipboard("https://example.com/share/banquet")
|
||||
log("copy link")
|
||||
@@ -275,7 +278,7 @@ func chatDemoSharedConversationView(c *ui.Context, log func(string)) {
|
||||
|
||||
func chatDemoConversationExport(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
export mujica.ConversationExportState
|
||||
export chat.ConversationExportState
|
||||
disabled bool
|
||||
}
|
||||
s := *ui.Local(c.Root(), "257", func() *state { return &state{} })
|
||||
@@ -283,7 +286,7 @@ func chatDemoConversationExport(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
ui.Column(c).Width(580).Children(func() {
|
||||
v := mujica.ConversationExport(c, &s.export, chatSampleMessages(time.Now()), mujica.ConversationExportOptions{Title: "Banquet seating",
|
||||
v := chat.ConversationExport(c, &s.export, chatSampleMessages(time.Now()), chat.ConversationExportOptions{Title: "Banquet seating",
|
||||
Selected: []int{1, 3}, Disabled: s.disabled})
|
||||
if v.Exported {
|
||||
log(fmt.Sprintf("export %d bytes as format %d (the caller writes the file)", len(v.Content), v.Format))
|
||||
@@ -298,7 +301,7 @@ func chatDemoRegenerateMenu(c *ui.Context, log func(string)) {
|
||||
choiceDemo(c, func() {
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
v := mujica.RegenerateMenu(c, mujica.RegenerateMenuOptions{Models: []string{"Opus", "Sonnet", "Haiku"}, Current: "Opus", Disabled: s.disabled})
|
||||
v := chat.RegenerateMenu(c, chat.RegenerateMenuOptions{Models: []string{"Opus", "Sonnet", "Haiku"}, Current: "Opus", Disabled: s.disabled})
|
||||
if v.Chosen {
|
||||
log(fmt.Sprintf("retry kind %d model %q variant %q", v.Choice.Kind, v.Choice.Model, v.Choice.Variant))
|
||||
}
|
||||
@@ -312,13 +315,13 @@ func chatDemoArtifactCard(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.streaming, "Streaming", log)
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
for _, a := range []mujica.ArtifactCardOptions{
|
||||
{Kind: mujica.ArtifactCode, Title: "seating.go", Detail: "Go · 120 lines"},
|
||||
{Kind: mujica.ArtifactDocument, Title: "Royal decree", Detail: "2 pages"},
|
||||
{Kind: mujica.ArtifactWeb, Title: "Ball invitation"},
|
||||
for _, a := range []chat.ArtifactCardOptions{
|
||||
{Kind: agent.ArtifactCode, Title: "seating.go", Detail: "Go · 120 lines"},
|
||||
{Kind: agent.ArtifactDocument, Title: "Royal decree", Detail: "2 pages"},
|
||||
{Kind: agent.ArtifactWeb, Title: "Ball invitation"},
|
||||
} {
|
||||
a.Streaming, a.Disabled = s.streaming, s.disabled
|
||||
if mujica.ArtifactCard(c, a).Changed() {
|
||||
if chat.ArtifactCard(c, a).Changed() {
|
||||
log("open " + a.Title)
|
||||
}
|
||||
}
|
||||
@@ -329,11 +332,11 @@ func chatDemoChatEmptyState(c *ui.Context, log func(string)) {
|
||||
type state struct{ kind int }
|
||||
s := *ui.Local(c.Root(), "260", func() *state { return &state{} })
|
||||
choiceDemo(c, func() {
|
||||
mujica.RadioGroup(c, &s.kind, []mujica.RadioItem[int]{{Value: 0, Label: "New"}, {Value: 1, Label: "No results"}, {Value: 2, Label: "Offline"}},
|
||||
mujica.RadioGroupOptions{Horizontal: true, Label: "Kind"})
|
||||
input.RadioGroup(c, &s.kind, []input.RadioItem[int]{{Value: 0, Label: "New"}, {Value: 1, Label: "No results"}, {Value: 2, Label: "Offline"}},
|
||||
input.RadioGroupOptions{Horizontal: true, Label: "Kind"})
|
||||
}, func() {
|
||||
ui.Column(c).Width(420).Children(func() {
|
||||
if mujica.ChatEmptyState(c, mujica.ChatEmptyStateOptions{Kind: mujica.ChatEmptyKind(s.kind), Query: "minuet"}).Action {
|
||||
if chat.ChatEmptyState(c, chat.ChatEmptyStateOptions{Kind: chat.ChatEmptyKind(s.kind), Query: "minuet"}).Action {
|
||||
log(fmt.Sprintf("action for kind %d", s.kind))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -8,9 +8,10 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/icons"
|
||||
"mujicaui/theme"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||
)
|
||||
|
||||
const choiceCategory = "Choices"
|
||||
@@ -18,104 +19,104 @@ const choiceCategory = "Choices"
|
||||
var choicePages = []page{
|
||||
{ID: "031", Name: "Switch", ZhName: "开关", Category: choiceCategory, Demo: switchDemo, Code: `wifi := true
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.Switch(c, &wifi, "Wi-Fi", mujica.SwitchOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if input.Switch(c, &wifi, "Wi-Fi", input.SwitchOptions{}).Changed() {
|
||||
log.Println("Wi-Fi:", wifi)
|
||||
}
|
||||
}`, Keys: "Tab focuses the switch; Space turns it over."},
|
||||
{ID: "032", Name: "Toggle", ZhName: "切换按钮", Category: choiceCategory, Demo: toggleDemo, Code: `bold := false
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Toggle(c, &bold, "", mujica.ToggleOptions{Icon: icons.Must("bold"), Label: "Bold"})
|
||||
core.Use(c, core.Settings{})
|
||||
input.Toggle(c, &bold, "", input.ToggleOptions{Icon: icons.Must("bold"), Label: "Bold"})
|
||||
}`, Keys: "Tab focuses the toggle; Space or Enter presses or releases it."},
|
||||
{ID: "033", Name: "ToggleGroup", ZhName: "切换按钮组", Category: choiceCategory, Demo: toggleGroupDemo, Code: `align := []string{"left"}
|
||||
items := []mujica.ToggleItem[string]{
|
||||
items := []input.ToggleItem[string]{
|
||||
{Value: "left", Label: "Left"},
|
||||
{Value: "center", Label: "Center"},
|
||||
{Value: "right", Label: "Right"},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ToggleGroup(c, &align, items, mujica.ToggleGroupOptions{Label: "Align"})
|
||||
core.Use(c, core.Settings{})
|
||||
input.ToggleGroup(c, &align, items, input.ToggleGroupOptions{Label: "Align"})
|
||||
}`, Keys: "Tab enters the group at the first enabled item, later at the last focused one; Left/Right (Up/Down when vertical), Home and End move; Space presses."},
|
||||
{ID: "034", Name: "Slider", ZhName: "滑块", Category: choiceCategory, Demo: sliderDemo, Code: `volume := 40.0
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Slider(c, &volume, mujica.SliderOptions{Step: 5, ShowValue: true, Label: "Volume"})
|
||||
core.Use(c, core.Settings{})
|
||||
input.Slider(c, &volume, input.SliderOptions{Step: 5, ShowValue: true, Label: "Volume"})
|
||||
}`, Keys: "Arrows move one step, Home and End go to the ends; the move applies when the key is released."},
|
||||
{ID: "035", Name: "RangeSlider", ZhName: "范围滑块", Category: choiceCategory, Demo: rangeSliderDemo, Code: `low, high := 20.0, 80.0
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.RangeSlider(c, &low, &high, mujica.RangeSliderOptions{Step: 10, Label: "Price"})
|
||||
core.Use(c, core.Settings{})
|
||||
input.RangeSlider(c, &low, &high, input.RangeSliderOptions{Step: 10, Label: "Price"})
|
||||
}`, Keys: "Tab moves between the two thumbs; arrows move one step; Home and End go as far as the other thumb allows."},
|
||||
{ID: "036", Name: "Select", ZhName: "选择器", Category: choiceCategory, Demo: selectDemo, Code: `plan := ""
|
||||
options := []mujica.SelectOption[string]{
|
||||
options := []input.SelectOption[string]{
|
||||
{Value: "free", Label: "Free"},
|
||||
{Value: "pro", Label: "Pro"},
|
||||
{Value: "team", Label: "Team", Disabled: true},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Select(c, &plan, options, mujica.SelectOptions{Clearable: true, Label: "Plan"})
|
||||
core.Use(c, core.Settings{})
|
||||
input.Select(c, &plan, options, input.SelectOptions{Clearable: true, Label: "Plan"})
|
||||
}`, Keys: "Enter, Space, Up or Down open; arrows, Home, End move; Enter chooses; Escape closes; typing letters on the closed select jumps to a match; Backspace/Delete clear when Clearable."},
|
||||
{ID: "037", Name: "MultiSelect", ZhName: "多选选择器", Category: choiceCategory, Demo: multiSelectDemo, Code: `tags := []int{1}
|
||||
options := []mujica.SelectOption[int]{
|
||||
options := []input.SelectOption[int]{
|
||||
{Value: 1, Label: "Rose"},
|
||||
{Value: 2, Label: "Lily"},
|
||||
{Value: 3, Label: "Iris"},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.MultiSelect(c, &tags, options, mujica.MultiSelectOptions{Max: 2, Label: "Flowers"})
|
||||
core.Use(c, core.Settings{})
|
||||
input.MultiSelect(c, &tags, options, input.MultiSelectOptions{Max: 2, Label: "Flowers"})
|
||||
}`, Keys: "Enter, Space or Down open with the search focused; Down enters the list; Up/Down move; Space checks; Escape closes; Backspace on the field removes the last tag."},
|
||||
{ID: "038", Name: "Combobox", ZhName: "组合框", Category: choiceCategory, Demo: comboboxDemo, Code: `font := ""
|
||||
fonts := []string{"Garamond", "Georgia", "Didot"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.Combobox(c, &font, fonts, mujica.ComboboxOptions{AllowCustom: true, Label: "Font"})
|
||||
core.Use(c, core.Settings{})
|
||||
r := input.Combobox(c, &font, fonts, input.ComboboxOptions{AllowCustom: true, Label: "Font"})
|
||||
if r.QueryChanged() {
|
||||
log.Println("search:", r.Query())
|
||||
}
|
||||
}`, Keys: "Typing filters; Down opens or moves; Up moves; Enter chooses; Escape closes; Tab leaves (keeping typed text with AllowCustom)."},
|
||||
{ID: "039", Name: "Cascader", ZhName: "级联选择", Category: choiceCategory, Demo: cascaderDemo, Code: `var path []string
|
||||
level := func(p []string) mujica.CascaderLevel {
|
||||
level := func(p []string) input.CascaderLevel {
|
||||
if len(p) == 0 {
|
||||
return mujica.CascaderLevel{Nodes: []mujica.CascaderNode{{Value: "fr", Label: "France"}}}
|
||||
return input.CascaderLevel{Nodes: []input.CascaderNode{{Value: "fr", Label: "France"}}}
|
||||
}
|
||||
return mujica.CascaderLevel{Nodes: []mujica.CascaderNode{{Value: "paris", Label: "Paris", Leaf: true}}}
|
||||
return input.CascaderLevel{Nodes: []input.CascaderNode{{Value: "paris", Label: "Paris", Leaf: true}}}
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Cascader(c, &path, mujica.CascaderOptions{Level: level, Label: "City"})
|
||||
core.Use(c, core.Settings{})
|
||||
input.Cascader(c, &path, input.CascaderOptions{Level: level, Label: "City"})
|
||||
}`, Keys: "Enter, Space or Down open; Up/Down move in a column; Right opens a branch; Left goes back; Enter chooses; Escape closes."},
|
||||
{ID: "040", Name: "TreeSelect", ZhName: "树选择", Category: choiceCategory, Demo: treeSelectDemo, Code: `var roles []string
|
||||
nodes := []mujica.TreeSelectNode{
|
||||
{ID: "court", Label: "Court", Children: []mujica.TreeSelectNode{
|
||||
nodes := []input.TreeSelectNode{
|
||||
{ID: "court", Label: "Court", Children: []input.TreeSelectNode{
|
||||
{ID: "king", Label: "King"},
|
||||
{ID: "queen", Label: "Queen"},
|
||||
}},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.TreeSelect(c, &roles, nodes, mujica.TreeSelectOptions{Multiple: true, Label: "Roles"})
|
||||
core.Use(c, core.Settings{})
|
||||
input.TreeSelect(c, &roles, nodes, input.TreeSelectOptions{Multiple: true, Label: "Roles"})
|
||||
}`, Keys: "Enter, Space or Down open with the search focused; Down enters the tree; Up/Down move; Right expands or enters; Left collapses or goes to the parent; Space checks; Escape closes."},
|
||||
}
|
||||
|
||||
// choiceDemo lays out a demo: parameter controls, then the samples.
|
||||
func choiceDemo(c *ui.Context, controls, samples func()) {
|
||||
k := mujica.Tokens(c)
|
||||
k := core.Tokens(c)
|
||||
ui.Column(c).Gap(20).AlignItems(ui.Start).Children(func() {
|
||||
ui.Text(c, "Parameters").Font(theme.SerifFont).FontSize(mujica.FontSize(c, theme.H3Size)).TextColor(k.Text)
|
||||
ui.Text(c, "Parameters").Font(theme.SerifFont).FontSize(core.FontSize(c, theme.H3Size)).TextColor(k.Text)
|
||||
ui.Row(c).Gap(20).Wrap().AlignItems(ui.Center).Children(controls)
|
||||
ui.Box(c).Height(1).FillWidth().Background(k.Ornament)
|
||||
ui.Text(c, "States").Font(theme.SerifFont).FontSize(mujica.FontSize(c, theme.H3Size)).TextColor(k.Text)
|
||||
ui.Text(c, "States").Font(theme.SerifFont).FontSize(core.FontSize(c, theme.H3Size)).TextColor(k.Text)
|
||||
ui.Column(c).Gap(16).AlignItems(ui.Start).Children(samples)
|
||||
})
|
||||
}
|
||||
|
||||
// choiceFlag is a parameter switch that logs its change.
|
||||
func choiceFlag(c *ui.Context, on *bool, label string, log func(string)) {
|
||||
if mujica.Switch(c, on, label, mujica.SwitchOptions{}).Changed() {
|
||||
if input.Switch(c, on, label, input.SwitchOptions{}).Changed() {
|
||||
log(fmt.Sprintf("%s = %v", label, *on))
|
||||
}
|
||||
}
|
||||
@@ -123,7 +124,7 @@ func choiceFlag(c *ui.Context, on *bool, label string, log func(string)) {
|
||||
// choiceLabeled shows a caption above a sample.
|
||||
func choiceLabeled(c *ui.Context, caption string, fn func()) {
|
||||
ui.Column(c).Gap(6).Children(func() {
|
||||
ui.Text(c, caption).FontSize(mujica.FontSize(c, theme.CaptionSize)).TextColor(mujica.Tokens(c).TextMuted)
|
||||
ui.Text(c, caption).FontSize(core.FontSize(c, theme.CaptionSize)).TextColor(core.Tokens(c).TextMuted)
|
||||
fn()
|
||||
})
|
||||
}
|
||||
@@ -136,12 +137,12 @@ func switchDemo(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
choiceFlag(c, &s.loading, "Loading", log)
|
||||
}, func() {
|
||||
if mujica.Switch(c, &s.wifi, "Wi-Fi", mujica.SwitchOptions{Disabled: s.disabled, Loading: s.loading}).Changed() {
|
||||
if input.Switch(c, &s.wifi, "Wi-Fi", input.SwitchOptions{Disabled: s.disabled, Loading: s.loading}).Changed() {
|
||||
log(fmt.Sprintf("Wi-Fi changed: %v", s.wifi))
|
||||
}
|
||||
// A caller applying the change: loading for a moment, then done.
|
||||
at := ui.Local(c.Root(), "031-at", func() time.Time { return time.Time{} })
|
||||
if mujica.Switch(c, &s.pending, "Backup (saves for 1 s)", mujica.SwitchOptions{Loading: !at.IsZero()}).Changed() {
|
||||
if input.Switch(c, &s.pending, "Backup (saves for 1 s)", input.SwitchOptions{Loading: !at.IsZero()}).Changed() {
|
||||
*at = c.Now()
|
||||
log(fmt.Sprintf("Backup requested: %v", s.pending))
|
||||
}
|
||||
@@ -155,8 +156,8 @@ func switchDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
off, on := false, true
|
||||
ui.Row(c).Gap(24).Children(func() {
|
||||
mujica.Switch(c, &off, "Disabled off", mujica.SwitchOptions{Disabled: true})
|
||||
mujica.Switch(c, &on, "Disabled on", mujica.SwitchOptions{Disabled: true})
|
||||
input.Switch(c, &off, "Disabled off", input.SwitchOptions{Disabled: true})
|
||||
input.Switch(c, &on, "Disabled on", input.SwitchOptions{Disabled: true})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -173,12 +174,12 @@ func toggleDemo(c *ui.Context, log func(string)) {
|
||||
on *bool
|
||||
label, ico string
|
||||
}{{&s.bold, "Bold", "bold"}, {&s.italic, "Italic", "italic"}, {&s.underline, "Underline", "underline"}} {
|
||||
if mujica.Toggle(c, t.on, "", mujica.ToggleOptions{Icon: icons.Must(t.ico), Label: t.label, Disabled: s.disabled}).Changed() {
|
||||
if input.Toggle(c, t.on, "", input.ToggleOptions{Icon: icons.Must(t.ico), Label: t.label, Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("%s: %v", t.label, *t.on))
|
||||
}
|
||||
}
|
||||
})
|
||||
if mujica.Toggle(c, &s.bold, "Bold text", mujica.ToggleOptions{Disabled: s.disabled}).Changed() {
|
||||
if input.Toggle(c, &s.bold, "Bold text", input.ToggleOptions{Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("Bold: %v", s.bold))
|
||||
}
|
||||
})
|
||||
@@ -190,12 +191,12 @@ func toggleGroupDemo(c *ui.Context, log func(string)) {
|
||||
multiple, allowEmpty, vertical, disabled bool
|
||||
}
|
||||
s := *ui.Local(c.Root(), "033", func() *state { return &state{value: []string{"left"}} })
|
||||
items := []mujica.ToggleItem[string]{
|
||||
items := []input.ToggleItem[string]{
|
||||
{Value: "left", Label: "Left"}, {Value: "center", Label: "Center"},
|
||||
{Value: "right", Label: "Right"}, {Value: "justify", Label: "Justify", Disabled: true},
|
||||
}
|
||||
choiceDemo(c, func() {
|
||||
if mujica.Switch(c, &s.multiple, "Multiple", mujica.SwitchOptions{}).Changed() {
|
||||
if input.Switch(c, &s.multiple, "Multiple", input.SwitchOptions{}).Changed() {
|
||||
s.value = s.value[:min(len(s.value), 1)]
|
||||
log(fmt.Sprintf("Multiple = %v", s.multiple))
|
||||
}
|
||||
@@ -203,11 +204,11 @@ func toggleGroupDemo(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.vertical, "Vertical", log)
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
opts := mujica.ToggleGroupOptions{Multiple: s.multiple, AllowEmpty: s.allowEmpty, Vertical: s.vertical, Disabled: s.disabled, Label: "Align"}
|
||||
if mujica.ToggleGroup(c, &s.value, items, opts).Changed() {
|
||||
opts := input.ToggleGroupOptions{Multiple: s.multiple, AllowEmpty: s.allowEmpty, Vertical: s.vertical, Disabled: s.disabled, Label: "Align"}
|
||||
if input.ToggleGroup(c, &s.value, items, opts).Changed() {
|
||||
log(fmt.Sprintf("value: %v", s.value))
|
||||
}
|
||||
ui.Text(c, fmt.Sprintf("Value: %v", s.value)).TextColor(mujica.Tokens(c).TextMuted)
|
||||
ui.Text(c, fmt.Sprintf("Value: %v", s.value)).TextColor(core.Tokens(c).TextMuted)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -222,24 +223,24 @@ func sliderDemo(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.showValue, "ShowValue", log)
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
choiceLabeled(c, fmt.Sprintf("Step %v", s.step), func() {
|
||||
if mujica.Slider(c, &s.step, mujica.SliderOptions{Max: 25, Step: 5, Label: "Step"}).Changed() {
|
||||
if input.Slider(c, &s.step, input.SliderOptions{Max: 25, Step: 5, Label: "Step"}).Changed() {
|
||||
log(fmt.Sprintf("Step = %v", s.step))
|
||||
}
|
||||
})
|
||||
}, func() {
|
||||
var marks []mujica.SliderMark
|
||||
var marks []input.SliderMark
|
||||
if s.marks {
|
||||
for v := 0.0; v <= 100; v += 25 {
|
||||
marks = append(marks, mujica.SliderMark{Value: v, Label: fmt.Sprint(v)})
|
||||
marks = append(marks, input.SliderMark{Value: v, Label: fmt.Sprint(v)})
|
||||
}
|
||||
}
|
||||
ui.Column(c).Width(360).Children(func() {
|
||||
opts := mujica.SliderOptions{Step: s.step, Marks: marks, ShowValue: s.showValue, Disabled: s.disabled, Label: "Volume"}
|
||||
if mujica.Slider(c, &s.value, opts).Changed() {
|
||||
opts := input.SliderOptions{Step: s.step, Marks: marks, ShowValue: s.showValue, Disabled: s.disabled, Label: "Volume"}
|
||||
if input.Slider(c, &s.value, opts).Changed() {
|
||||
log(fmt.Sprintf("Volume: %v", s.value))
|
||||
}
|
||||
})
|
||||
ui.Text(c, fmt.Sprintf("Value: %v", s.value)).TextColor(mujica.Tokens(c).TextMuted)
|
||||
ui.Text(c, fmt.Sprintf("Value: %v", s.value)).TextColor(core.Tokens(c).TextMuted)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -254,18 +255,18 @@ func rangeSliderDemo(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.showValue, "ShowValue", log)
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
var marks []mujica.SliderMark
|
||||
var marks []input.SliderMark
|
||||
if s.marks {
|
||||
marks = []mujica.SliderMark{{Value: 0, Label: "£0"}, {Value: 500, Label: "£500"}, {Value: 1000, Label: "£1000"}}
|
||||
marks = []input.SliderMark{{Value: 0, Label: "£0"}, {Value: 500, Label: "£500"}, {Value: 1000, Label: "£1000"}}
|
||||
}
|
||||
ui.Column(c).Width(360).Children(func() {
|
||||
opts := mujica.RangeSliderOptions{Max: 1000, Step: 50, Marks: marks, ShowValue: s.showValue, Disabled: s.disabled, Label: "Price",
|
||||
opts := input.RangeSliderOptions{Max: 1000, Step: 50, Marks: marks, ShowValue: s.showValue, Disabled: s.disabled, Label: "Price",
|
||||
Format: func(v float64) string { return fmt.Sprintf("£%.0f", v) }}
|
||||
if mujica.RangeSlider(c, &s.low, &s.high, opts).Changed() {
|
||||
if input.RangeSlider(c, &s.low, &s.high, opts).Changed() {
|
||||
log(fmt.Sprintf("Price: %v–%v", s.low, s.high))
|
||||
}
|
||||
})
|
||||
ui.Text(c, fmt.Sprintf("Range: £%.0f – £%.0f", s.low, s.high)).TextColor(mujica.Tokens(c).TextMuted)
|
||||
ui.Text(c, fmt.Sprintf("Range: £%.0f – £%.0f", s.low, s.high)).TextColor(core.Tokens(c).TextMuted)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -281,7 +282,7 @@ func choiceDue(c *ui.Context, at time.Time) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
var choiceFruits = []mujica.SelectOption[string]{
|
||||
var choiceFruits = []input.SelectOption[string]{
|
||||
{Value: "apple", Label: "Apple"}, {Value: "banana", Label: "Banana"}, {Value: "blueberry", Label: "Blueberry"},
|
||||
{Value: "cherry", Label: "Cherry", Disabled: true}, {Value: "date", Label: "Date"}, {Value: "fig", Label: "Fig"},
|
||||
}
|
||||
@@ -302,12 +303,12 @@ func selectDemo(c *ui.Context, log func(string)) {
|
||||
options = nil
|
||||
}
|
||||
choiceLabeled(c, "Fruit", func() {
|
||||
if mujica.Select(c, &s.value, options, mujica.SelectOptions{Clearable: s.clearable, Disabled: s.disabled, Label: "Fruit"}).Changed() {
|
||||
if input.Select(c, &s.value, options, input.SelectOptions{Clearable: s.clearable, Disabled: s.disabled, Label: "Fruit"}).Changed() {
|
||||
log(fmt.Sprintf("Fruit: %q", s.value))
|
||||
}
|
||||
})
|
||||
choiceLabeled(c, "Placeholder", func() {
|
||||
if mujica.Select(c, &s.picked, choiceFruits, mujica.SelectOptions{Placeholder: "Pick a fruit", Clearable: true}).Changed() {
|
||||
if input.Select(c, &s.picked, choiceFruits, input.SelectOptions{Placeholder: "Pick a fruit", Clearable: true}).Changed() {
|
||||
log(fmt.Sprintf("Picked: %q", s.picked))
|
||||
}
|
||||
})
|
||||
@@ -329,7 +330,7 @@ func multiSelectDemo(c *ui.Context, log func(string)) {
|
||||
limit = 3
|
||||
}
|
||||
choiceLabeled(c, "Fruits", func() {
|
||||
r := mujica.MultiSelect(c, &s.values, choiceFruits, mujica.MultiSelectOptions{Max: limit, MaxTags: 2, Disabled: s.disable, Label: "Fruits"})
|
||||
r := input.MultiSelect(c, &s.values, choiceFruits, input.MultiSelectOptions{Max: limit, MaxTags: 2, Disabled: s.disable, Label: "Fruits"})
|
||||
r.Element.Width(320)
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("Fruits: %v", s.values))
|
||||
@@ -368,7 +369,7 @@ func comboboxDemo(c *ui.Context, log func(string)) {
|
||||
options, loading = s.results, s.asked != s.query
|
||||
}
|
||||
choiceLabeled(c, "Typeface", func() {
|
||||
r := mujica.Combobox(c, &s.value, options, mujica.ComboboxOptions{AllowCustom: s.custom, ExternalFilter: s.remote, Loading: loading, Label: "Typeface"})
|
||||
r := input.Combobox(c, &s.value, options, input.ComboboxOptions{AllowCustom: s.custom, ExternalFilter: s.remote, Loading: loading, Label: "Typeface"})
|
||||
if r.QueryChanged() {
|
||||
s.query, s.at = r.Query(), c.Now()
|
||||
log(fmt.Sprintf("query: %q", s.query))
|
||||
@@ -379,13 +380,13 @@ func comboboxDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
disabled := "Didot"
|
||||
choiceLabeled(c, "Disabled", func() {
|
||||
mujica.Combobox(c, &disabled, choiceFonts, mujica.ComboboxOptions{Disabled: true})
|
||||
input.Combobox(c, &disabled, choiceFonts, input.ComboboxOptions{Disabled: true})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// choiceAtlas is the cascader demo's pretend server data.
|
||||
var choiceAtlas = map[string][]mujica.CascaderNode{
|
||||
var choiceAtlas = map[string][]input.CascaderNode{
|
||||
"": {{Value: "eu", Label: "Europe"}, {Value: "as", Label: "Asia"}, {Value: "aq", Label: "Antarctica", Disabled: true}},
|
||||
"eu": {{Value: "fr", Label: "France"}, {Value: "it", Label: "Italy"}},
|
||||
"as": {{Value: "jp", Label: "Japan"}, {Value: "cn", Label: "China"}},
|
||||
@@ -398,34 +399,34 @@ var choiceAtlas = map[string][]mujica.CascaderNode{
|
||||
func cascaderDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
path []string
|
||||
levels map[string]mujica.CascaderLevel
|
||||
levels map[string]input.CascaderLevel
|
||||
pending map[string]time.Time
|
||||
anyLevel, fail, ds bool
|
||||
}
|
||||
s := *ui.Local(c.Root(), "039", func() *state {
|
||||
return &state{levels: map[string]mujica.CascaderLevel{"": {Nodes: choiceAtlas[""]}}, pending: map[string]time.Time{}}
|
||||
return &state{levels: map[string]input.CascaderLevel{"": {Nodes: choiceAtlas[""]}}, pending: map[string]time.Time{}}
|
||||
})
|
||||
for key, at := range s.pending {
|
||||
if choiceDue(c, at) {
|
||||
delete(s.pending, key)
|
||||
if s.fail {
|
||||
s.levels[key] = mujica.CascaderLevel{Status: mujica.CascaderFailed, Err: errors.New("server busy")}
|
||||
s.levels[key] = input.CascaderLevel{Status: input.CascaderFailed, Err: errors.New("server busy")}
|
||||
log(fmt.Sprintf("load %q failed", key))
|
||||
continue
|
||||
}
|
||||
s.levels[key] = mujica.CascaderLevel{Nodes: choiceAtlas[key]}
|
||||
s.levels[key] = input.CascaderLevel{Nodes: choiceAtlas[key]}
|
||||
log(fmt.Sprintf("loaded %q", key))
|
||||
}
|
||||
}
|
||||
level := func(p []string) mujica.CascaderLevel {
|
||||
level := func(p []string) input.CascaderLevel {
|
||||
if lv, ok := s.levels[strings.Join(p, "/")]; ok {
|
||||
return lv
|
||||
}
|
||||
return mujica.CascaderLevel{Status: mujica.CascaderIdle}
|
||||
return input.CascaderLevel{Status: input.CascaderIdle}
|
||||
}
|
||||
load := func(p []string) {
|
||||
key := strings.Join(p, "/")
|
||||
s.levels[key] = mujica.CascaderLevel{Status: mujica.CascaderLoading}
|
||||
s.levels[key] = input.CascaderLevel{Status: input.CascaderLoading}
|
||||
s.pending[key] = c.Now()
|
||||
log(fmt.Sprintf("load %q", key))
|
||||
}
|
||||
@@ -435,8 +436,8 @@ func cascaderDemo(c *ui.Context, log func(string)) {
|
||||
choiceFlag(c, &s.ds, "Disabled", log)
|
||||
}, func() {
|
||||
choiceLabeled(c, "City (children load on demand)", func() {
|
||||
opts := mujica.CascaderOptions{Level: level, Load: load, AnyLevel: s.anyLevel, Disabled: s.ds, Label: "City"}
|
||||
r := mujica.Cascader(c, &s.path, opts)
|
||||
opts := input.CascaderOptions{Level: level, Load: load, AnyLevel: s.anyLevel, Disabled: s.ds, Label: "City"}
|
||||
r := input.Cascader(c, &s.path, opts)
|
||||
r.Element.Width(320)
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("path: %v", s.path))
|
||||
@@ -445,12 +446,12 @@ func cascaderDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
}
|
||||
|
||||
var choiceRoles = []mujica.TreeSelectNode{
|
||||
{ID: "court", Label: "Court", Children: []mujica.TreeSelectNode{
|
||||
var choiceRoles = []input.TreeSelectNode{
|
||||
{ID: "court", Label: "Court", Children: []input.TreeSelectNode{
|
||||
{ID: "king", Label: "King"}, {ID: "queen", Label: "Queen"}, {ID: "jester", Label: "Jester", Disabled: true},
|
||||
{ID: "guard", Label: "Royal guard", Children: []mujica.TreeSelectNode{{ID: "captain", Label: "Captain"}, {ID: "archer", Label: "Archer"}}},
|
||||
{ID: "guard", Label: "Royal guard", Children: []input.TreeSelectNode{{ID: "captain", Label: "Captain"}, {ID: "archer", Label: "Archer"}}},
|
||||
}},
|
||||
{ID: "church", Label: "Church", Children: []mujica.TreeSelectNode{{ID: "bishop", Label: "Bishop"}, {ID: "monk", Label: "Monk"}}},
|
||||
{ID: "church", Label: "Church", Children: []input.TreeSelectNode{{ID: "bishop", Label: "Bishop"}, {ID: "monk", Label: "Monk"}}},
|
||||
{ID: "merchant", Label: "Merchant"},
|
||||
}
|
||||
|
||||
@@ -461,20 +462,20 @@ func treeSelectDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
s := *ui.Local(c.Root(), "040", func() *state { return &state{sel: []string{"queen", "bishop"}, multiple: true} })
|
||||
choiceDemo(c, func() {
|
||||
if mujica.Switch(c, &s.multiple, "Multiple", mujica.SwitchOptions{}).Changed() {
|
||||
if input.Switch(c, &s.multiple, "Multiple", input.SwitchOptions{}).Changed() {
|
||||
s.sel = s.sel[:min(len(s.sel), 1)]
|
||||
log(fmt.Sprintf("Multiple = %v", s.multiple))
|
||||
}
|
||||
choiceFlag(c, &s.independent, "Independent rule", log)
|
||||
choiceFlag(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
rule := mujica.TreeSelectCascade
|
||||
rule := input.TreeSelectCascade
|
||||
if s.independent {
|
||||
rule = mujica.TreeSelectIndependent
|
||||
rule = input.TreeSelectIndependent
|
||||
}
|
||||
choiceLabeled(c, "Roles", func() {
|
||||
opts := mujica.TreeSelectOptions{Multiple: s.multiple, Rule: rule, MaxTags: 2, Disabled: s.disabled, Label: "Roles"}
|
||||
r := mujica.TreeSelect(c, &s.sel, choiceRoles, opts)
|
||||
opts := input.TreeSelectOptions{Multiple: s.multiple, Rule: rule, MaxTags: 2, Disabled: s.disabled, Label: "Roles"}
|
||||
r := input.TreeSelect(c, &s.sel, choiceRoles, opts)
|
||||
r.Element.Width(320)
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("Roles: %v", s.sel))
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
)
|
||||
|
||||
// choiceDemoTester runs the demo of page id at text scale and returns its tester and event log.
|
||||
@@ -18,7 +18,7 @@ func choiceDemoTester(t *testing.T, id string, scale float32) (*ui.Tester, *[]st
|
||||
}
|
||||
events := &[]string{}
|
||||
tt := ui.NewTester(func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{Mode: mujica.Light})
|
||||
core.Use(c, core.Settings{Mode: core.Light})
|
||||
ui.Scroll(c).Fill().Padding(24).Children(func() {
|
||||
choicePages[i].Demo(c, func(s string) { *events = append(*events, s) })
|
||||
})
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/code"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
const codeCategory = "Terminal & Code"
|
||||
@@ -57,7 +59,7 @@ var codePages = []page{
|
||||
|
||||
// codeDemoCheck is a switch that logs its changes.
|
||||
func codeDemoCheck(c *ui.Context, v *bool, label string, log func(string)) {
|
||||
if mujica.Switch(c, v, label, mujica.SwitchOptions{}).Changed() {
|
||||
if input.Switch(c, v, label, input.SwitchOptions{}).Changed() {
|
||||
log(fmt.Sprintf("%s: %v", label, *v))
|
||||
}
|
||||
}
|
||||
@@ -68,7 +70,7 @@ func codeDemoControls(c *ui.Context, fn func()) {
|
||||
|
||||
func codeTerminalDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
term mujica.TerminalState
|
||||
term code.TerminalState
|
||||
line string
|
||||
disabled bool
|
||||
}
|
||||
@@ -80,13 +82,13 @@ func codeTerminalDemo(c *ui.Context, log func(string)) {
|
||||
st := *s
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
if mujica.Button(c, "Clear screen", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Clear screen", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
st.term.Write("\x1b[2J\x1b[H$ ")
|
||||
log("clear screen")
|
||||
}
|
||||
})
|
||||
// A tiny echo shell stands in for the PTY.
|
||||
mujica.Terminal(c, &st.term, mujica.TerminalOptions{Disabled: st.disabled,
|
||||
code.Terminal(c, &st.term, code.TerminalOptions{Disabled: st.disabled,
|
||||
Input: func(data string) {
|
||||
log(fmt.Sprintf("input %q", data))
|
||||
for _, r := range data {
|
||||
@@ -128,27 +130,27 @@ func codeShell(line string) string {
|
||||
|
||||
func codeTerminalTabsDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
tabs []mujica.TerminalTab
|
||||
tabs []code.TerminalTab
|
||||
active string
|
||||
next int
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "322", func() *state {
|
||||
return &state{tabs: []mujica.TerminalTab{{ID: "1", Title: "zsh"}, {ID: "2", Title: "build", Busy: true}, {ID: "3", Title: "logs"}}, active: "1", next: 4}
|
||||
return &state{tabs: []code.TerminalTab{{ID: "1", Title: "zsh"}, {ID: "2", Title: "build", Busy: true}, {ID: "3", Title: "logs"}}, active: "1", next: 4}
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
if mujica.Button(c, "New terminal", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "New terminal", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
id := fmt.Sprint(st.next)
|
||||
st.next++
|
||||
st.tabs = append(st.tabs, mujica.TerminalTab{ID: id, Title: "shell " + id})
|
||||
st.tabs = append(st.tabs, code.TerminalTab{ID: id, Title: "shell " + id})
|
||||
st.active = id
|
||||
log("new " + id)
|
||||
}
|
||||
})
|
||||
r := mujica.TerminalTabs(c, &st.tabs, &st.active, mujica.TerminalTabsOptions{Disabled: st.disabled}, func(id string) {
|
||||
ui.Text(c, "Terminal "+id+" content").TextColor(mujica.Tokens(c).TextMuted)
|
||||
r := code.TerminalTabs(c, &st.tabs, &st.active, code.TerminalTabsOptions{Disabled: st.disabled}, func(id string) {
|
||||
ui.Text(c, "Terminal "+id+" content").TextColor(core.Tokens(c).TextMuted)
|
||||
})
|
||||
r.Element.Width(560)
|
||||
if r.Changed {
|
||||
@@ -164,8 +166,8 @@ func codeTerminalTabsDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func codeTerminalSearchDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
term mujica.TerminalState
|
||||
search mujica.TerminalSearchState
|
||||
term code.TerminalState
|
||||
search code.TerminalSearchState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "323", func() *state {
|
||||
@@ -182,62 +184,62 @@ func codeTerminalSearchDemo(c *ui.Context, log func(string)) {
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
ui.Column(c).Width(600).Gap(8).Children(func() {
|
||||
if mujica.TerminalSearch(c, &st.term, &st.search, mujica.TerminalSearchOptions{Disabled: st.disabled}).Changed() {
|
||||
if code.TerminalSearch(c, &st.term, &st.search, code.TerminalSearchOptions{Disabled: st.disabled}).Changed() {
|
||||
log(fmt.Sprintf("query %q, match %d", st.search.Query, st.search.Current+1))
|
||||
}
|
||||
mujica.Terminal(c, &st.term, mujica.TerminalOptions{}).Height(220)
|
||||
code.Terminal(c, &st.term, code.TerminalOptions{}).Height(220)
|
||||
})
|
||||
}
|
||||
|
||||
func codeCommandHistoryDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
entries []mujica.HistoryEntry
|
||||
s mujica.CommandHistoryState
|
||||
entries []code.HistoryEntry
|
||||
s code.CommandHistoryState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "324", func() *state {
|
||||
at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
var e []mujica.HistoryEntry
|
||||
var e []code.HistoryEntry
|
||||
for i, cmd := range []string{"git pull", "go build ./...", "go test ./...", "make seal", "ls -la", "git status"} {
|
||||
e = append(e, mujica.HistoryEntry{Command: cmd, Time: at.Add(time.Duration(i) * time.Minute)})
|
||||
e = append(e, code.HistoryEntry{Command: cmd, Time: at.Add(time.Duration(i) * time.Minute)})
|
||||
}
|
||||
e[3].ExitCode = 2
|
||||
return &state{entries: e}
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
r := mujica.CommandHistory(c, st.entries, &st.s, mujica.CommandHistoryOptions{Disabled: st.disabled})
|
||||
r := code.CommandHistory(c, st.entries, &st.s, code.CommandHistoryOptions{Disabled: st.disabled})
|
||||
r.Element.Size(480, 300)
|
||||
if r.Replayed != nil {
|
||||
log("replay " + r.Replayed.Command)
|
||||
st.entries = append(st.entries, mujica.HistoryEntry{Command: r.Replayed.Command, Time: time.Now()})
|
||||
st.entries = append(st.entries, code.HistoryEntry{Command: r.Replayed.Command, Time: time.Now()})
|
||||
}
|
||||
}
|
||||
|
||||
func codeLogViewerDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
lines []mujica.LogLine
|
||||
s mujica.LogViewerState
|
||||
lines []code.LogLine
|
||||
s code.LogViewerState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "325", func() *state {
|
||||
at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
lines := make([]mujica.LogLine, 100_000)
|
||||
lines := make([]code.LogLine, 100_000)
|
||||
words := []string{"ledger synced", "seal applied", "herald dispatched", "archive rotated"}
|
||||
for i := range lines {
|
||||
lines[i] = mujica.LogLine{Time: at.Add(time.Duration(i) * time.Second), Level: mujica.LogLevel(i % 7 % 4), Text: fmt.Sprintf("%s #%d", words[i%4], i)}
|
||||
lines[i] = code.LogLine{Time: at.Add(time.Duration(i) * time.Second), Level: code.LogLevel(i % 7 % 4), Text: fmt.Sprintf("%s #%d", words[i%4], i)}
|
||||
}
|
||||
return &state{lines: lines}
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
if mujica.Button(c, "Append line", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
st.lines = append(st.lines, mujica.LogLine{Time: time.Now(), Level: mujica.LogWarn, Text: fmt.Sprintf("late seal #%d", len(st.lines))})
|
||||
if input.Button(c, "Append line", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
st.lines = append(st.lines, code.LogLine{Time: time.Now(), Level: code.LogWarn, Text: fmt.Sprintf("late seal #%d", len(st.lines))})
|
||||
log("append")
|
||||
}
|
||||
})
|
||||
r := mujica.LogViewer(c, st.lines, &st.s, mujica.LogViewerOptions{Disabled: st.disabled})
|
||||
r := code.LogViewer(c, st.lines, &st.s, code.LogViewerOptions{Disabled: st.disabled})
|
||||
r.Element.Size(720, 360)
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("hidden %v, follow %v", st.s.Hidden, st.s.Follow))
|
||||
|
||||
@@ -6,12 +6,15 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/code"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func codeSymbolDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
s mujica.SymbolOutlineState
|
||||
s code.SymbolOutlineState
|
||||
cursor float64
|
||||
disabled bool
|
||||
}
|
||||
@@ -20,19 +23,19 @@ func codeSymbolDemo(c *ui.Context, log func(string)) {
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
ui.Text(c, fmt.Sprintf("Cursor line %.0f", st.cursor))
|
||||
if mujica.Slider(c, &st.cursor, mujica.SliderOptions{Min: 1, Max: 34, Step: 1}).Changed() {
|
||||
if input.Slider(c, &st.cursor, input.SliderOptions{Min: 1, Max: 34, Step: 1}).Changed() {
|
||||
log(fmt.Sprintf("cursor %.0f", st.cursor))
|
||||
}
|
||||
})
|
||||
symbols := []mujica.Symbol{
|
||||
{Name: "Decree", Kind: mujica.SymbolType, Line: 6, EndLine: 9, Detail: "struct", Children: []mujica.Symbol{
|
||||
{Name: "ID", Kind: mujica.SymbolField, Line: 7, EndLine: 7, Detail: "int"},
|
||||
{Name: "Title", Kind: mujica.SymbolField, Line: 8, EndLine: 8, Detail: "string"},
|
||||
symbols := []code.Symbol{
|
||||
{Name: "Decree", Kind: code.SymbolType, Line: 6, EndLine: 9, Detail: "struct", Children: []code.Symbol{
|
||||
{Name: "ID", Kind: code.SymbolField, Line: 7, EndLine: 7, Detail: "int"},
|
||||
{Name: "Title", Kind: code.SymbolField, Line: 8, EndLine: 8, Detail: "string"},
|
||||
}},
|
||||
{Name: "Seal", Kind: mujica.SymbolFunction, Line: 13, EndLine: 21, Detail: "(d Decree) (string, error)"},
|
||||
{Name: "Herald", Kind: mujica.SymbolFunction, Line: 23, EndLine: 31, Detail: "(ds []Decree) int"},
|
||||
{Name: "Seal", Kind: code.SymbolFunction, Line: 13, EndLine: 21, Detail: "(d Decree) (string, error)"},
|
||||
{Name: "Herald", Kind: code.SymbolFunction, Line: 23, EndLine: 31, Detail: "(ds []Decree) int"},
|
||||
}
|
||||
r := mujica.SymbolOutline(c, symbols, &st.s, mujica.SymbolOutlineOptions{CursorLine: int(st.cursor), Disabled: st.disabled})
|
||||
r := code.SymbolOutline(c, symbols, &st.s, code.SymbolOutlineOptions{CursorLine: int(st.cursor), Disabled: st.disabled})
|
||||
r.Element.Size(420, 320)
|
||||
if r.Jumped != nil {
|
||||
log(fmt.Sprintf("jump to %s, line %d", r.Jumped.Name, r.Jumped.Line))
|
||||
@@ -43,33 +46,33 @@ func codeCompletionDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
text string
|
||||
open bool
|
||||
s mujica.CompletionMenuState
|
||||
s code.CompletionMenuState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "334", func() *state { return &state{} })
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
all := []mujica.Completion{
|
||||
{Label: "Seal", Kind: mujica.SymbolFunction, Detail: "func(d Decree) (string, error)", Doc: "Seal stamps a decree with the royal wax."},
|
||||
{Label: "Search", Kind: mujica.SymbolFunction, Detail: "func(q string) []Decree"},
|
||||
{Label: "Season", Kind: mujica.SymbolConstant, Detail: "= 4"},
|
||||
{Label: "Herald", Kind: mujica.SymbolFunction, Detail: "func(ds []Decree) int"},
|
||||
{Label: "Decree", Kind: mujica.SymbolType, Detail: "struct", Doc: "Decree is an order of the court."},
|
||||
all := []code.Completion{
|
||||
{Label: "Seal", Kind: code.SymbolFunction, Detail: "func(d Decree) (string, error)", Doc: "Seal stamps a decree with the royal wax."},
|
||||
{Label: "Search", Kind: code.SymbolFunction, Detail: "func(q string) []Decree"},
|
||||
{Label: "Season", Kind: code.SymbolConstant, Detail: "= 4"},
|
||||
{Label: "Herald", Kind: code.SymbolFunction, Detail: "func(ds []Decree) int"},
|
||||
{Label: "Decree", Kind: code.SymbolType, Detail: "struct", Doc: "Decree is an order of the court."},
|
||||
}
|
||||
word := st.text[strings.LastIndexAny(st.text, " .(")+1:]
|
||||
var items []mujica.Completion
|
||||
var items []code.Completion
|
||||
for _, it := range all {
|
||||
if word != "" && strings.HasPrefix(strings.ToLower(it.Label), strings.ToLower(word)) {
|
||||
items = append(items, it)
|
||||
}
|
||||
}
|
||||
ui.Column(c).Width(420).Gap(8).Children(func() {
|
||||
ui.Text(c, "Type “se” or “de” to see suggestions.").TextColor(mujica.Tokens(c).TextMuted)
|
||||
in := mujica.TextInput(c, &st.text, mujica.TextInputOptions{Label: "Code", Placeholder: "court.", Disabled: st.disabled})
|
||||
ui.Text(c, "Type “se” or “de” to see suggestions.").TextColor(core.Tokens(c).TextMuted)
|
||||
in := input.TextInput(c, &st.text, input.TextInputOptions{Label: "Code", Placeholder: "court.", Disabled: st.disabled})
|
||||
if in.Changed() {
|
||||
st.open = len(items) > 0
|
||||
}
|
||||
r := mujica.CompletionMenu(c, in.Input, &st.open, items, &st.s, mujica.CompletionMenuOptions{Disabled: st.disabled})
|
||||
r := code.CompletionMenu(c, in.Input, &st.open, items, &st.s, code.CompletionMenuOptions{Disabled: st.disabled})
|
||||
if r.Accepted != nil {
|
||||
st.text = st.text[:len(st.text)-len(word)] + r.Accepted.Label
|
||||
log("accept " + r.Accepted.Label)
|
||||
@@ -79,37 +82,37 @@ func codeCompletionDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func codeDebugDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
dbg mujica.DebugState
|
||||
dbg code.DebugState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "335", func() *state { return &state{dbg: mujica.DebugPaused} })
|
||||
s := ui.Local(c.Root(), "335", func() *state { return &state{dbg: code.DebugPaused} })
|
||||
st := *s
|
||||
names := []string{"Stopped", "Running", "Paused"}
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
ui.Text(c, "State: "+names[st.dbg])
|
||||
})
|
||||
a := mujica.DebugToolbar(c, st.dbg, mujica.DebugToolbarOptions{Disabled: st.disabled}).Action
|
||||
a := code.DebugToolbar(c, st.dbg, code.DebugToolbarOptions{Disabled: st.disabled}).Action
|
||||
switch a {
|
||||
case mujica.DebugNone:
|
||||
case code.DebugNone:
|
||||
return
|
||||
case mujica.DebugContinue, mujica.DebugRestart:
|
||||
st.dbg = mujica.DebugRunning
|
||||
case mujica.DebugPause, mujica.DebugStepOver, mujica.DebugStepInto, mujica.DebugStepOut:
|
||||
st.dbg = mujica.DebugPaused
|
||||
case mujica.DebugStop:
|
||||
st.dbg = mujica.DebugStopped
|
||||
case code.DebugContinue, code.DebugRestart:
|
||||
st.dbg = code.DebugRunning
|
||||
case code.DebugPause, code.DebugStepOver, code.DebugStepInto, code.DebugStepOut:
|
||||
st.dbg = code.DebugPaused
|
||||
case code.DebugStop:
|
||||
st.dbg = code.DebugStopped
|
||||
}
|
||||
log(fmt.Sprintf("action %d, now %s", a, names[st.dbg]))
|
||||
}
|
||||
|
||||
func codeBreakpointDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
bps []mujica.Breakpoint
|
||||
bps []code.Breakpoint
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "336", func() *state {
|
||||
return &state{bps: []mujica.Breakpoint{
|
||||
return &state{bps: []code.Breakpoint{
|
||||
{ID: "1", File: "court/seal.go", Line: 14, Enabled: true},
|
||||
{ID: "2", File: "court/ledger.go", Line: 40, Condition: "n > 3"},
|
||||
{ID: "3", File: "main.go", Line: 9, Enabled: true},
|
||||
@@ -117,7 +120,7 @@ func codeBreakpointDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
r := mujica.BreakpointList(c, &st.bps, mujica.BreakpointListOptions{Disabled: st.disabled})
|
||||
r := code.BreakpointList(c, &st.bps, code.BreakpointListOptions{Disabled: st.disabled})
|
||||
r.Element.Size(480, 240)
|
||||
for _, e := range []struct{ what, id string }{{"toggled", r.Toggled}, {"condition", r.Edited}, {"open", r.Opened}} {
|
||||
if e.id != "" {
|
||||
@@ -128,21 +131,21 @@ func codeBreakpointDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func codeCallStackDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
s mujica.CallStackState
|
||||
s code.CallStackState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "337", func() *state { return &state{s: mujica.CallStackState{Current: "f1"}} })
|
||||
s := ui.Local(c.Root(), "337", func() *state { return &state{s: code.CallStackState{Current: "f1"}} })
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
threads := []mujica.DebugThread{
|
||||
{ID: "t1", Name: "main", Frames: []mujica.StackFrame{
|
||||
threads := []code.DebugThread{
|
||||
{ID: "t1", Name: "main", Frames: []code.StackFrame{
|
||||
{ID: "f1", Name: "court.Seal", File: "court/seal.go", Line: 14},
|
||||
{ID: "f2", Name: "court.Herald", File: "court/seal.go", Line: 27},
|
||||
{ID: "f3", Name: "main.main", File: "main.go", Line: 9},
|
||||
}},
|
||||
{ID: "t2", Name: "ledger writer", Frames: []mujica.StackFrame{{ID: "f4", Name: "ledger.Flush", File: "ledger/write.go", Line: 51}}},
|
||||
{ID: "t2", Name: "ledger writer", Frames: []code.StackFrame{{ID: "f4", Name: "ledger.Flush", File: "ledger/write.go", Line: 51}}},
|
||||
}
|
||||
r := mujica.CallStack(c, threads, &st.s, mujica.CallStackOptions{Disabled: st.disabled})
|
||||
r := code.CallStack(c, threads, &st.s, code.CallStackOptions{Disabled: st.disabled})
|
||||
r.Element.Size(440, 300)
|
||||
if r.Changed() {
|
||||
log("frame " + st.s.Current)
|
||||
@@ -151,13 +154,13 @@ func codeCallStackDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func codeVariablesDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
vars []mujica.Variable
|
||||
s mujica.VariablesPanelState
|
||||
vars []code.Variable
|
||||
s code.VariablesPanelState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "338", func() *state {
|
||||
return &state{vars: []mujica.Variable{
|
||||
{Name: "d", Type: "court.Decree", Value: "{…}", Children: []mujica.Variable{
|
||||
return &state{vars: []code.Variable{
|
||||
{Name: "d", Type: "court.Decree", Value: "{…}", Children: []code.Variable{
|
||||
{Name: "ID", Value: "42", Type: "int", Changed: true},
|
||||
{Name: "Title", Value: `"Of seals"`, Type: "string"},
|
||||
}},
|
||||
@@ -167,7 +170,7 @@ func codeVariablesDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
r := mujica.VariablesPanel(c, st.vars, &st.s, mujica.VariablesPanelOptions{Disabled: st.disabled})
|
||||
r := code.VariablesPanel(c, st.vars, &st.s, code.VariablesPanelOptions{Disabled: st.disabled})
|
||||
r.Element.Size(440, 260)
|
||||
if r.Edited != nil {
|
||||
log(fmt.Sprintf("set %s = %s", r.Edited.Path, r.Edited.Value))
|
||||
@@ -176,7 +179,7 @@ func codeVariablesDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
// codeDemoSet writes value at the dotted path.
|
||||
func codeDemoSet(vars []mujica.Variable, path []string, value string) {
|
||||
func codeDemoSet(vars []code.Variable, path []string, value string) {
|
||||
for i := range vars {
|
||||
if vars[i].Name != path[0] {
|
||||
continue
|
||||
@@ -191,14 +194,14 @@ func codeDemoSet(vars []mujica.Variable, path []string, value string) {
|
||||
|
||||
func codeHexDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
s mujica.HexViewerState
|
||||
s code.HexViewerState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "339", func() *state { return &state{} })
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
data := []byte(strings.Join(codeDemoSource, "\n"))
|
||||
r := mujica.HexViewer(c, data, &st.s, mujica.HexViewerOptions{Disabled: st.disabled})
|
||||
r := code.HexViewer(c, data, &st.s, code.HexViewerOptions{Disabled: st.disabled})
|
||||
r.Element.Size(720, 300)
|
||||
if r.Changed() {
|
||||
a, b := st.s.Selection()
|
||||
@@ -208,7 +211,7 @@ func codeHexDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func codeFlamegraphDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
s mujica.FlamegraphState
|
||||
s code.FlamegraphState
|
||||
status string
|
||||
disabled bool
|
||||
}
|
||||
@@ -216,22 +219,22 @@ func codeFlamegraphDemo(c *ui.Context, log func(string)) {
|
||||
st := *s
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
opts := []mujica.SelectOption[string]{{Value: "Ready", Label: "Ready"}, {Value: "Loading", Label: "Loading"}, {Value: "Failed", Label: "Failed"}}
|
||||
if mujica.Select(c, &st.status, opts, mujica.SelectOptions{Label: "Status"}).Changed() {
|
||||
opts := []input.SelectOption[string]{{Value: "Ready", Label: "Ready"}, {Value: "Loading", Label: "Loading"}, {Value: "Failed", Label: "Failed"}}
|
||||
if input.Select(c, &st.status, opts, input.SelectOptions{Label: "Status"}).Changed() {
|
||||
log("status " + st.status)
|
||||
}
|
||||
})
|
||||
root := &mujica.FlameNode{Name: "main", Value: 240, Children: []mujica.FlameNode{
|
||||
{Name: "court.Herald", Value: 150, Children: []mujica.FlameNode{
|
||||
{Name: "court.Seal", Value: 110, Children: []mujica.FlameNode{{Name: "wax.Melt", Value: 70}, {Name: "ink.Dry", Value: 25}}},
|
||||
root := &code.FlameNode{Name: "main", Value: 240, Children: []code.FlameNode{
|
||||
{Name: "court.Herald", Value: 150, Children: []code.FlameNode{
|
||||
{Name: "court.Seal", Value: 110, Children: []code.FlameNode{{Name: "wax.Melt", Value: 70}, {Name: "ink.Dry", Value: 25}}},
|
||||
{Name: "ledger.Write", Value: 30},
|
||||
}},
|
||||
{Name: "archive.Load", Value: 60, Children: []mujica.FlameNode{{Name: "json.Decode", Value: 45}}},
|
||||
{Name: "archive.Load", Value: 60, Children: []code.FlameNode{{Name: "json.Decode", Value: 45}}},
|
||||
{Name: "gc", Value: 20},
|
||||
}}
|
||||
status := map[string]mujica.DataStatus{"Ready": mujica.DataReady, "Loading": mujica.DataLoading, "Failed": mujica.DataFailed}[st.status]
|
||||
status := map[string]data.DataStatus{"Ready": data.DataReady, "Loading": data.DataLoading, "Failed": data.DataFailed}[st.status]
|
||||
ui.Column(c).Width(680).Children(func() {
|
||||
if mujica.Flamegraph(c, root, &st.s, mujica.FlamegraphOptions{Unit: "ms", Status: status, Error: "Profile could not be read", Disabled: st.disabled}).Changed() {
|
||||
if code.Flamegraph(c, root, &st.s, code.FlamegraphOptions{Unit: "ms", Status: status, Error: "Profile could not be read", Disabled: st.disabled}).Changed() {
|
||||
log(fmt.Sprintf("zoom %v focus %v", st.s.Zoom, st.s.Focus))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2,22 +2,22 @@ package main
|
||||
|
||||
// Snippets are the bodies of the Example functions in example_code_test.go.
|
||||
|
||||
const codeTerminalCode = `var term mujica.TerminalState
|
||||
const codeTerminalCode = `var term code.TerminalState
|
||||
term.Write("\x1b[1m~/court\x1b[0m $ ls\r\n\x1b[34mdecrees\x1b[0m ledger.txt\r\n$ ")
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Terminal(c, &term, mujica.TerminalOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
code.Terminal(c, &term, code.TerminalOptions{
|
||||
Input: func(data string) { fmt.Printf("to pty: %q\n", data) },
|
||||
Resized: func(cols, rows int) { fmt.Println("pty size", cols, rows) },
|
||||
}).Size(380, 200)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeTerminalTabsCode = `tabs := []mujica.TerminalTab{{ID: "a", Title: "zsh"}, {ID: "b", Title: "build", Busy: true}}
|
||||
const codeTerminalTabsCode = `tabs := []code.TerminalTab{{ID: "a", Title: "zsh"}, {ID: "b", Title: "build", Busy: true}}
|
||||
active := "a"
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.TerminalTabs(c, &tabs, &active, mujica.TerminalTabsOptions{}, func(id string) {
|
||||
core.Use(c, core.Settings{})
|
||||
r := code.TerminalTabs(c, &tabs, &active, code.TerminalTabsOptions{}, func(id string) {
|
||||
ui.Text(c, "terminal "+id)
|
||||
})
|
||||
if r.Closed != "" {
|
||||
@@ -26,23 +26,23 @@ view := func(c *ui.Context) {
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeTerminalSearchCode = `var term mujica.TerminalState
|
||||
const codeTerminalSearchCode = `var term code.TerminalState
|
||||
term.Write("build ok\r\nerror: missing seal\r\n")
|
||||
var search mujica.TerminalSearchState
|
||||
var search code.TerminalSearchState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
core.Use(c, core.Settings{})
|
||||
ui.Column(c).Gap(8).Children(func() {
|
||||
mujica.TerminalSearch(c, &term, &search, mujica.TerminalSearchOptions{})
|
||||
mujica.Terminal(c, &term, mujica.TerminalOptions{}).Height(160)
|
||||
code.TerminalSearch(c, &term, &search, code.TerminalSearchOptions{})
|
||||
code.Terminal(c, &term, code.TerminalOptions{}).Height(160)
|
||||
})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeCommandHistoryCode = `history := []mujica.HistoryEntry{{Command: "git status"}, {Command: "go test ./...", ExitCode: 1}}
|
||||
var state mujica.CommandHistoryState
|
||||
const codeCommandHistoryCode = `history := []code.HistoryEntry{{Command: "git status"}, {Command: "go test ./...", ExitCode: 1}}
|
||||
var state code.CommandHistoryState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.CommandHistory(c, history, &state, mujica.CommandHistoryOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := code.CommandHistory(c, history, &state, code.CommandHistoryOptions{})
|
||||
r.Element.Height(240)
|
||||
if r.Replayed != nil {
|
||||
fmt.Println("run", r.Replayed.Command)
|
||||
@@ -50,19 +50,19 @@ view := func(c *ui.Context) {
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeLogViewerCode = `lines := []mujica.LogLine{{Level: mujica.LogInfo, Text: "court opened"}, {Level: mujica.LogError, Text: "seal missing"}}
|
||||
var state mujica.LogViewerState
|
||||
const codeLogViewerCode = `lines := []code.LogLine{{Level: code.LogInfo, Text: "court opened"}, {Level: code.LogError, Text: "seal missing"}}
|
||||
var state code.LogViewerState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.LogViewer(c, lines, &state, mujica.LogViewerOptions{}).Element.Height(240)
|
||||
core.Use(c, core.Settings{})
|
||||
code.LogViewer(c, lines, &state, code.LogViewerOptions{}).Element.Height(240)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeProcessListCode = `procs := []mujica.Process{{PID: 7, Name: "archivist", CPU: 42, Memory: 50 << 20}, {PID: 9, Name: "herald", CPU: 3, Memory: 8 << 20}}
|
||||
var state mujica.ProcessListState
|
||||
const codeProcessListCode = `procs := []code.Process{{PID: 7, Name: "archivist", CPU: 42, Memory: 50 << 20}, {PID: 9, Name: "herald", CPU: 3, Memory: 8 << 20}}
|
||||
var state code.ProcessListState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.ProcessList(c, procs, &state, mujica.ProcessListOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := code.ProcessList(c, procs, &state, code.ProcessListOptions{})
|
||||
r.Element.Height(240)
|
||||
if r.End != nil {
|
||||
fmt.Println("kill", r.End.PID)
|
||||
@@ -71,38 +71,38 @@ view := func(c *ui.Context) {
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeCodeViewerCode = `lines := []string{"func Seal(d string) int {", "\treturn len(d)", "}"}
|
||||
var state mujica.CodeViewerState
|
||||
var state code.CodeViewerState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.CodeViewer(c, lines, &state, mujica.CodeViewerOptions{Language: "go"}).Element.Height(240)
|
||||
core.Use(c, core.Settings{})
|
||||
code.CodeViewer(c, lines, &state, code.CodeViewerOptions{Language: "go"}).Element.Height(240)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeMinimapCode = `lines := []string{"package court", "", "func Seal() {}", "func Herald() {}"}
|
||||
view := mujica.MinimapView{Top: 0, Visible: 2}
|
||||
view := code.MinimapView{Top: 0, Visible: 2}
|
||||
draw := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.Minimap(c, lines, &view, mujica.MinimapOptions{Language: "go"}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if code.Minimap(c, lines, &view, code.MinimapOptions{Language: "go"}).Changed() {
|
||||
fmt.Println("scroll editor to", view.Top)
|
||||
}
|
||||
}
|
||||
ui.Render(draw, 400, 300, 1)`
|
||||
|
||||
const codeFindWidgetCode = `text := "the seal and the seal"
|
||||
state := mujica.FindState{Query: "seal", Replacement: "signet", ShowReplace: true}
|
||||
state := code.FindState{Query: "seal", Replacement: "signet", ShowReplace: true}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.FindWidget(c, &text, &state, mujica.FindWidgetOptions{}).Replaced > 0 {
|
||||
core.Use(c, core.Settings{})
|
||||
if code.FindWidget(c, &text, &state, code.FindWidgetOptions{}).Replaced > 0 {
|
||||
fmt.Println(text)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeSearchPanelCode = `results := []mujica.SearchFileResult{{Path: "court/seal.go", Matches: []mujica.SearchMatch{{Line: 12, Text: "func Seal() {", Start: 5, End: 9}}}}
|
||||
state := mujica.SearchPanelState{Query: "seal"}
|
||||
const codeSearchPanelCode = `results := []code.SearchFileResult{{Path: "court/seal.go", Matches: []code.SearchMatch{{Line: 12, Text: "func Seal() {", Start: 5, End: 9}}}}
|
||||
state := code.SearchPanelState{Query: "seal"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.SearchPanel(c, results, &state, mujica.SearchPanelOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := code.SearchPanel(c, results, &state, code.SearchPanelOptions{})
|
||||
r.Element.Height(280)
|
||||
if r.Opened != nil {
|
||||
fmt.Println("open", r.Opened.Path, r.Opened.Line)
|
||||
@@ -110,11 +110,11 @@ view := func(c *ui.Context) {
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeProblemsPanelCode = `problems := []mujica.Problem{{Severity: mujica.ProblemError, File: "court/seal.go", Line: 12, Col: 5, Message: "undefined: Decree"}}
|
||||
var state mujica.ProblemsPanelState
|
||||
const codeProblemsPanelCode = `problems := []code.Problem{{Severity: code.ProblemError, File: "court/seal.go", Line: 12, Col: 5, Message: "undefined: Decree"}}
|
||||
var state code.ProblemsPanelState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.ProblemsPanel(c, problems, &state, mujica.ProblemsPanelOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := code.ProblemsPanel(c, problems, &state, code.ProblemsPanelOptions{})
|
||||
r.Element.Height(240)
|
||||
if r.Opened != nil {
|
||||
fmt.Println("go to", r.Opened.File, r.Opened.Line)
|
||||
@@ -122,11 +122,11 @@ view := func(c *ui.Context) {
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeOutputPanelCode = `channels := []mujica.OutputChannel{{Name: "Build", Lines: []string{"compiling court", "\x1b[32mok\x1b[0m"}}}
|
||||
var state mujica.OutputPanelState
|
||||
const codeOutputPanelCode = `channels := []code.OutputChannel{{Name: "Build", Lines: []string{"compiling court", "\x1b[32mok\x1b[0m"}}}
|
||||
var state code.OutputPanelState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.OutputPanel(c, channels, &state, mujica.OutputPanelOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := code.OutputPanel(c, channels, &state, code.OutputPanelOptions{})
|
||||
r.Element.Height(240)
|
||||
if r.Cleared != "" {
|
||||
channels[state.Channel].Lines = nil
|
||||
@@ -134,13 +134,13 @@ view := func(c *ui.Context) {
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeSymbolOutlineCode = `symbols := []mujica.Symbol{{Name: "Court", Kind: mujica.SymbolType, Line: 3, EndLine: 40, Children: []mujica.Symbol{
|
||||
{Name: "Seal", Kind: mujica.SymbolMethod, Line: 10, EndLine: 20},
|
||||
const codeSymbolOutlineCode = `symbols := []code.Symbol{{Name: "Court", Kind: code.SymbolType, Line: 3, EndLine: 40, Children: []code.Symbol{
|
||||
{Name: "Seal", Kind: code.SymbolMethod, Line: 10, EndLine: 20},
|
||||
}}}
|
||||
var state mujica.SymbolOutlineState
|
||||
var state code.SymbolOutlineState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.SymbolOutline(c, symbols, &state, mujica.SymbolOutlineOptions{CursorLine: 12})
|
||||
core.Use(c, core.Settings{})
|
||||
r := code.SymbolOutline(c, symbols, &state, code.SymbolOutlineOptions{CursorLine: 12})
|
||||
r.Element.Height(240)
|
||||
if r.Jumped != nil {
|
||||
fmt.Println("go to line", r.Jumped.Line)
|
||||
@@ -150,33 +150,33 @@ ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeCompletionMenuCode = `text := "se"
|
||||
open := true
|
||||
items := []mujica.Completion{{Label: "Seal", Kind: mujica.SymbolMethod, Detail: "func()"}, {Label: "Search", Kind: mujica.SymbolFunction}}
|
||||
var state mujica.CompletionMenuState
|
||||
items := []code.Completion{{Label: "Seal", Kind: code.SymbolMethod, Detail: "func()"}, {Label: "Search", Kind: code.SymbolFunction}}
|
||||
var state code.CompletionMenuState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
in := mujica.TextInput(c, &text, mujica.TextInputOptions{Label: "Code"})
|
||||
if r := mujica.CompletionMenu(c, in.Input, &open, items, &state, mujica.CompletionMenuOptions{}); r.Accepted != nil {
|
||||
core.Use(c, core.Settings{})
|
||||
in := input.TextInput(c, &text, input.TextInputOptions{Label: "Code"})
|
||||
if r := code.CompletionMenu(c, in.Input, &open, items, &state, code.CompletionMenuOptions{}); r.Accepted != nil {
|
||||
text = r.Accepted.Label
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeDebugToolbarCode = `state := mujica.DebugPaused
|
||||
const codeDebugToolbarCode = `state := code.DebugPaused
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
switch mujica.DebugToolbar(c, state, mujica.DebugToolbarOptions{}).Action {
|
||||
case mujica.DebugContinue:
|
||||
state = mujica.DebugRunning
|
||||
case mujica.DebugStop:
|
||||
state = mujica.DebugStopped
|
||||
core.Use(c, core.Settings{})
|
||||
switch code.DebugToolbar(c, state, code.DebugToolbarOptions{}).Action {
|
||||
case code.DebugContinue:
|
||||
state = code.DebugRunning
|
||||
case code.DebugStop:
|
||||
state = code.DebugStopped
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeBreakpointListCode = `bps := []mujica.Breakpoint{{ID: "a", File: "court/seal.go", Line: 12, Enabled: true}, {ID: "b", File: "main.go", Line: 9, Condition: "n > 3"}}
|
||||
const codeBreakpointListCode = `bps := []code.Breakpoint{{ID: "a", File: "court/seal.go", Line: 12, Enabled: true}, {ID: "b", File: "main.go", Line: 9, Condition: "n > 3"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.BreakpointList(c, &bps, mujica.BreakpointListOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := code.BreakpointList(c, &bps, code.BreakpointListOptions{})
|
||||
r.Element.Height(200)
|
||||
if r.Toggled != "" || r.Edited != "" {
|
||||
fmt.Println("sync breakpoints with the debugger")
|
||||
@@ -184,14 +184,14 @@ view := func(c *ui.Context) {
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeCallStackCode = `threads := []mujica.DebugThread{{ID: "t1", Name: "main", Frames: []mujica.StackFrame{
|
||||
const codeCallStackCode = `threads := []code.DebugThread{{ID: "t1", Name: "main", Frames: []code.StackFrame{
|
||||
{ID: "f1", Name: "court.Seal", File: "court/seal.go", Line: 14},
|
||||
{ID: "f2", Name: "main.main", File: "main.go", Line: 9},
|
||||
}}}
|
||||
state := mujica.CallStackState{Current: "f1"}
|
||||
state := code.CallStackState{Current: "f1"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.CallStack(c, threads, &state, mujica.CallStackOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := code.CallStack(c, threads, &state, code.CallStackOptions{})
|
||||
r.Element.Height(200)
|
||||
if r.Changed() {
|
||||
fmt.Println("show frame", state.Current)
|
||||
@@ -199,11 +199,11 @@ view := func(c *ui.Context) {
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeVariablesPanelCode = `vars := []mujica.Variable{{Name: "decree", Value: "{…}", Children: []mujica.Variable{{Name: "id", Value: "42", Changed: true}}}, {Name: "count", Value: "7"}}
|
||||
var state mujica.VariablesPanelState
|
||||
const codeVariablesPanelCode = `vars := []code.Variable{{Name: "decree", Value: "{…}", Children: []code.Variable{{Name: "id", Value: "42", Changed: true}}}, {Name: "count", Value: "7"}}
|
||||
var state code.VariablesPanelState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.VariablesPanel(c, vars, &state, mujica.VariablesPanelOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := code.VariablesPanel(c, vars, &state, code.VariablesPanelOptions{})
|
||||
r.Element.Height(200)
|
||||
if r.Edited != nil {
|
||||
fmt.Println("set", r.Edited.Path, "=", r.Edited.Value)
|
||||
@@ -212,19 +212,19 @@ view := func(c *ui.Context) {
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeHexViewerCode = `data := []byte("MUJICA court ledger\x00\x01")
|
||||
var state mujica.HexViewerState
|
||||
var state code.HexViewerState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.HexViewer(c, data, &state, mujica.HexViewerOptions{}).Element.Height(160)
|
||||
core.Use(c, core.Settings{})
|
||||
code.HexViewer(c, data, &state, code.HexViewerOptions{}).Element.Height(160)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeFlamegraphCode = `root := &mujica.FlameNode{Name: "main", Value: 100, Children: []mujica.FlameNode{
|
||||
const codeFlamegraphCode = `root := &code.FlameNode{Name: "main", Value: 100, Children: []code.FlameNode{
|
||||
{Name: "court.Seal", Value: 60}, {Name: "ledger.Write", Value: 30},
|
||||
}}
|
||||
var state mujica.FlamegraphState
|
||||
var state code.FlamegraphState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Flamegraph(c, root, &state, mujica.FlamegraphOptions{Unit: "ms"})
|
||||
core.Use(c, core.Settings{})
|
||||
code.Flamegraph(c, root, &state, code.FlamegraphOptions{Unit: "ms"})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
@@ -6,28 +6,30 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/code"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func codeProcessListDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
procs []mujica.Process
|
||||
s mujica.ProcessListState
|
||||
procs []code.Process
|
||||
s code.ProcessListState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "326", func() *state {
|
||||
return &state{procs: []mujica.Process{
|
||||
return &state{procs: []code.Process{
|
||||
{PID: 101, Name: "chancellor", CPU: 3.5, Memory: 200 << 20}, {PID: 7, Name: "archivist", CPU: 42.1, Memory: 50 << 20},
|
||||
{PID: 330, Name: "herald", CPU: 0.2, Memory: 2 << 30}, {PID: 412, Name: "scribe", CPU: 12.8, Memory: 96 << 20},
|
||||
}}
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
r := mujica.ProcessList(c, st.procs, &st.s, mujica.ProcessListOptions{Disabled: st.disabled})
|
||||
r := code.ProcessList(c, st.procs, &st.s, code.ProcessListOptions{Disabled: st.disabled})
|
||||
r.Element.Size(560, 260)
|
||||
if r.End != nil {
|
||||
log(fmt.Sprintf("end %s (PID %d)", r.End.Name, r.End.PID))
|
||||
var kept []mujica.Process
|
||||
var kept []code.Process
|
||||
for _, p := range st.procs {
|
||||
if p.PID != r.End.PID {
|
||||
kept = append(kept, p)
|
||||
@@ -71,7 +73,7 @@ func Herald(ds []Decree) int {
|
||||
|
||||
func codeCodeViewerDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
s mujica.CodeViewerState
|
||||
s code.CodeViewerState
|
||||
lang string
|
||||
disabled bool
|
||||
}
|
||||
@@ -79,16 +81,16 @@ func codeCodeViewerDemo(c *ui.Context, log func(string)) {
|
||||
st := *s
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
langs := []mujica.SelectOption[string]{{Value: "go", Label: "Go"}, {Value: "", Label: "Plain"}}
|
||||
if mujica.Select(c, &st.lang, langs, mujica.SelectOptions{Label: "Language"}).Changed() {
|
||||
langs := []input.SelectOption[string]{{Value: "go", Label: "Go"}, {Value: "", Label: "Plain"}}
|
||||
if input.Select(c, &st.lang, langs, input.SelectOptions{Label: "Language"}).Changed() {
|
||||
log("language " + st.lang)
|
||||
}
|
||||
if mujica.Button(c, "Go to line 20", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Go to line 20", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
st.s.GoTo(19)
|
||||
log("go to 20")
|
||||
}
|
||||
})
|
||||
r := mujica.CodeViewer(c, codeDemoSource, &st.s, mujica.CodeViewerOptions{Language: st.lang, Label: "Source", Disabled: st.disabled})
|
||||
r := code.CodeViewer(c, codeDemoSource, &st.s, code.CodeViewerOptions{Language: st.lang, Label: "Source", Disabled: st.disabled})
|
||||
r.Element.Size(600, 360)
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("line %d", st.s.Line()+1))
|
||||
@@ -97,8 +99,8 @@ func codeCodeViewerDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func codeMinimapDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
s mujica.CodeViewerState
|
||||
view mujica.MinimapView
|
||||
s code.CodeViewerState
|
||||
view code.MinimapView
|
||||
lines []string
|
||||
disabled bool
|
||||
}
|
||||
@@ -112,10 +114,10 @@ func codeMinimapDemo(c *ui.Context, log func(string)) {
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
first, last := st.s.Visible()
|
||||
st.view = mujica.MinimapView{Top: first, Visible: max(last-first+1, 1)}
|
||||
st.view = code.MinimapView{Top: first, Visible: max(last-first+1, 1)}
|
||||
ui.Row(c).Gap(8).Height(360).AlignItems(ui.Stretch).Children(func() {
|
||||
mujica.CodeViewer(c, st.lines, &st.s, mujica.CodeViewerOptions{Language: "go"}).Element.Width(520)
|
||||
if mujica.Minimap(c, st.lines, &st.view, mujica.MinimapOptions{Language: "go", Disabled: st.disabled}).Changed() {
|
||||
code.CodeViewer(c, st.lines, &st.s, code.CodeViewerOptions{Language: "go"}).Element.Width(520)
|
||||
if code.Minimap(c, st.lines, &st.view, code.MinimapOptions{Language: "go", Disabled: st.disabled}).Changed() {
|
||||
st.s.ScrollTo(st.view.Top)
|
||||
log(fmt.Sprintf("jump to line %d", st.view.Top+1))
|
||||
}
|
||||
@@ -125,11 +127,11 @@ func codeMinimapDemo(c *ui.Context, log func(string)) {
|
||||
func codeFindWidgetDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
text string
|
||||
s mujica.FindState
|
||||
s code.FindState
|
||||
readOnly, disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "329", func() *state {
|
||||
return &state{text: "The seal of the court; seals are kept sealed. Seal 42 and seal 7.", s: mujica.FindState{Query: "seal"}}
|
||||
return &state{text: "The seal of the court; seals are kept sealed. Seal 42 and seal 7.", s: code.FindState{Query: "seal"}}
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() {
|
||||
@@ -137,7 +139,7 @@ func codeFindWidgetDemo(c *ui.Context, log func(string)) {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
})
|
||||
ui.Column(c).Width(620).Gap(12).Children(func() {
|
||||
r := mujica.FindWidget(c, &st.text, &st.s, mujica.FindWidgetOptions{ReadOnly: st.readOnly, Disabled: st.disabled})
|
||||
r := code.FindWidget(c, &st.text, &st.s, code.FindWidgetOptions{ReadOnly: st.readOnly, Disabled: st.disabled})
|
||||
if r.Moved {
|
||||
log(fmt.Sprintf("%d matches, at %d", len(st.s.Matches()), st.s.Current+1))
|
||||
}
|
||||
@@ -148,7 +150,7 @@ func codeFindWidgetDemo(c *ui.Context, log func(string)) {
|
||||
at := 0
|
||||
for i, m := range st.s.Matches() {
|
||||
spans = append(spans, ui.Span{Text: st.text[at:m[0]]})
|
||||
sp := ui.Span{Text: st.text[m[0]:m[1]], Background: mujica.Tokens(c).Selection, Underline: true}
|
||||
sp := ui.Span{Text: st.text[m[0]:m[1]], Background: core.Tokens(c).Selection, Underline: true}
|
||||
if i == st.s.Current {
|
||||
sp.Weight = 700
|
||||
}
|
||||
@@ -162,11 +164,11 @@ func codeFindWidgetDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func codeSearchPanelDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
s mujica.SearchPanelState
|
||||
results []mujica.SearchFileResult
|
||||
s code.SearchPanelState
|
||||
results []code.SearchFileResult
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "330", func() *state { return &state{s: mujica.SearchPanelState{Query: "seal"}} })
|
||||
s := ui.Local(c.Root(), "330", func() *state { return &state{s: code.SearchPanelState{Query: "seal"}} })
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
// The demo searches its own two files.
|
||||
@@ -174,7 +176,7 @@ func codeSearchPanelDemo(c *ui.Context, log func(string)) {
|
||||
if st.results == nil || st.s.Query != "" && len(st.results) == 0 {
|
||||
st.results = codeDemoSearch(files, st.s.Query, st.s.Include, st.s.Exclude)
|
||||
}
|
||||
r := mujica.SearchPanel(c, st.results, &st.s, mujica.SearchPanelOptions{Disabled: st.disabled})
|
||||
r := code.SearchPanel(c, st.results, &st.s, code.SearchPanelOptions{Disabled: st.disabled})
|
||||
r.Element.Size(560, 380)
|
||||
if r.Changed {
|
||||
st.results = codeDemoSearch(files, st.s.Query, st.s.Include, st.s.Exclude)
|
||||
@@ -186,8 +188,8 @@ func codeSearchPanelDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
// codeDemoSearch finds q in the demo files.
|
||||
func codeDemoSearch(files map[string][]string, q, include, exclude string) []mujica.SearchFileResult {
|
||||
out := []mujica.SearchFileResult{}
|
||||
func codeDemoSearch(files map[string][]string, q, include, exclude string) []code.SearchFileResult {
|
||||
out := []code.SearchFileResult{}
|
||||
if q == "" {
|
||||
return out
|
||||
}
|
||||
@@ -195,10 +197,10 @@ func codeDemoSearch(files map[string][]string, q, include, exclude string) []muj
|
||||
if include != "" && !strings.Contains(path, strings.Trim(include, "*")) || exclude != "" && strings.Contains(path, strings.Trim(exclude, "*")) {
|
||||
continue
|
||||
}
|
||||
f := mujica.SearchFileResult{Path: path}
|
||||
f := code.SearchFileResult{Path: path}
|
||||
for i, line := range files[path] {
|
||||
if at := strings.Index(strings.ToLower(line), strings.ToLower(q)); at >= 0 {
|
||||
f.Matches = append(f.Matches, mujica.SearchMatch{Line: i + 1, Text: line, Start: at, End: at + len(q)})
|
||||
f.Matches = append(f.Matches, code.SearchMatch{Line: i + 1, Text: line, Start: at, End: at + len(q)})
|
||||
}
|
||||
}
|
||||
if len(f.Matches) > 0 {
|
||||
@@ -210,19 +212,19 @@ func codeDemoSearch(files map[string][]string, q, include, exclude string) []muj
|
||||
|
||||
func codeProblemsDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
s mujica.ProblemsPanelState
|
||||
s code.ProblemsPanelState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "331", func() *state { return &state{} })
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
problems := []mujica.Problem{
|
||||
{Severity: mujica.ProblemError, File: "court/seal.go", Line: 12, Col: 5, Message: "undefined: Decree", Source: "compiler"},
|
||||
{Severity: mujica.ProblemWarning, File: "court/seal.go", Line: 30, Col: 2, Message: "unused variable wax", Source: "vet"},
|
||||
{Severity: mujica.ProblemInfo, File: "docs/ledger.md", Line: 4, Col: 1, Message: "line is longer than 80", Source: "lint"},
|
||||
{Severity: mujica.ProblemError, File: "main.go", Line: 9, Col: 14, Message: "missing return", Source: "compiler"},
|
||||
problems := []code.Problem{
|
||||
{Severity: code.ProblemError, File: "court/seal.go", Line: 12, Col: 5, Message: "undefined: Decree", Source: "compiler"},
|
||||
{Severity: code.ProblemWarning, File: "court/seal.go", Line: 30, Col: 2, Message: "unused variable wax", Source: "vet"},
|
||||
{Severity: code.ProblemInfo, File: "docs/ledger.md", Line: 4, Col: 1, Message: "line is longer than 80", Source: "lint"},
|
||||
{Severity: code.ProblemError, File: "main.go", Line: 9, Col: 14, Message: "missing return", Source: "compiler"},
|
||||
}
|
||||
r := mujica.ProblemsPanel(c, problems, &st.s, mujica.ProblemsPanelOptions{Disabled: st.disabled})
|
||||
r := code.ProblemsPanel(c, problems, &st.s, code.ProblemsPanelOptions{Disabled: st.disabled})
|
||||
r.Element.Size(620, 320)
|
||||
if r.Opened != nil {
|
||||
log(fmt.Sprintf("go to %s:%d:%d", r.Opened.File, r.Opened.Line, r.Opened.Col))
|
||||
@@ -231,8 +233,8 @@ func codeProblemsDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func codeOutputDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
channels []mujica.OutputChannel
|
||||
s mujica.OutputPanelState
|
||||
channels []code.OutputChannel
|
||||
s code.OutputPanelState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "332", func() *state {
|
||||
@@ -240,19 +242,19 @@ func codeOutputDemo(c *ui.Context, log func(string)) {
|
||||
for i := range 300 {
|
||||
build = append(build, fmt.Sprintf("compiling unit %d of 300", i+1))
|
||||
}
|
||||
return &state{channels: []mujica.OutputChannel{{Name: "Build", Lines: build},
|
||||
return &state{channels: []code.OutputChannel{{Name: "Build", Lines: build},
|
||||
{Name: "Tests", Lines: []string{"\x1b[32mPASS\x1b[0m court/seal", "\x1b[31mFAIL\x1b[0m court/ledger: expected 3 seals", "\x1b[1mok\x1b[0m 2 of 3 packages"}}}}
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
if mujica.Button(c, "Write line", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Write line", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
ch := &st.channels[st.s.Channel]
|
||||
ch.Lines = append(ch.Lines, fmt.Sprintf("line %d", len(ch.Lines)+1))
|
||||
log("write to " + ch.Name)
|
||||
}
|
||||
})
|
||||
r := mujica.OutputPanel(c, st.channels, &st.s, mujica.OutputPanelOptions{Disabled: st.disabled})
|
||||
r := code.OutputPanel(c, st.channels, &st.s, code.OutputPanelOptions{Disabled: st.disabled})
|
||||
r.Element.Size(560, 300)
|
||||
if r.Changed {
|
||||
log("channel " + st.channels[st.s.Channel].Name)
|
||||
|
||||
@@ -3,8 +3,9 @@ package main
|
||||
import (
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/theme"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||
)
|
||||
|
||||
const dataCategory = "Data"
|
||||
@@ -39,18 +40,18 @@ var dataPages = []page{
|
||||
// dataSection lays out a titled block of a demo.
|
||||
func dataSection(c *ui.Context, title string, fn func()) {
|
||||
ui.Column(c).Gap(12).Children(func() {
|
||||
ui.Text(c, title).Font(theme.SerifFont).FontSize(mujica.FontSize(c, theme.H3Size))
|
||||
ui.Text(c, title).Font(theme.SerifFont).FontSize(core.FontSize(c, theme.H3Size))
|
||||
fn()
|
||||
})
|
||||
}
|
||||
|
||||
// dataStates lays labeled variants side by side.
|
||||
func dataStates(c *ui.Context, fn func(state func(name string, body func()))) {
|
||||
k := mujica.Tokens(c)
|
||||
k := core.Tokens(c)
|
||||
ui.Row(c).Gap(24).Wrap().AlignItems(ui.Start).Children(func() {
|
||||
fn(func(name string, body func()) {
|
||||
ui.Column(c).Gap(6).AlignItems(ui.Start).Children(func() {
|
||||
ui.Text(c, name).FontSize(mujica.FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted)
|
||||
ui.Text(c, name).FontSize(core.FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted)
|
||||
body()
|
||||
})
|
||||
})
|
||||
@@ -64,18 +65,18 @@ func dataControls(c *ui.Context, fn func()) {
|
||||
var dataStatuses = []string{"Ready", "Loading", "Loading more", "Failed", "Empty"}
|
||||
|
||||
// dataStatus maps a status choice to the status and whether rows show.
|
||||
func dataStatus(s string) (mujica.DataStatus, bool) {
|
||||
func dataStatus(s string) (data.DataStatus, bool) {
|
||||
switch s {
|
||||
case "Ready":
|
||||
return mujica.DataReady, true
|
||||
return data.DataReady, true
|
||||
case "Loading":
|
||||
return mujica.DataLoading, false
|
||||
return data.DataLoading, false
|
||||
case "Loading more":
|
||||
return mujica.DataLoadingMore, true
|
||||
return data.DataLoadingMore, true
|
||||
case "Failed":
|
||||
return mujica.DataFailed, true
|
||||
return data.DataFailed, true
|
||||
case "Empty":
|
||||
return mujica.DataReady, false
|
||||
return data.DataReady, false
|
||||
}
|
||||
panic("gallery: unknown status " + s)
|
||||
}
|
||||
@@ -3,9 +3,9 @@ package main
|
||||
// Snippets are the bodies of the Example functions in example_data_test.go.
|
||||
|
||||
const dataCardCode = `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Card(c, mujica.CardOptions{Title: "Archive", Decorated: true, Footer: func() {
|
||||
mujica.Button(c, "Open", mujica.ButtonOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
data.Card(c, data.CardOptions{Title: "Archive", Decorated: true, Footer: func() {
|
||||
input.Button(c, "Open", input.ButtonOptions{})
|
||||
}}, func() {
|
||||
ui.Text(c, "Letters of the court, sealed in wax.")
|
||||
}).Width(280)
|
||||
@@ -13,10 +13,10 @@ const dataCardCode = `view := func(c *ui.Context) {
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const dataListCode = `names := []string{"Matins", "Lauds", "Prime", "Terce"}
|
||||
var state mujica.ListState[string]
|
||||
var state data.ListState[string]
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
v := mujica.List(c, &state, len(names), mujica.ListOptions[string]{
|
||||
core.Use(c, core.Settings{})
|
||||
v := data.List(c, &state, len(names), data.ListOptions[string]{
|
||||
Key: func(i int) string { return names[i] },
|
||||
Label: func(i int) string { return names[i] },
|
||||
}, func(i int) { ui.Text(c, names[i]) })
|
||||
@@ -34,10 +34,10 @@ const dataTableCode = `type relic struct {
|
||||
Age int
|
||||
}
|
||||
rows := []relic{{1, "Censer", 300}, {2, "Altar", 120}, {3, "Bell", 80}}
|
||||
var state mujica.DataTableState[int]
|
||||
opts := mujica.DataTableOptions[relic, int]{
|
||||
var state data.DataTableState[int]
|
||||
opts := data.DataTableOptions[relic, int]{
|
||||
Key: func(r relic) int { return r.ID },
|
||||
Columns: []mujica.DataColumn[relic]{
|
||||
Columns: []data.DataColumn[relic]{
|
||||
{ID: "name", Title: "Name", Sortable: true,
|
||||
Text: func(r relic) string { return r.Name },
|
||||
Compare: func(a, b relic) int { return cmp.Compare(a.Name, b.Name) }},
|
||||
@@ -47,16 +47,16 @@ opts := mujica.DataTableOptions[relic, int]{
|
||||
},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.DataTable(c, &state, rows, opts).Size(360, 240)
|
||||
core.Use(c, core.Settings{})
|
||||
data.DataTable(c, &state, rows, opts).Size(360, 240)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const dataTreeCode = `kids := map[string][]string{"Nave": {"Pulpit", "Pews"}}
|
||||
var state mujica.TreeState[string]
|
||||
var state data.TreeState[string]
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Tree(c, &state, mujica.TreeOptions[string]{
|
||||
core.Use(c, core.Settings{})
|
||||
data.Tree(c, &state, data.TreeOptions[string]{
|
||||
Roots: []string{"Nave", "Sacristy"},
|
||||
Children: func(n string) []string { return kids[n] },
|
||||
Label: func(n string) string { return n },
|
||||
@@ -66,14 +66,14 @@ ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const dataTreeTableCode = `kids := map[string][]string{"Vestry": {"Stole", "Alb"}}
|
||||
size := map[string]int{"Vestry": 9, "Stole": 2, "Alb": 5, "Organ": 7}
|
||||
var state mujica.TreeTableState[string]
|
||||
var state data.TreeTableState[string]
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.TreeTable(c, &state, mujica.TreeTableOptions[string]{
|
||||
core.Use(c, core.Settings{})
|
||||
data.TreeTable(c, &state, data.TreeTableOptions[string]{
|
||||
Roots: []string{"Vestry", "Organ"},
|
||||
Children: func(n string) []string { return kids[n] },
|
||||
Label: func(n string) string { return n },
|
||||
Columns: []mujica.TreeColumn[string]{
|
||||
Columns: []data.TreeColumn[string]{
|
||||
{ID: "name", Title: "Name", Cell: func(n string) { ui.Text(c, n) },
|
||||
Compare: cmp.Compare[string]},
|
||||
{ID: "size", Title: "Size", Width: 80, Align: ui.End,
|
||||
@@ -85,59 +85,59 @@ ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const dataAccordionCode = `open := []string{"choir"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Accordion(c, &open, []mujica.AccordionItem{
|
||||
core.Use(c, core.Settings{})
|
||||
data.Accordion(c, &open, []data.AccordionItem{
|
||||
{ID: "choir", Title: "Choir", Content: func() { ui.Text(c, "Twelve voices.") }},
|
||||
{ID: "nave", Title: "Nave", Content: func() { ui.Text(c, "Black oak pews.") }},
|
||||
}, mujica.AccordionOptions{})
|
||||
}, data.AccordionOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const dataCollapsibleCode = `open := false
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Collapsible(c, &open, mujica.CollapsibleOptions{Title: "Details"}, func() {
|
||||
core.Use(c, core.Settings{})
|
||||
data.Collapsible(c, &open, data.CollapsibleOptions{Title: "Details"}, func() {
|
||||
ui.Text(c, "Candles are lit at the seventh bell.")
|
||||
})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const dataTimelineCode = `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Timeline(c, []mujica.TimelineItem{
|
||||
{Time: "06:00", Title: "Matins sung", Status: mujica.TimelineDone},
|
||||
{Time: "09:00", Title: "Relics moved", Content: "From the crypt to the altar.", Status: mujica.TimelineActive},
|
||||
core.Use(c, core.Settings{})
|
||||
data.Timeline(c, []data.TimelineItem{
|
||||
{Time: "06:00", Title: "Matins sung", Status: data.TimelineDone},
|
||||
{Time: "09:00", Title: "Relics moved", Content: "From the crypt to the altar.", Status: data.TimelineActive},
|
||||
{Time: "18:00", Title: "Vespers"},
|
||||
})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const dataDescriptionCode = `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.DescriptionList(c, []mujica.DescriptionItem{
|
||||
core.Use(c, core.Settings{})
|
||||
data.DescriptionList(c, []data.DescriptionItem{
|
||||
{Label: "Archivist", Value: "Sister Agnès", Copyable: true},
|
||||
{Label: "Shelf", Value: "North vault, row 7"},
|
||||
{Label: "Seal", Value: "Wine-red wax", Span: -1},
|
||||
}, mujica.DescriptionListOptions{})
|
||||
}, data.DescriptionListOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const dataStatisticCode = `change := 12.5
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Statistic(c, 12840, mujica.StatisticOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
data.Statistic(c, 12840, data.StatisticOptions{
|
||||
Title: "Pilgrims", Unit: "souls", Precision: 0,
|
||||
Change: &change, ChangeUnit: "%", ChangeNote: "vs last feast",
|
||||
})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const dataViewerCode = "picture := ui.MustParseSVG([]byte(`<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"240\" height=\"160\"><rect width=\"240\" height=\"160\" fill=\"#AD9164\"/><path d=\"M120 20 L200 80 L120 140 L40 80 Z\" fill=\"#7D2034\"/></svg>`))\nopen, index := true, 0\nview := func(c *ui.Context) {\n\tmujica.Use(c, mujica.Settings{})\n\tmujica.ImageViewer(c, &open, &index, []mujica.ViewerImage{{Source: picture, Alt: \"Rose window\"}})\n}\nui.Render(view, 400, 300, 1)"
|
||||
const dataViewerCode = "picture := ui.MustParseSVG([]byte(`<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"240\" height=\"160\"><rect width=\"240\" height=\"160\" fill=\"#AD9164\"/><path d=\"M120 20 L200 80 L120 140 L40 80 Z\" fill=\"#7D2034\"/></svg>`))\nopen, index := true, 0\nview := func(c *ui.Context) {\n\tcore.Use(c, core.Settings{})\n\tdata.ImageViewer(c, &open, &index, []data.ViewerImage{{Source: picture, Alt: \"Rose window\"}})\n}\nui.Render(view, 400, 300, 1)"
|
||||
|
||||
const dataCarouselCode = `index := 0
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Carousel(c, &index, 3, mujica.CarouselOptions{Label: "Windows", Autoplay: true}, func(i int) {
|
||||
core.Use(c, core.Settings{})
|
||||
data.Carousel(c, &index, 3, data.CarouselOptions{Label: "Windows", Autoplay: true}, func(i int) {
|
||||
ui.Text(c, fmt.Sprintf("Rose window %d", i+1)).Padding(24)
|
||||
}).Size(360, 220)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
var dataOffices = []string{"Matins", "Lauds", "Prime", "Terce", "Sext", "None", "Vespers", "Compline"}
|
||||
@@ -18,7 +20,7 @@ func dataListDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
big, grid, multiple bool
|
||||
status, query string
|
||||
list mujica.ListState[int]
|
||||
list data.ListState[int]
|
||||
}
|
||||
s := ui.Local(c.Root(), "070", func() state { return state{status: "Ready"} })
|
||||
n := 24
|
||||
@@ -39,7 +41,7 @@ func dataListDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
dataControls(c, func() {
|
||||
ui.TextInput(c, &s.query).Width(200).Placeholder("Find by label")
|
||||
if mujica.Button(c, "Find", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Find", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
for i := range n {
|
||||
if strings.Contains(strings.ToLower(label(i)), strings.ToLower(s.query)) {
|
||||
s.list.ScrollTo(i)
|
||||
@@ -49,13 +51,13 @@ func dataListDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
}
|
||||
})
|
||||
v := mujica.List(c, &s.list, n, mujica.ListOptions[int]{
|
||||
v := data.List(c, &s.list, n, data.ListOptions[int]{
|
||||
Key: func(i int) int { return i }, Label: label, Grid: s.grid, Multiple: s.multiple,
|
||||
ItemWidth: 140, ItemHeight: 72, Status: st,
|
||||
}, func(i int) {
|
||||
ui.Text(c, label(i)).SingleLine()
|
||||
if s.grid {
|
||||
ui.Text(c, "Office").TextColor(mujica.Tokens(c).TextMuted)
|
||||
ui.Text(c, "Office").TextColor(core.Tokens(c).TextMuted)
|
||||
}
|
||||
})
|
||||
v.Element.Size(480, 320)
|
||||
@@ -95,7 +97,7 @@ func dataTableDemo(c *ui.Context, log func(string)) {
|
||||
big, multiple bool
|
||||
status string
|
||||
small, large []dataRelic
|
||||
table mujica.DataTableState[int]
|
||||
table data.DataTableState[int]
|
||||
}
|
||||
s := ui.Local(c.Root(), "071", func() state { return state{status: "Ready", small: dataRelics(60)} })
|
||||
rows := s.small
|
||||
@@ -109,8 +111,8 @@ func dataTableDemo(c *ui.Context, log func(string)) {
|
||||
if !show {
|
||||
rows = nil
|
||||
}
|
||||
k := mujica.Tokens(c)
|
||||
cols := []mujica.DataColumn[dataRelic]{
|
||||
k := core.Tokens(c)
|
||||
cols := []data.DataColumn[dataRelic]{
|
||||
{ID: "id", Title: "ID", Width: 80, Align: ui.End, Sortable: true, Text: func(r dataRelic) string { return strconv.Itoa(r.ID) },
|
||||
Compare: func(a, b dataRelic) int { return cmp.Compare(a.ID, b.ID) }},
|
||||
{ID: "name", Title: "Name", MinWidth: 120, Sortable: true, Text: func(r dataRelic) string { return r.Name },
|
||||
@@ -135,7 +137,7 @@ func dataTableDemo(c *ui.Context, log func(string)) {
|
||||
ui.Select(c, &s.status, dataStatuses)
|
||||
ui.TextInput(c, &s.table.Filter).Width(180).Placeholder("Filter")
|
||||
})
|
||||
v := mujica.DataTable(c, &s.table, rows, mujica.DataTableOptions[dataRelic, int]{
|
||||
v := data.DataTable(c, &s.table, rows, data.DataTableOptions[dataRelic, int]{
|
||||
Columns: cols, Key: func(r dataRelic) int { return r.ID }, Multiple: s.multiple, Status: st,
|
||||
})
|
||||
v.Element.Size(620, 360)
|
||||
@@ -160,7 +162,7 @@ func dataTableDemo(c *ui.Context, log func(string)) {
|
||||
// dataLazy simulates children that arrive 600 ms after they are asked for.
|
||||
type dataLazy struct {
|
||||
kids map[string][]string
|
||||
status map[string]mujica.DataStatus
|
||||
status map[string]data.DataStatus
|
||||
due map[string]time.Time
|
||||
failed map[string]bool
|
||||
}
|
||||
@@ -169,14 +171,14 @@ func newDataLazy() *dataLazy {
|
||||
return &dataLazy{
|
||||
kids: map[string][]string{"Nave": {"Pulpit", "Pews", "Font"}, "Crypt": {}, "Archive": {},
|
||||
"Pulpit": nil, "Pews": nil, "Font": nil, "Sacristy": nil},
|
||||
status: map[string]mujica.DataStatus{"Crypt": mujica.DataUnloaded, "Archive": mujica.DataUnloaded},
|
||||
status: map[string]data.DataStatus{"Crypt": data.DataUnloaded, "Archive": data.DataUnloaded},
|
||||
due: map[string]time.Time{}, failed: map[string]bool{},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dataLazy) load(c *ui.Context, log func(string)) func(string) {
|
||||
return func(n string) {
|
||||
d.status[n] = mujica.DataLoading
|
||||
d.status[n] = data.DataLoading
|
||||
d.due[n] = c.Now().Add(600 * time.Millisecond)
|
||||
log("load " + n)
|
||||
}
|
||||
@@ -191,10 +193,10 @@ func (d *dataLazy) tick(c *ui.Context) {
|
||||
}
|
||||
delete(d.due, n)
|
||||
if n == "Archive" && !d.failed[n] {
|
||||
d.failed[n], d.status[n] = true, mujica.DataFailed
|
||||
d.failed[n], d.status[n] = true, data.DataFailed
|
||||
continue
|
||||
}
|
||||
d.status[n] = mujica.DataReady
|
||||
d.status[n] = data.DataReady
|
||||
d.kids[n] = []string{n + " north", n + " south"}
|
||||
}
|
||||
}
|
||||
@@ -203,14 +205,14 @@ func dataTreeDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
big, multiple bool
|
||||
lazy *dataLazy
|
||||
tree mujica.TreeState[string]
|
||||
bigTree mujica.TreeState[string]
|
||||
tree data.TreeState[string]
|
||||
bigTree data.TreeState[string]
|
||||
}
|
||||
s := ui.Local(c.Root(), "072", func() state { return state{lazy: newDataLazy()} })
|
||||
s.lazy.tick(c)
|
||||
opts := mujica.TreeOptions[string]{
|
||||
opts := data.TreeOptions[string]{
|
||||
Roots: []string{"Nave", "Crypt", "Archive", "Sacristy"}, Children: func(n string) []string { return s.lazy.kids[n] },
|
||||
Label: func(n string) string { return n }, ItemStatus: func(n string) mujica.DataStatus { return s.lazy.status[n] },
|
||||
Label: func(n string) string { return n }, ItemStatus: func(n string) data.DataStatus { return s.lazy.status[n] },
|
||||
Load: s.lazy.load(c, log), Multiple: s.multiple,
|
||||
}
|
||||
ts := &s.tree
|
||||
@@ -223,7 +225,7 @@ func dataTreeDemo(c *ui.Context, log func(string)) {
|
||||
ui.Checkbox(c, &s.big, "100,000 nodes")
|
||||
ui.Checkbox(c, &s.multiple, "Multiple")
|
||||
})
|
||||
v := mujica.Tree(c, ts, opts)
|
||||
v := data.Tree(c, ts, opts)
|
||||
v.Element.Size(420, 340)
|
||||
if v.Changed() {
|
||||
n, _ := ts.Selected()
|
||||
@@ -237,12 +239,12 @@ func dataTreeDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
// dataBigTree has 100 roots of 999 children: 100,000 nodes, built as they open.
|
||||
func dataBigTree(multiple bool) mujica.TreeOptions[string] {
|
||||
func dataBigTree(multiple bool) data.TreeOptions[string] {
|
||||
roots := make([]string, 100)
|
||||
for i := range roots {
|
||||
roots[i] = fmt.Sprintf("Wing %d", i+1)
|
||||
}
|
||||
return mujica.TreeOptions[string]{
|
||||
return data.TreeOptions[string]{
|
||||
Roots: roots, Label: func(n string) string { return n }, Multiple: multiple,
|
||||
Children: func(n string) []string {
|
||||
if !strings.HasPrefix(n, "Wing ") || strings.Contains(n, "·") {
|
||||
@@ -271,7 +273,7 @@ func dataTreeTableDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
multiple bool
|
||||
lazy *dataLazy
|
||||
table mujica.TreeTableState[string]
|
||||
table data.TreeTableState[string]
|
||||
}
|
||||
s := ui.Local(c.Root(), "073", func() state { return state{lazy: newDataLazy()} })
|
||||
s.lazy.tick(c)
|
||||
@@ -279,11 +281,11 @@ func dataTreeTableDemo(c *ui.Context, log func(string)) {
|
||||
text := func(n string) { ui.Text(c, n).SingleLine() }
|
||||
dataSection(c, "TreeTable", func() {
|
||||
dataControls(c, func() { ui.Checkbox(c, &s.multiple, "Multiple") })
|
||||
v := mujica.TreeTable(c, &s.table, mujica.TreeTableOptions[string]{
|
||||
v := data.TreeTable(c, &s.table, data.TreeTableOptions[string]{
|
||||
Roots: []string{"Nave", "Crypt", "Archive", "Sacristy"}, Children: func(n string) []string { return s.lazy.kids[n] },
|
||||
Label: func(n string) string { return n }, ItemStatus: func(n string) mujica.DataStatus { return s.lazy.status[n] },
|
||||
Label: func(n string) string { return n }, ItemStatus: func(n string) data.DataStatus { return s.lazy.status[n] },
|
||||
Load: s.lazy.load(c, log), Multiple: s.multiple,
|
||||
Columns: []mujica.TreeColumn[string]{
|
||||
Columns: []data.TreeColumn[string]{
|
||||
{ID: "name", Title: "Name", MinWidth: 160, Cell: text, Compare: cmp.Compare[string]},
|
||||
{ID: "size", Title: "Size", Width: 90, Align: ui.End, Cell: func(n string) { ui.Text(c, fmt.Sprintf("%d m²", size(n))) },
|
||||
Compare: func(a, b string) int { return cmp.Compare(size(a), size(b)) }},
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func dataCardDemo(c *ui.Context, log func(string)) {
|
||||
@@ -23,15 +25,15 @@ func dataCardDemo(c *ui.Context, log func(string)) {
|
||||
ui.Checkbox(c, &s.footer, "Footer")
|
||||
})
|
||||
card := func(decorated bool) {
|
||||
opts := mujica.CardOptions{Title: s.title, Decorated: decorated}
|
||||
opts := data.CardOptions{Title: s.title, Decorated: decorated}
|
||||
if s.footer {
|
||||
opts.Footer = func() {
|
||||
if mujica.Button(c, "Open", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Open", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
log(fmt.Sprintf("open clicked (decorated=%v)", decorated))
|
||||
}
|
||||
}
|
||||
}
|
||||
mujica.Card(c, opts, func() {
|
||||
data.Card(c, opts, func() {
|
||||
ui.Text(c, "Letters of the court, sealed in wine-red wax and kept in the north vault.")
|
||||
}).Width(280)
|
||||
}
|
||||
@@ -53,21 +55,21 @@ func dataAccordionDemo(c *ui.Context, log func(string)) {
|
||||
dataControls(c, func() {
|
||||
ui.Checkbox(c, &s.multiple, "Multiple")
|
||||
ui.Checkbox(c, &s.keepOne, "Keep one open")
|
||||
if mujica.Button(c, "Close all", mujica.ButtonOptions{Variant: mujica.Ghost}).Clicked() {
|
||||
if input.Button(c, "Close all", input.ButtonOptions{Variant: input.Ghost}).Clicked() {
|
||||
s.open = nil
|
||||
log("closed all from outside")
|
||||
}
|
||||
})
|
||||
r := mujica.Accordion(c, &s.open, []mujica.AccordionItem{
|
||||
r := data.Accordion(c, &s.open, []data.AccordionItem{
|
||||
{ID: "choir", Title: "Choir", Content: func() { ui.Text(c, "Twelve voices, two at the altar.") }},
|
||||
{ID: "nave", Title: "Nave", Content: func() { ui.Text(c, "Pews of black oak, polished for the feast.") }},
|
||||
{ID: "crypt", Title: "Crypt (disabled)", Disabled: true, Content: func() {}},
|
||||
{ID: "bells", Title: "Bells", Content: func() {
|
||||
if mujica.Button(c, "Ring", mujica.ButtonOptions{}).Clicked() {
|
||||
if input.Button(c, "Ring", input.ButtonOptions{}).Clicked() {
|
||||
log("ring the bells")
|
||||
}
|
||||
}},
|
||||
}, mujica.AccordionOptions{Multiple: s.multiple, KeepOne: s.keepOne})
|
||||
}, data.AccordionOptions{Multiple: s.multiple, KeepOne: s.keepOne})
|
||||
r.Element.Width(420)
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("open %v", s.open))
|
||||
@@ -80,15 +82,15 @@ func dataCollapsibleDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "075", func() state { return state{} })
|
||||
dataSection(c, "Collapsible", func() {
|
||||
dataControls(c, func() {
|
||||
if mujica.Button(c, "Open from outside", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Open from outside", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.open = true
|
||||
log("opened from outside")
|
||||
}
|
||||
ui.Checkbox(c, &s.disabled, "Disabled")
|
||||
})
|
||||
if mujica.Collapsible(c, &s.open, mujica.CollapsibleOptions{Title: "Rites of the evening", Disabled: s.disabled}, func() {
|
||||
if data.Collapsible(c, &s.open, data.CollapsibleOptions{Title: "Rites of the evening", Disabled: s.disabled}, func() {
|
||||
ui.Text(c, "Candles are lit at the seventh bell; the choir enters from the north door.")
|
||||
if mujica.Button(c, "Light candles", mujica.ButtonOptions{Variant: mujica.Outline}).Clicked() {
|
||||
if input.Button(c, "Light candles", input.ButtonOptions{Variant: input.Outline}).Clicked() {
|
||||
log("light candles")
|
||||
}
|
||||
}).Changed() {
|
||||
@@ -98,25 +100,25 @@ func dataCollapsibleDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
func dataTimelineDemo(c *ui.Context, log func(string)) {
|
||||
type state struct{ items []mujica.TimelineItem }
|
||||
type state struct{ items []data.TimelineItem }
|
||||
s := ui.Local(c.Root(), "076", func() state {
|
||||
return state{items: []mujica.TimelineItem{
|
||||
{Time: "06:00", Title: "Matins sung", Content: "The choir gathered before dawn.", Status: mujica.TimelineDone},
|
||||
{Time: "09:00", Title: "Relics moved", Status: mujica.TimelineActive,
|
||||
return state{items: []data.TimelineItem{
|
||||
{Time: "06:00", Title: "Matins sung", Content: "The choir gathered before dawn.", Status: data.TimelineDone},
|
||||
{Time: "09:00", Title: "Relics moved", Status: data.TimelineActive,
|
||||
Content: "The reliquaries travel from the crypt to the high altar, carried in procession by four acolytes in black and wine."},
|
||||
{Time: "12:00", Title: "Bell cracked", Status: mujica.TimelineError},
|
||||
{Time: "15:00", Title: "Storm warning", Status: mujica.TimelineWarning},
|
||||
{Time: "12:00", Title: "Bell cracked", Status: data.TimelineError},
|
||||
{Time: "15:00", Title: "Storm warning", Status: data.TimelineWarning},
|
||||
{Time: "18:00", Title: "Vespers"},
|
||||
}}
|
||||
})
|
||||
dataSection(c, "Timeline", func() {
|
||||
dataControls(c, func() {
|
||||
if mujica.Button(c, "Add event", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
s.items = append(s.items, mujica.TimelineItem{Time: fmt.Sprintf("%02d:00", 19+len(s.items)%5), Title: "Compline"})
|
||||
if input.Button(c, "Add event", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.items = append(s.items, data.TimelineItem{Time: fmt.Sprintf("%02d:00", 19+len(s.items)%5), Title: "Compline"})
|
||||
log(fmt.Sprintf("%d events", len(s.items)))
|
||||
}
|
||||
})
|
||||
ui.Column(c).Width(420).Children(func() { mujica.Timeline(c, s.items) })
|
||||
ui.Column(c).Width(420).Children(func() { data.Timeline(c, s.items) })
|
||||
})
|
||||
}
|
||||
|
||||
@@ -134,12 +136,12 @@ func dataDescriptionDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
cols, _ := strconv.Atoi(s.columns)
|
||||
ui.Column(c).Width(float32(s.width)).Children(func() {
|
||||
if i, ok := mujica.DescriptionList(c, []mujica.DescriptionItem{
|
||||
if i, ok := data.DescriptionList(c, []data.DescriptionItem{
|
||||
{Label: "Archivist", Value: "Sister Agnès", Copyable: true},
|
||||
{Label: "Shelf", Value: "North vault, row 7"},
|
||||
{Label: "Catalogue", Value: "MS-1347-B", Copyable: true},
|
||||
{Label: "Seal", Value: "Wine-red wax, cracked at the rim", Span: -1},
|
||||
}, mujica.DescriptionListOptions{Columns: cols}).Copied(); ok {
|
||||
}, data.DescriptionListOptions{Columns: cols}).Copied(); ok {
|
||||
log(fmt.Sprintf("copied item %d", i))
|
||||
}
|
||||
})
|
||||
@@ -166,22 +168,22 @@ func dataStatisticDemo(c *ui.Context, log func(string)) {
|
||||
change := s.change
|
||||
dataStates(c, func(state func(string, func())) {
|
||||
state("With change", func() {
|
||||
mujica.Statistic(c, s.value, mujica.StatisticOptions{Title: "Pilgrims", Unit: "souls", Precision: p,
|
||||
data.Statistic(c, s.value, data.StatisticOptions{Title: "Pilgrims", Unit: "souls", Precision: p,
|
||||
Change: &change, ChangeUnit: "%", ChangeNote: "vs last feast"})
|
||||
})
|
||||
state("Prefix", func() {
|
||||
mujica.Statistic(c, s.value/1000, mujica.StatisticOptions{Title: "Tithe", Prefix: "£", Suffix: "k", Precision: p})
|
||||
data.Statistic(c, s.value/1000, data.StatisticOptions{Title: "Tithe", Prefix: "£", Suffix: "k", Precision: p})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
var dataPictures = func() []mujica.ViewerImage {
|
||||
var dataPictures = func() []data.ViewerImage {
|
||||
art := func(bg, fg string, shape string) *ui.SVG {
|
||||
return ui.MustParseSVG([]byte(`<svg xmlns="http://www.w3.org/2000/svg" width="480" height="320"><rect width="480" height="320" fill="` + bg + `"/>` +
|
||||
`<path d="` + shape + `" fill="` + fg + `"/></svg>`))
|
||||
}
|
||||
return []mujica.ViewerImage{
|
||||
return []data.ViewerImage{
|
||||
{Source: art("#AD9164", "#7D2034", "M240 40 L380 160 L240 280 L100 160 Z"), Alt: "Rose window"},
|
||||
{Source: art("#211C1D", "#BDA172", "M80 280 L80 120 L240 40 L400 120 L400 280 Z"), Alt: "Chapel arch"},
|
||||
{Source: art("#F2EBDD", "#651729", "M60 60 H420 V100 H60 Z M60 140 H420 V180 H60 Z M60 220 H420 V260 H60 Z"), Alt: "Choir stalls"},
|
||||
@@ -195,7 +197,7 @@ func dataViewerDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
s := ui.Local(c.Root(), "079", func() state { return state{} })
|
||||
dataSection(c, "ImageViewer", func() {
|
||||
k := mujica.Tokens(c)
|
||||
k := core.Tokens(c)
|
||||
ui.Text(c, "Choose a picture to open the viewer.").TextColor(k.TextMuted)
|
||||
ui.Row(c).Gap(12).Children(func() {
|
||||
for i, p := range dataPictures {
|
||||
@@ -208,7 +210,7 @@ func dataViewerDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
})
|
||||
wasOpen := s.open
|
||||
if mujica.ImageViewer(c, &s.open, &s.index, dataPictures).Changed() {
|
||||
if data.ImageViewer(c, &s.open, &s.index, dataPictures).Changed() {
|
||||
log(fmt.Sprintf("image %d", s.index+1))
|
||||
}
|
||||
if wasOpen && !s.open {
|
||||
@@ -234,7 +236,7 @@ func dataCarouselDemo(c *ui.Context, log func(string)) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
v := mujica.Carousel(c, &s.index, len(dataPictures), mujica.CarouselOptions{Label: "Windows", Autoplay: s.autoplay, Loop: s.loop, Interval: d}, func(i int) {
|
||||
v := data.Carousel(c, &s.index, len(dataPictures), data.CarouselOptions{Label: "Windows", Autoplay: s.autoplay, Loop: s.loop, Interval: d}, func(i int) {
|
||||
ui.Image(c, dataPictures[i].Source).Grow(1).Label(dataPictures[i].Alt)
|
||||
})
|
||||
v.Element.Size(520, 300)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
)
|
||||
|
||||
// dataDemo runs the demo of page id and returns its tester and event log.
|
||||
@@ -18,7 +18,7 @@ func dataDemo(t *testing.T, id string) (*ui.Tester, *[]string) {
|
||||
}
|
||||
events := &[]string{}
|
||||
tt := ui.NewTester(func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
core.Use(c, core.Settings{})
|
||||
ui.Scroll(c).Fill().Padding(24).Children(func() {
|
||||
dataPages[i].Demo(c, func(s string) { *events = append(*events, s) })
|
||||
})
|
||||
|
||||
@@ -6,8 +6,9 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/theme"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/datetime"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||
)
|
||||
|
||||
const dtCategory = "Date & Time"
|
||||
@@ -33,20 +34,20 @@ var datetimePages = []page{
|
||||
|
||||
// dtSection lays out a captioned block of a demo.
|
||||
func dtSection(c *ui.Context, title string, fn func()) {
|
||||
k := mujica.Tokens(c)
|
||||
k := core.Tokens(c)
|
||||
ui.Column(c).Gap(8).Children(func() {
|
||||
ui.Text(c, title).Font(theme.SerifFont).FontSize(mujica.FontSize(c, theme.H3Size)).TextColor(k.Text)
|
||||
ui.Text(c, title).Font(theme.SerifFont).FontSize(core.FontSize(c, theme.H3Size)).TextColor(k.Text)
|
||||
fn()
|
||||
})
|
||||
}
|
||||
|
||||
// dtStates lays labeled variants side by side.
|
||||
func dtStates(c *ui.Context, fn func(state func(name string, body func()))) {
|
||||
k := mujica.Tokens(c)
|
||||
k := core.Tokens(c)
|
||||
ui.Row(c).Gap(24).Wrap().AlignItems(ui.Start).Children(func() {
|
||||
fn(func(name string, body func()) {
|
||||
ui.Column(c).Gap(6).AlignItems(ui.Start).Children(func() {
|
||||
ui.Text(c, name).FontSize(mujica.FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted)
|
||||
ui.Text(c, name).FontSize(core.FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted)
|
||||
body()
|
||||
})
|
||||
})
|
||||
@@ -59,10 +60,10 @@ func dtControls(c *ui.Context, fn func()) {
|
||||
|
||||
var dtWeekStarts = []string{"Locale", "Sunday", "Monday", "Saturday"}
|
||||
|
||||
func dtWeekStart(s string) mujica.WeekStart {
|
||||
func dtWeekStart(s string) datetime.WeekStart {
|
||||
for i, n := range dtWeekStarts {
|
||||
if n == s {
|
||||
return mujica.WeekStart(i)
|
||||
return datetime.WeekStart(i)
|
||||
}
|
||||
}
|
||||
panic("gallery: unknown week start " + s)
|
||||
@@ -70,11 +71,11 @@ func dtWeekStart(s string) mujica.WeekStart {
|
||||
|
||||
func dtCalendarDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
day mujica.Date
|
||||
day core.Date
|
||||
weekStart string
|
||||
weekends, bound, frozen bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "051", func() state { return state{day: mujica.DateOf(time.Now()), weekStart: "Locale"} })
|
||||
s := ui.Local(c.Root(), "051", func() state { return state{day: core.DateOf(time.Now()), weekStart: "Locale"} })
|
||||
dtSection(c, "Calendar", func() {
|
||||
dtControls(c, func() {
|
||||
ui.Select(c, &s.weekStart, dtWeekStarts)
|
||||
@@ -82,16 +83,16 @@ func dtCalendarDemo(c *ui.Context, log func(string)) {
|
||||
ui.Checkbox(c, &s.bound, "Only this month")
|
||||
ui.Checkbox(c, &s.frozen, "Disabled")
|
||||
})
|
||||
opts := mujica.CalendarOptions{WeekStart: dtWeekStart(s.weekStart), Disabled: s.frozen, Label: "Calendar demo"}
|
||||
opts := datetime.CalendarOptions{WeekStart: dtWeekStart(s.weekStart), Disabled: s.frozen, Label: "Calendar demo"}
|
||||
if s.weekends {
|
||||
opts.DisableDate = func(d mujica.Date) bool { return d.Weekday() == time.Saturday || d.Weekday() == time.Sunday }
|
||||
opts.DisableDate = func(d core.Date) bool { return d.Weekday() == time.Saturday || d.Weekday() == time.Sunday }
|
||||
}
|
||||
if s.bound {
|
||||
now := mujica.DateOf(time.Now())
|
||||
opts.Min = mujica.Date{Year: now.Year, Month: now.Month, Day: 1}
|
||||
now := core.DateOf(time.Now())
|
||||
opts.Min = core.Date{Year: now.Year, Month: now.Month, Day: 1}
|
||||
opts.Max = opts.Min.AddMonths(1).AddDays(-1)
|
||||
}
|
||||
if mujica.Calendar(c, &s.day, opts).Changed() {
|
||||
if datetime.Calendar(c, &s.day, opts).Changed() {
|
||||
log("Calendar changed: " + s.day.String())
|
||||
}
|
||||
})
|
||||
@@ -99,11 +100,11 @@ func dtCalendarDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func dtDatePickerDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
a, b, d mujica.Date
|
||||
a, b, d core.Date
|
||||
clearable, slash bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "052", func() state {
|
||||
t := mujica.DateOf(time.Now())
|
||||
t := core.DateOf(time.Now())
|
||||
return state{a: t, d: t, clearable: true}
|
||||
})
|
||||
dtSection(c, "DatePicker", func() {
|
||||
@@ -115,24 +116,24 @@ func dtDatePickerDemo(c *ui.Context, log func(string)) {
|
||||
if s.slash {
|
||||
layout = "02/01/2006"
|
||||
}
|
||||
now := mujica.DateOf(time.Now())
|
||||
now := core.DateOf(time.Now())
|
||||
dtStates(c, func(state func(string, func())) {
|
||||
state("Default", func() {
|
||||
if mujica.DatePicker(c, &s.a, mujica.DatePickerOptions{Label: "Due date", Layout: layout, Clearable: s.clearable}).Changed() {
|
||||
if datetime.DatePicker(c, &s.a, datetime.DatePickerOptions{Label: "Due date", Layout: layout, Clearable: s.clearable}).Changed() {
|
||||
log("DatePicker changed: " + s.a.String())
|
||||
}
|
||||
})
|
||||
state("Next 30 days only", func() {
|
||||
opts := mujica.DatePickerOptions{Label: "Delivery", Layout: layout, Clearable: s.clearable, Min: now, Max: now.AddDays(30)}
|
||||
if mujica.DatePicker(c, &s.b, opts).Changed() {
|
||||
opts := datetime.DatePickerOptions{Label: "Delivery", Layout: layout, Clearable: s.clearable, Min: now, Max: now.AddDays(30)}
|
||||
if datetime.DatePicker(c, &s.b, opts).Changed() {
|
||||
log("DatePicker (bounded) changed: " + s.b.String())
|
||||
}
|
||||
})
|
||||
state("Read-only", func() {
|
||||
mujica.DatePicker(c, &s.d, mujica.DatePickerOptions{Label: "Signed on", Layout: layout, ReadOnly: true})
|
||||
datetime.DatePicker(c, &s.d, datetime.DatePickerOptions{Label: "Signed on", Layout: layout, ReadOnly: true})
|
||||
})
|
||||
state("Disabled", func() {
|
||||
mujica.DatePicker(c, &s.d, mujica.DatePickerOptions{Label: "Locked", Layout: layout, Disabled: true})
|
||||
datetime.DatePicker(c, &s.d, datetime.DatePickerOptions{Label: "Locked", Layout: layout, Disabled: true})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -140,7 +141,7 @@ func dtDatePickerDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func dtDateRangeDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
a mujica.DateRange
|
||||
a datetime.DateRange
|
||||
noTuesdays bool
|
||||
frozen bool
|
||||
fromThisWeek bool
|
||||
@@ -152,14 +153,14 @@ func dtDateRangeDemo(c *ui.Context, log func(string)) {
|
||||
ui.Checkbox(c, &s.fromThisWeek, "Start from today")
|
||||
ui.Checkbox(c, &s.frozen, "Disabled")
|
||||
})
|
||||
opts := mujica.DateRangePickerOptions{Label: "Stay", Disabled: s.frozen}
|
||||
opts := datetime.DateRangePickerOptions{Label: "Stay", Disabled: s.frozen}
|
||||
if s.noTuesdays {
|
||||
opts.DisableDate = func(d mujica.Date) bool { return d.Weekday() == time.Tuesday }
|
||||
opts.DisableDate = func(d core.Date) bool { return d.Weekday() == time.Tuesday }
|
||||
}
|
||||
if s.fromThisWeek {
|
||||
opts.Min = mujica.DateOf(time.Now())
|
||||
opts.Min = core.DateOf(time.Now())
|
||||
}
|
||||
r := mujica.DateRangePicker(c, &s.a, opts)
|
||||
r := datetime.DateRangePicker(c, &s.a, opts)
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("DateRangePicker changed: %v – %v (%d days)", s.a.Start, s.a.End, s.a.Days()))
|
||||
}
|
||||
@@ -171,12 +172,12 @@ func dtDateRangeDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func dtTimePickerDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
a, b mujica.TimeOfDay
|
||||
a, b datetime.TimeOfDay
|
||||
seconds, hour12 bool
|
||||
step string
|
||||
}
|
||||
s := ui.Local(c.Root(), "054", func() state {
|
||||
return state{a: mujica.TimeOfDay{Hour: 9, Minute: 30}, b: mujica.TimeOfDay{Hour: 10}, step: "1"}
|
||||
return state{a: datetime.TimeOfDay{Hour: 9, Minute: 30}, b: datetime.TimeOfDay{Hour: 10}, step: "1"}
|
||||
})
|
||||
dtSection(c, "TimePicker", func() {
|
||||
dtControls(c, func() {
|
||||
@@ -189,31 +190,31 @@ func dtTimePickerDemo(c *ui.Context, log func(string)) {
|
||||
if _, err := fmt.Sscan(s.step, &step); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
base := mujica.TimePickerOptions{Seconds: s.seconds, Hour12: s.hour12, MinuteStep: step}
|
||||
base := datetime.TimePickerOptions{Seconds: s.seconds, Hour12: s.hour12, MinuteStep: step}
|
||||
dtStates(c, func(state func(string, func())) {
|
||||
state("Any time", func() {
|
||||
o := base
|
||||
o.Label = "Alarm"
|
||||
if mujica.TimePicker(c, &s.a, o).Changed() {
|
||||
if datetime.TimePicker(c, &s.a, o).Changed() {
|
||||
log("TimePicker changed: " + s.a.String())
|
||||
}
|
||||
})
|
||||
state("Office hours 09:00–18:00", func() {
|
||||
o := base
|
||||
o.Label, o.Min, o.Max = "Meeting", mujica.TimeOfDay{Hour: 9}, mujica.TimeOfDay{Hour: 18}
|
||||
if mujica.TimePicker(c, &s.b, o).Changed() {
|
||||
o.Label, o.Min, o.Max = "Meeting", datetime.TimeOfDay{Hour: 9}, datetime.TimeOfDay{Hour: 18}
|
||||
if datetime.TimePicker(c, &s.b, o).Changed() {
|
||||
log("TimePicker (bounded) changed: " + s.b.String())
|
||||
}
|
||||
})
|
||||
state("Read-only", func() {
|
||||
o := base
|
||||
o.Label, o.ReadOnly = "Logged at", true
|
||||
mujica.TimePicker(c, &s.a, o)
|
||||
datetime.TimePicker(c, &s.a, o)
|
||||
})
|
||||
state("Disabled", func() {
|
||||
o := base
|
||||
o.Label, o.Disabled = "Locked", true
|
||||
mujica.TimePicker(c, &s.a, o)
|
||||
datetime.TimePicker(c, &s.a, o)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -221,21 +222,21 @@ func dtTimePickerDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func dtTimeRangeDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
v mujica.TimeRange
|
||||
v datetime.TimeRange
|
||||
overnight bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "055", func() state {
|
||||
return state{v: mujica.TimeRange{Start: mujica.TimeOfDay{Hour: 9}, End: mujica.TimeOfDay{Hour: 17}}}
|
||||
return state{v: datetime.TimeRange{Start: datetime.TimeOfDay{Hour: 9}, End: datetime.TimeOfDay{Hour: 17}}}
|
||||
})
|
||||
dtSection(c, "TimeRangePicker", func() {
|
||||
dtControls(c, func() {
|
||||
if ui.Checkbox(c, &s.overnight, "Allow overnight").Changed() && !s.overnight && s.v.Overnight() {
|
||||
s.v = mujica.TimeRange{Start: s.v.End, End: s.v.Start}
|
||||
s.v = datetime.TimeRange{Start: s.v.End, End: s.v.Start}
|
||||
log("TimeRangePicker swapped ends: overnight turned off")
|
||||
}
|
||||
})
|
||||
opts := mujica.TimeRangePickerOptions{Label: "Opening hours", AllowOvernight: s.overnight, MinuteStep: 15}
|
||||
if mujica.TimeRangePicker(c, &s.v, opts).Changed() {
|
||||
opts := datetime.TimeRangePickerOptions{Label: "Opening hours", AllowOvernight: s.overnight, MinuteStep: 15}
|
||||
if datetime.TimeRangePicker(c, &s.v, opts).Changed() {
|
||||
log(fmt.Sprintf("TimeRangePicker changed: %v – %v overnight=%v", s.v.Start, s.v.End, s.v.Overnight()))
|
||||
}
|
||||
})
|
||||
@@ -260,22 +261,22 @@ func dtDateTimeDemo(c *ui.Context, log func(string)) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
r := mujica.DateTimePicker(c, &s.v, mujica.DateTimePickerOptions{Label: "Meeting", Location: loc, Hour12: s.hour12})
|
||||
r := datetime.DateTimePicker(c, &s.v, datetime.DateTimePickerOptions{Label: "Meeting", Location: loc, Hour12: s.hour12})
|
||||
if r.Changed() {
|
||||
log("DateTimePicker changed: " + s.v.Format(time.RFC3339))
|
||||
}
|
||||
if r.Canceled() {
|
||||
log("DateTimePicker canceled")
|
||||
}
|
||||
k := mujica.Tokens(c)
|
||||
k := core.Tokens(c)
|
||||
ui.Text(c, "Try 2026-03-08 02:30 in America/New_York: the picker reports the DST gap.").
|
||||
FontSize(mujica.FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted)
|
||||
FontSize(core.FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted)
|
||||
})
|
||||
}
|
||||
|
||||
func dtMonthDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
v mujica.YearMonth
|
||||
v datetime.YearMonth
|
||||
clearable, past bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "057", func() state { return state{clearable: true} })
|
||||
@@ -284,23 +285,23 @@ func dtMonthDemo(c *ui.Context, log func(string)) {
|
||||
ui.Checkbox(c, &s.clearable, "Clearable")
|
||||
ui.Checkbox(c, &s.past, "Past months only")
|
||||
})
|
||||
opts := mujica.MonthPickerOptions{Label: "Billing month", Clearable: s.clearable,
|
||||
DisableMonth: func(m mujica.YearMonth) bool { return m.Month == time.February }}
|
||||
opts := datetime.MonthPickerOptions{Label: "Billing month", Clearable: s.clearable,
|
||||
DisableMonth: func(m datetime.YearMonth) bool { return m.Month == time.February }}
|
||||
if s.past {
|
||||
now := time.Now()
|
||||
opts.Max = mujica.YearMonth{Year: now.Year(), Month: now.Month()}
|
||||
opts.Max = datetime.YearMonth{Year: now.Year(), Month: now.Month()}
|
||||
}
|
||||
dtStates(c, func(state func(string, func())) {
|
||||
state("February unavailable", func() {
|
||||
if mujica.MonthPicker(c, &s.v, opts).Changed() {
|
||||
if datetime.MonthPicker(c, &s.v, opts).Changed() {
|
||||
log("MonthPicker changed: " + s.v.String())
|
||||
}
|
||||
})
|
||||
state("Read-only", func() {
|
||||
mujica.MonthPicker(c, &s.v, mujica.MonthPickerOptions{Label: "Closed month", ReadOnly: true})
|
||||
datetime.MonthPicker(c, &s.v, datetime.MonthPickerOptions{Label: "Closed month", ReadOnly: true})
|
||||
})
|
||||
state("Disabled", func() {
|
||||
mujica.MonthPicker(c, &s.v, mujica.MonthPickerOptions{Label: "Locked", Disabled: true})
|
||||
datetime.MonthPicker(c, &s.v, datetime.MonthPickerOptions{Label: "Locked", Disabled: true})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -314,19 +315,19 @@ func dtYearDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "058", func() state { return state{v: time.Now().Year(), clearable: true} })
|
||||
dtSection(c, "YearPicker", func() {
|
||||
dtControls(c, func() { ui.Checkbox(c, &s.clearable, "Clearable") })
|
||||
opts := mujica.YearPickerOptions{Label: "Vintage", Min: 1990, Max: time.Now().Year(), Clearable: s.clearable,
|
||||
opts := datetime.YearPickerOptions{Label: "Vintage", Min: 1990, Max: time.Now().Year(), Clearable: s.clearable,
|
||||
DisableYear: func(y int) bool { return y == 2020 }}
|
||||
dtStates(c, func(state func(string, func())) {
|
||||
state("1990 to now, 2020 unavailable", func() {
|
||||
if mujica.YearPicker(c, &s.v, opts).Changed() {
|
||||
if datetime.YearPicker(c, &s.v, opts).Changed() {
|
||||
log(fmt.Sprintf("YearPicker changed: %d", s.v))
|
||||
}
|
||||
})
|
||||
state("Read-only", func() {
|
||||
mujica.YearPicker(c, &s.v, mujica.YearPickerOptions{Label: "Founded", ReadOnly: true})
|
||||
datetime.YearPicker(c, &s.v, datetime.YearPickerOptions{Label: "Founded", ReadOnly: true})
|
||||
})
|
||||
state("Disabled", func() {
|
||||
mujica.YearPicker(c, &s.v, mujica.YearPickerOptions{Label: "Locked", Disabled: true})
|
||||
datetime.YearPicker(c, &s.v, datetime.YearPickerOptions{Label: "Locked", Disabled: true})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -334,47 +335,47 @@ func dtYearDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
// Code snippets mirror example_datetime_test.go.
|
||||
const (
|
||||
dtCalendarCode = `day := mujica.Date{Year: 2026, Month: time.October, Day: 5}
|
||||
dtCalendarCode = `day := core.Date{Year: 2026, Month: time.October, Day: 5}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.Calendar(c, &day, mujica.CalendarOptions{WeekStart: mujica.WeekStartMonday}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if datetime.Calendar(c, &day, datetime.CalendarOptions{WeekStart: datetime.WeekStartMonday}).Changed() {
|
||||
fmt.Println("chosen", day)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
dtDatePickerCode = `due := mujica.Date{Year: 2026, Month: time.October, Day: 5}
|
||||
dtDatePickerCode = `due := core.Date{Year: 2026, Month: time.October, Day: 5}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
opts := mujica.DatePickerOptions{Label: "Due date", Clearable: true,
|
||||
Min: mujica.Date{Year: 2026, Month: time.January, Day: 1}}
|
||||
if mujica.DatePicker(c, &due, opts).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
opts := datetime.DatePickerOptions{Label: "Due date", Clearable: true,
|
||||
Min: core.Date{Year: 2026, Month: time.January, Day: 1}}
|
||||
if datetime.DatePicker(c, &due, opts).Changed() {
|
||||
fmt.Println("due", due)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
dtDateRangeCode = `var stay mujica.DateRange
|
||||
dtDateRangeCode = `var stay datetime.DateRange
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.DateRangePicker(c, &stay, mujica.DateRangePickerOptions{Label: "Stay"})
|
||||
core.Use(c, core.Settings{})
|
||||
r := datetime.DateRangePicker(c, &stay, datetime.DateRangePickerOptions{Label: "Stay"})
|
||||
if r.Changed() {
|
||||
fmt.Println("stay", stay.Start, stay.End, stay.Days())
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
dtTimePickerCode = `alarm := mujica.TimeOfDay{Hour: 7, Minute: 30}
|
||||
dtTimePickerCode = `alarm := datetime.TimeOfDay{Hour: 7, Minute: 30}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
opts := mujica.TimePickerOptions{Label: "Alarm", Hour12: true, MinuteStep: 5}
|
||||
if mujica.TimePicker(c, &alarm, opts).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
opts := datetime.TimePickerOptions{Label: "Alarm", Hour12: true, MinuteStep: 5}
|
||||
if datetime.TimePicker(c, &alarm, opts).Changed() {
|
||||
fmt.Println("alarm", alarm)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
dtTimeRangeCode = `shift := mujica.TimeRange{Start: mujica.TimeOfDay{Hour: 22}, End: mujica.TimeOfDay{Hour: 6}}
|
||||
dtTimeRangeCode = `shift := datetime.TimeRange{Start: datetime.TimeOfDay{Hour: 22}, End: datetime.TimeOfDay{Hour: 6}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
opts := mujica.TimeRangePickerOptions{Label: "Night shift", AllowOvernight: true}
|
||||
if mujica.TimeRangePicker(c, &shift, opts).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
opts := datetime.TimeRangePickerOptions{Label: "Night shift", AllowOvernight: true}
|
||||
if datetime.TimeRangePicker(c, &shift, opts).Changed() {
|
||||
fmt.Println("shift", shift.Start, shift.End, shift.Overnight())
|
||||
}
|
||||
}
|
||||
@@ -385,27 +386,27 @@ if err != nil {
|
||||
}
|
||||
var meeting time.Time
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
opts := mujica.DateTimePickerOptions{Label: "Meeting", Location: tokyo, MinuteStep: 15}
|
||||
if mujica.DateTimePicker(c, &meeting, opts).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
opts := datetime.DateTimePickerOptions{Label: "Meeting", Location: tokyo, MinuteStep: 15}
|
||||
if datetime.DateTimePicker(c, &meeting, opts).Changed() {
|
||||
fmt.Println("meeting", meeting)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
dtMonthCode = `var month mujica.YearMonth
|
||||
dtMonthCode = `var month datetime.YearMonth
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
opts := mujica.MonthPickerOptions{Label: "Billing month", Clearable: true}
|
||||
if mujica.MonthPicker(c, &month, opts).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
opts := datetime.MonthPickerOptions{Label: "Billing month", Clearable: true}
|
||||
if datetime.MonthPicker(c, &month, opts).Changed() {
|
||||
fmt.Println("month", month)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
dtYearCode = `year := 2026
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
opts := mujica.YearPickerOptions{Label: "Vintage", Min: 1990, Max: 2030}
|
||||
if mujica.YearPicker(c, &year, opts).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
opts := datetime.YearPickerOptions{Label: "Vintage", Min: 1990, Max: 2030}
|
||||
if datetime.YearPicker(c, &year, opts).Changed() {
|
||||
fmt.Println("year", year)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
package main
|
||||
|
||||
const devStatCardCode = `value := "12,480"
|
||||
opts := mujica.StatCardOptions{Label: "Requests", Change: 0.12, HasChange: true, Trend: []float64{3, 5, 4, 8}}
|
||||
opts := devtools.StatCardOptions{Label: "Requests", Change: 0.12, HasChange: true, Trend: []float64{3, 5, 4, 8}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.StatCard(c, value, opts).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if devtools.StatCard(c, value, opts).Changed() {
|
||||
fmt.Println("retry")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 720, 480, 1)`
|
||||
|
||||
const devAlertListCode = `alerts := []mujica.AlertItem{{ID: "cpu", Title: "CPU usage high", Level: mujica.AlertWarning, Source: "worker", At: time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)}}
|
||||
const devAlertListCode = `alerts := []devtools.AlertItem{{ID: "cpu", Title: "CPU usage high", Level: devtools.AlertWarning, Source: "worker", At: time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.AlertList(c, alerts, mujica.AlertListOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := devtools.AlertList(c, alerts, devtools.AlertListOptions{})
|
||||
if r.Ack != "" {
|
||||
fmt.Println("ack", r.Ack)
|
||||
}
|
||||
@@ -23,17 +23,17 @@ view := func(c *ui.Context) {
|
||||
}
|
||||
ui.Render(view, 720, 480, 1)`
|
||||
|
||||
const devIncidentCardCode = `incident := mujica.Incident{Title: "API latency", Status: mujica.IncidentMonitoring, Impact: mujica.ImpactMinor, Updates: []mujica.IncidentUpdate{{Status: mujica.IncidentIdentified, Text: "Slow query located"}, {Status: mujica.IncidentMonitoring, Text: "Recovery observed"}}}
|
||||
const devIncidentCardCode = `incident := devtools.Incident{Title: "API latency", Status: devtools.IncidentMonitoring, Impact: devtools.ImpactMinor, Updates: []devtools.IncidentUpdate{{Status: devtools.IncidentIdentified, Text: "Slow query located"}, {Status: devtools.IncidentMonitoring, Text: "Recovery observed"}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.IncidentCard(c, incident)
|
||||
core.Use(c, core.Settings{})
|
||||
devtools.IncidentCard(c, incident)
|
||||
}
|
||||
ui.Render(view, 720, 480, 1)`
|
||||
|
||||
const devUptimeBarCode = `days := []mujica.UptimeDay{{Date: mujica.Date{Year: 2026, Month: 10, Day: 1}, Uptime: 1}, {Date: mujica.Date{Year: 2026, Month: 10, Day: 2}, Uptime: 0.91, Note: "Recovery"}}
|
||||
const devUptimeBarCode = `days := []devtools.UptimeDay{{Date: core.Date{Year: 2026, Month: 10, Day: 1}, Uptime: 1}, {Date: core.Date{Year: 2026, Month: 10, Day: 2}, Uptime: 0.91, Note: "Recovery"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.UptimeBar(c, days, mujica.UptimeBarOptions{Frame: mujica.ChartFrame{Label: "API uptime"}})
|
||||
core.Use(c, core.Settings{})
|
||||
r := devtools.UptimeBar(c, days, devtools.UptimeBarOptions{Frame: chart.ChartFrame{Label: "API uptime"}})
|
||||
if r.Changed() {
|
||||
fmt.Println("day", r.Point)
|
||||
}
|
||||
@@ -43,46 +43,46 @@ view := func(c *ui.Context) {
|
||||
}
|
||||
ui.Render(view, 720, 480, 1)`
|
||||
|
||||
const devServiceStatusCode = `services := []mujica.ServiceItem{{Name: "API", Health: mujica.ServiceOperational}, {Name: "Search", Health: mujica.ServiceDegraded, Note: "Slow queries"}}
|
||||
const devServiceStatusCode = `services := []devtools.ServiceItem{{Name: "API", Health: devtools.ServiceOperational}, {Name: "Search", Health: devtools.ServiceDegraded, Note: "Slow queries"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ServiceStatus(c, services, mujica.ServiceStatusOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
devtools.ServiceStatus(c, services, devtools.ServiceStatusOptions{})
|
||||
}
|
||||
ui.Render(view, 720, 480, 1)`
|
||||
|
||||
const devLogStreamCode = `lines := []mujica.LogLine{{Level: mujica.LogInfo, Text: "server ready"}, {Level: mujica.LogWarn, Text: "slow query"}}
|
||||
var state mujica.LogStreamState
|
||||
const devLogStreamCode = `lines := []code.LogLine{{Level: code.LogInfo, Text: "server ready"}, {Level: code.LogWarn, Text: "slow query"}}
|
||||
var state devtools.LogStreamState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.LogStream(c, lines, &state, mujica.LogStreamOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if devtools.LogStream(c, lines, &state, devtools.LogStreamOptions{}).Changed() {
|
||||
fmt.Println("filter changed")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 720, 480, 1)`
|
||||
|
||||
const devTraceWaterfallCode = `spans := []mujica.WaterfallSpan{{ID: "root", Name: "GET /api", Service: "gateway", Duration: 200 * time.Millisecond}, {ID: "db", Parent: "root", Name: "Query", Service: "database", Start: 20 * time.Millisecond, Duration: 80 * time.Millisecond}}
|
||||
const devTraceWaterfallCode = `spans := []devtools.WaterfallSpan{{ID: "root", Name: "GET /api", Service: "gateway", Duration: 200 * time.Millisecond}, {ID: "db", Parent: "root", Name: "Query", Service: "database", Start: 20 * time.Millisecond, Duration: 80 * time.Millisecond}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.TraceWaterfall(c, spans, mujica.TraceWaterfallOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if devtools.TraceWaterfall(c, spans, devtools.TraceWaterfallOptions{}).Changed() {
|
||||
fmt.Println("span selected")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 720, 480, 1)`
|
||||
|
||||
const devResourceGaugeCode = `readings := []mujica.ResourceReading{{Name: "CPU", Used: 45, Total: 100}, {Name: "Memory", Used: 6.4, Total: 8, Unit: "GB"}, {Name: "Disk", Used: 470, Total: 500, Unit: "GB"}}
|
||||
const devResourceGaugeCode = `readings := []devtools.ResourceReading{{Name: "CPU", Used: 45, Total: 100}, {Name: "Memory", Used: 6.4, Total: 8, Unit: "GB"}, {Name: "Disk", Used: 470, Total: 500, Unit: "GB"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.ResourceGauge(c, readings, mujica.ResourceGaugeOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if devtools.ResourceGauge(c, readings, devtools.ResourceGaugeOptions{}).Changed() {
|
||||
fmt.Println("retry")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 720, 480, 1)`
|
||||
|
||||
const devSystemMonitorCode = `times := []time.Time{time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC), time.Date(2026, 10, 5, 9, 0, 5, 0, time.UTC)}
|
||||
metrics := []mujica.MonitorMetric{{Name: "CPU", Unit: "%", Samples: []float64{42, 51}}, {Name: "Memory", Unit: "GB", Samples: []float64{3.2, 3.4}}}
|
||||
metrics := []devtools.MonitorMetric{{Name: "CPU", Unit: "%", Samples: []float64{42, 51}}, {Name: "Memory", Unit: "GB", Samples: []float64{3.2, 3.4}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.SystemMonitor(c, times, metrics, mujica.SystemMonitorOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if devtools.SystemMonitor(c, times, metrics, devtools.SystemMonitorOptions{}).Changed() {
|
||||
fmt.Println("retry")
|
||||
}
|
||||
}
|
||||
@@ -90,8 +90,8 @@ ui.Render(view, 720, 480, 1)`
|
||||
|
||||
const devRefreshIntervalSelectorCode = `interval := 30 * time.Second
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.RefreshIntervalSelector(c, &interval, mujica.RefreshIntervalSelectorOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := devtools.RefreshIntervalSelector(c, &interval, devtools.RefreshIntervalSelectorOptions{})
|
||||
if r.Refresh {
|
||||
fmt.Println("refresh")
|
||||
}
|
||||
@@ -101,21 +101,21 @@ view := func(c *ui.Context) {
|
||||
}
|
||||
ui.Render(view, 720, 480, 1)`
|
||||
|
||||
const devDashboardFilterBarCode = `var filters mujica.DashboardFilters
|
||||
opts := mujica.DashboardFilterBarOptions{Envs: []string{"production", "staging"}, Tags: []string{"api", "db"}}
|
||||
const devDashboardFilterBarCode = `var filters devtools.DashboardFilters
|
||||
opts := devtools.DashboardFilterBarOptions{Envs: []string{"production", "staging"}, Tags: []string{"api", "db"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.DashboardFilterBar(c, &filters, opts).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if devtools.DashboardFilterBar(c, &filters, opts).Changed() {
|
||||
fmt.Println("filters changed")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 720, 480, 1)`
|
||||
|
||||
const devJsonViewerCode = `data := ` + "`" + `{"server":{"port":8080},"ready":true}` + "`" + `
|
||||
var state mujica.JsonViewerState
|
||||
var state devtools.JsonViewerState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.JsonViewer(c, data, &state, mujica.JsonViewerOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := devtools.JsonViewer(c, data, &state, devtools.JsonViewerOptions{})
|
||||
if r.Copied != "" {
|
||||
fmt.Println("path copied")
|
||||
}
|
||||
@@ -124,29 +124,29 @@ ui.Render(view, 720, 480, 1)`
|
||||
|
||||
const devJsonEditorCode = `text := ` + "`" + `{"port":8080,"ready":true}` + "`" + `
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.JsonEditor(c, &text, mujica.JsonEditorOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := devtools.JsonEditor(c, &text, devtools.JsonEditorOptions{})
|
||||
if r.Changed {
|
||||
fmt.Println("valid", r.Valid)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 720, 480, 1)`
|
||||
|
||||
const devSchemaTreeCode = `roots := []mujica.SchemaNode{{Name: "app", Children: []mujica.SchemaNode{{Name: "users", Kind: mujica.SchemaTable, Children: []mujica.SchemaNode{{Name: "id", Kind: mujica.SchemaColumn, Detail: "bigint"}, {Name: "users_pk", Kind: mujica.SchemaIndex, Detail: "(id)"}}}}}}
|
||||
var state mujica.SchemaTreeState
|
||||
const devSchemaTreeCode = `roots := []devtools.SchemaNode{{Name: "app", Children: []devtools.SchemaNode{{Name: "users", Kind: devtools.SchemaTable, Children: []devtools.SchemaNode{{Name: "id", Kind: devtools.SchemaColumn, Detail: "bigint"}, {Name: "users_pk", Kind: devtools.SchemaIndex, Detail: "(id)"}}}}}}
|
||||
var state devtools.SchemaTreeState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.SchemaTree(c, &state, roots, mujica.SchemaTreeOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if devtools.SchemaTree(c, &state, roots, devtools.SchemaTreeOptions{}).Changed() {
|
||||
path, _ := state.Selected()
|
||||
fmt.Println("selected", path)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 720, 480, 1)`
|
||||
|
||||
const devApiRequestBuilderCode = `request := mujica.ApiRequest{Method: "POST", URL: "https://example.com/api", Headers: []mujica.KeyValue{{Key: "Accept", Value: "application/json", Enabled: true}}}
|
||||
const devApiRequestBuilderCode = `request := devtools.ApiRequest{Method: "POST", URL: "https://example.com/api", Headers: []devtools.KeyValue{{Key: "Accept", Value: "application/json", Enabled: true}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.ApiRequestBuilder(c, &request, mujica.ApiRequestBuilderOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := devtools.ApiRequestBuilder(c, &request, devtools.ApiRequestBuilderOptions{})
|
||||
if r.Send {
|
||||
fmt.Println("send", request.Method)
|
||||
}
|
||||
@@ -156,19 +156,19 @@ view := func(c *ui.Context) {
|
||||
}
|
||||
ui.Render(view, 720, 480, 1)`
|
||||
|
||||
const devResponseViewerCode = `response := &mujica.ApiResponse{Status: 200, Duration: 182 * time.Millisecond, Size: 32, Body: ` + "`" + `{"ready":true}` + "`" + `, Headers: []mujica.KeyValue{{Key: "Content-Type", Value: "application/json"}}}
|
||||
const devResponseViewerCode = `response := &devtools.ApiResponse{Status: 200, Duration: 182 * time.Millisecond, Size: 32, Body: ` + "`" + `{"ready":true}` + "`" + `, Headers: []devtools.KeyValue{{Key: "Content-Type", Value: "application/json"}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.ResponseViewer(c, response, mujica.ResponseViewerOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if devtools.ResponseViewer(c, response, devtools.ResponseViewerOptions{}).Changed() {
|
||||
fmt.Println("retry")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 720, 480, 1)`
|
||||
|
||||
const devKeyValueInputCode = `rows := []mujica.KeyValue{{Key: "Accept", Value: "application/json", Enabled: true}}
|
||||
const devKeyValueInputCode = `rows := []devtools.KeyValue{{Key: "Accept", Value: "application/json", Enabled: true}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.KeyValueInput(c, &rows, mujica.KeyValueInputOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if devtools.KeyValueInput(c, &rows, devtools.KeyValueInputOptions{}).Changed() {
|
||||
fmt.Println("rows", len(rows))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,18 +6,19 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/devtools"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func devJsonViewerDemo(c *ui.Context, log func(string)) {
|
||||
dtSection(c, "JsonViewer", func() {
|
||||
s := devControls(c, "453", log, false)
|
||||
type state struct {
|
||||
tree mujica.JsonViewerState
|
||||
tree devtools.JsonViewerState
|
||||
invalid bool
|
||||
}
|
||||
st := ui.Local(c.Root(), "453", func() state { return state{} })
|
||||
if mujica.Checkbox(c, &st.invalid, "Invalid JSON", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, &st.invalid, "Invalid JSON", input.CheckboxOptions{}).Changed() {
|
||||
log("JSON validity changed")
|
||||
}
|
||||
devPreview(c, s, func() {
|
||||
@@ -28,7 +29,7 @@ func devJsonViewerDemo(c *ui.Context, log func(string)) {
|
||||
if s.empty {
|
||||
data = `{}`
|
||||
}
|
||||
if mujica.JsonViewer(c, data, &st.tree, mujica.JsonViewerOptions{}).Copied != "" {
|
||||
if devtools.JsonViewer(c, data, &st.tree, devtools.JsonViewerOptions{}).Copied != "" {
|
||||
log("path copied")
|
||||
}
|
||||
})
|
||||
@@ -39,12 +40,12 @@ func devJsonEditorDemo(c *ui.Context, log func(string)) {
|
||||
dtSection(c, "JsonEditor", func() {
|
||||
s := devControls(c, "454", log, false)
|
||||
text := ui.Local(c.Root(), "454", func() string { return `{"port":8080,"ready":true}` })
|
||||
if mujica.Button(c, "Invalid sample", mujica.ButtonOptions{}).Clicked() {
|
||||
if input.Button(c, "Invalid sample", input.ButtonOptions{}).Clicked() {
|
||||
*text = "{\n \"port\": [1,2,]\n}"
|
||||
log("invalid sample loaded")
|
||||
}
|
||||
devPreview(c, s, func() {
|
||||
r := mujica.JsonEditor(c, text, mujica.JsonEditorOptions{})
|
||||
r := devtools.JsonEditor(c, text, devtools.JsonEditorOptions{})
|
||||
if r.Changed {
|
||||
log(fmt.Sprintf("JSON edited; valid=%t", r.Valid))
|
||||
}
|
||||
@@ -56,18 +57,18 @@ func devSchemaTreeDemo(c *ui.Context, log func(string)) {
|
||||
dtSection(c, "SchemaTree", func() {
|
||||
s := devControls(c, "455", log, false)
|
||||
type state struct {
|
||||
tree mujica.SchemaTreeState
|
||||
roots []mujica.SchemaNode
|
||||
tree devtools.SchemaTreeState
|
||||
roots []devtools.SchemaNode
|
||||
}
|
||||
st := ui.Local(c.Root(), "455", func() state {
|
||||
return state{roots: []mujica.SchemaNode{{Name: "app", Children: []mujica.SchemaNode{{Name: "users", Kind: mujica.SchemaTable, Children: []mujica.SchemaNode{{Name: "id", Kind: mujica.SchemaColumn, Detail: "bigint"}, {Name: "name", Kind: mujica.SchemaColumn, Detail: "text"}, {Name: "users_pk", Kind: mujica.SchemaIndex, Detail: "(id)"}}}, {Name: "active_users", Kind: mujica.SchemaView}}}}}
|
||||
return state{roots: []devtools.SchemaNode{{Name: "app", Children: []devtools.SchemaNode{{Name: "users", Kind: devtools.SchemaTable, Children: []devtools.SchemaNode{{Name: "id", Kind: devtools.SchemaColumn, Detail: "bigint"}, {Name: "name", Kind: devtools.SchemaColumn, Detail: "text"}, {Name: "users_pk", Kind: devtools.SchemaIndex, Detail: "(id)"}}}, {Name: "active_users", Kind: devtools.SchemaView}}}}}
|
||||
})
|
||||
devPreview(c, s, func() {
|
||||
roots := st.roots
|
||||
if s.empty {
|
||||
roots = nil
|
||||
}
|
||||
if mujica.SchemaTree(c, &st.tree, roots, mujica.SchemaTreeOptions{}).Changed() {
|
||||
if devtools.SchemaTree(c, &st.tree, roots, devtools.SchemaTreeOptions{}).Changed() {
|
||||
log("schema node selected")
|
||||
}
|
||||
})
|
||||
@@ -77,15 +78,15 @@ func devSchemaTreeDemo(c *ui.Context, log func(string)) {
|
||||
func devApiRequestBuilderDemo(c *ui.Context, log func(string)) {
|
||||
dtSection(c, "ApiRequestBuilder", func() {
|
||||
s := devControls(c, "456", log, false)
|
||||
request := ui.Local(c.Root(), "456", func() mujica.ApiRequest {
|
||||
return mujica.ApiRequest{URL: "https://example.com/api", Headers: []mujica.KeyValue{{Key: "Accept", Value: "application/json", Enabled: true}}}
|
||||
request := ui.Local(c.Root(), "456", func() devtools.ApiRequest {
|
||||
return devtools.ApiRequest{URL: "https://example.com/api", Headers: []devtools.KeyValue{{Key: "Accept", Value: "application/json", Enabled: true}}}
|
||||
})
|
||||
sending := ui.Local(c.Root(), "456-sending", func() bool { return false })
|
||||
if mujica.Checkbox(c, sending, "Sending", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, sending, "Sending", input.CheckboxOptions{}).Changed() {
|
||||
log("send state changed")
|
||||
}
|
||||
devPreview(c, s, func() {
|
||||
r := mujica.ApiRequestBuilder(c, request, mujica.ApiRequestBuilderOptions{Sending: *sending})
|
||||
r := devtools.ApiRequestBuilder(c, request, devtools.ApiRequestBuilderOptions{Sending: *sending})
|
||||
if r.Send {
|
||||
log("send requested")
|
||||
}
|
||||
@@ -100,18 +101,18 @@ func devResponseViewerDemo(c *ui.Context, log func(string)) {
|
||||
dtSection(c, "ResponseViewer", func() {
|
||||
s := devControls(c, "457", log, true)
|
||||
text := ui.Local(c.Root(), "457", func() bool { return false })
|
||||
if mujica.Checkbox(c, text, "Text body", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, text, "Text body", input.CheckboxOptions{}).Changed() {
|
||||
log("response format changed")
|
||||
}
|
||||
devPreview(c, s, func() {
|
||||
resp := &mujica.ApiResponse{Status: 200, Duration: 182 * time.Millisecond, Size: 2048, Body: `{"ready":true,"requests":1248}`, Headers: []mujica.KeyValue{{Key: "Content-Type", Value: "application/json"}, {Key: "X-Trace", Value: "demo"}}}
|
||||
resp := &devtools.ApiResponse{Status: 200, Duration: 182 * time.Millisecond, Size: 2048, Body: `{"ready":true,"requests":1248}`, Headers: []devtools.KeyValue{{Key: "Content-Type", Value: "application/json"}, {Key: "X-Trace", Value: "demo"}}}
|
||||
if *text {
|
||||
resp.Body = "Response received successfully."
|
||||
}
|
||||
if s.empty {
|
||||
resp = nil
|
||||
}
|
||||
if mujica.ResponseViewer(c, resp, mujica.ResponseViewerOptions{Height: 260, Status: s.status, Error: "Connection refused"}).Changed() {
|
||||
if devtools.ResponseViewer(c, resp, devtools.ResponseViewerOptions{Height: 260, Status: s.status, Error: "Connection refused"}).Changed() {
|
||||
log("retry requested")
|
||||
}
|
||||
})
|
||||
@@ -121,11 +122,11 @@ func devResponseViewerDemo(c *ui.Context, log func(string)) {
|
||||
func devKeyValueInputDemo(c *ui.Context, log func(string)) {
|
||||
dtSection(c, "KeyValueInput", func() {
|
||||
s := devControls(c, "458", log, false)
|
||||
rows := ui.Local(c.Root(), "458", func() []mujica.KeyValue {
|
||||
return []mujica.KeyValue{{Key: "Accept", Value: "application/json", Enabled: true}, {Key: "debug", Value: "true"}}
|
||||
rows := ui.Local(c.Root(), "458", func() []devtools.KeyValue {
|
||||
return []devtools.KeyValue{{Key: "Accept", Value: "application/json", Enabled: true}, {Key: "debug", Value: "true"}}
|
||||
})
|
||||
devPreview(c, s, func() {
|
||||
if mujica.KeyValueInput(c, rows, mujica.KeyValueInputOptions{Label: "Headers"}).Changed() {
|
||||
if devtools.KeyValueInput(c, rows, devtools.KeyValueInputOptions{Label: "Headers"}).Changed() {
|
||||
log("pairs edited")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -6,31 +6,35 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chart"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/devtools"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
type devDemoSettings struct {
|
||||
disabled bool
|
||||
status mujica.DataStatus
|
||||
status data.DataStatus
|
||||
empty bool
|
||||
}
|
||||
|
||||
func devControls(c *ui.Context, id string, log func(string), states bool) *devDemoSettings {
|
||||
s := ui.Local(c.Root(), id+"-controls", func() devDemoSettings { return devDemoSettings{} })
|
||||
dtControls(c, func() {
|
||||
if mujica.Checkbox(c, &s.disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, &s.disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log("disabled changed")
|
||||
}
|
||||
switch id {
|
||||
case "451", "454", "456", "458":
|
||||
default:
|
||||
if mujica.Checkbox(c, &s.empty, "Empty data", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, &s.empty, "Empty data", input.CheckboxOptions{}).Changed() {
|
||||
log("data state changed")
|
||||
}
|
||||
}
|
||||
if states {
|
||||
options := []mujica.SelectOption[mujica.DataStatus]{{Value: mujica.DataReady, Label: "Ready"}, {Value: mujica.DataLoading, Label: "Loading"}, {Value: mujica.DataFailed, Label: "Failed"}}
|
||||
if mujica.Select(c, &s.status, options, mujica.SelectOptions{Label: "Status"}).Changed() {
|
||||
options := []input.SelectOption[data.DataStatus]{{Value: data.DataReady, Label: "Ready"}, {Value: data.DataLoading, Label: "Loading"}, {Value: data.DataFailed, Label: "Failed"}}
|
||||
if input.Select(c, &s.status, options, input.SelectOptions{Label: "Status"}).Changed() {
|
||||
log("status changed")
|
||||
}
|
||||
}
|
||||
@@ -46,7 +50,7 @@ func devStatCardDemo(c *ui.Context, log func(string)) {
|
||||
dtSection(c, "StatCard", func() {
|
||||
s := devControls(c, "442", log, true)
|
||||
lower := ui.Local(c.Root(), "442-lower", func() bool { return false })
|
||||
if mujica.Checkbox(c, lower, "Lower is better", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, lower, "Lower is better", input.CheckboxOptions{}).Changed() {
|
||||
log("change direction updated")
|
||||
}
|
||||
devPreview(c, s, func() {
|
||||
@@ -55,7 +59,7 @@ func devStatCardDemo(c *ui.Context, log func(string)) {
|
||||
if s.empty {
|
||||
value, trend = "—", nil
|
||||
}
|
||||
if mujica.StatCard(c, value, mujica.StatCardOptions{Label: "Requests", Change: 0.12, HasChange: true, LowerIsBetter: *lower, Trend: trend, Status: s.status, Error: "Source unreachable"}).Changed() {
|
||||
if devtools.StatCard(c, value, devtools.StatCardOptions{Label: "Requests", Change: 0.12, HasChange: true, LowerIsBetter: *lower, Trend: trend, Status: s.status, Error: "Source unreachable"}).Changed() {
|
||||
log("retry requested")
|
||||
}
|
||||
})
|
||||
@@ -65,15 +69,15 @@ func devStatCardDemo(c *ui.Context, log func(string)) {
|
||||
func devAlertListDemo(c *ui.Context, log func(string)) {
|
||||
dtSection(c, "AlertList", func() {
|
||||
s := devControls(c, "443", log, false)
|
||||
alerts := ui.Local(c.Root(), "443", func() []mujica.AlertItem {
|
||||
return []mujica.AlertItem{{ID: "cpu", Title: "CPU usage high", Level: mujica.AlertWarning, Source: "worker", At: time.Now()}, {ID: "api", Title: "API unavailable", Level: mujica.AlertCritical, Source: "gateway", At: time.Now()}}
|
||||
alerts := ui.Local(c.Root(), "443", func() []devtools.AlertItem {
|
||||
return []devtools.AlertItem{{ID: "cpu", Title: "CPU usage high", Level: devtools.AlertWarning, Source: "worker", At: time.Now()}, {ID: "api", Title: "API unavailable", Level: devtools.AlertCritical, Source: "gateway", At: time.Now()}}
|
||||
})
|
||||
devPreview(c, s, func() {
|
||||
shown := *alerts
|
||||
if s.empty {
|
||||
shown = nil
|
||||
}
|
||||
r := mujica.AlertList(c, shown, mujica.AlertListOptions{})
|
||||
r := devtools.AlertList(c, shown, devtools.AlertListOptions{})
|
||||
for i := range *alerts {
|
||||
if r.Ack == (*alerts)[i].ID {
|
||||
(*alerts)[i].Acked = true
|
||||
@@ -91,17 +95,17 @@ func devAlertListDemo(c *ui.Context, log func(string)) {
|
||||
func devIncidentCardDemo(c *ui.Context, log func(string)) {
|
||||
dtSection(c, "IncidentCard", func() {
|
||||
s := devControls(c, "444", log, false)
|
||||
status := ui.Local(c.Root(), "444", func() mujica.IncidentStatus { return mujica.IncidentMonitoring })
|
||||
choices := []mujica.SelectOption[mujica.IncidentStatus]{{Value: mujica.IncidentInvestigating, Label: "Investigating"}, {Value: mujica.IncidentIdentified, Label: "Identified"}, {Value: mujica.IncidentMonitoring, Label: "Monitoring"}, {Value: mujica.IncidentResolved, Label: "Resolved"}}
|
||||
if mujica.Select(c, status, choices, mujica.SelectOptions{Label: "Incident status"}).Changed() {
|
||||
status := ui.Local(c.Root(), "444", func() devtools.IncidentStatus { return devtools.IncidentMonitoring })
|
||||
choices := []input.SelectOption[devtools.IncidentStatus]{{Value: devtools.IncidentInvestigating, Label: "Investigating"}, {Value: devtools.IncidentIdentified, Label: "Identified"}, {Value: devtools.IncidentMonitoring, Label: "Monitoring"}, {Value: devtools.IncidentResolved, Label: "Resolved"}}
|
||||
if input.Select(c, status, choices, input.SelectOptions{Label: "Incident status"}).Changed() {
|
||||
log("incident status changed")
|
||||
}
|
||||
devPreview(c, s, func() {
|
||||
updates := []mujica.IncidentUpdate{{Status: mujica.IncidentIdentified, Text: "Slow query located"}, {Status: mujica.IncidentMonitoring, Text: "Recovery observed"}}
|
||||
updates := []devtools.IncidentUpdate{{Status: devtools.IncidentIdentified, Text: "Slow query located"}, {Status: devtools.IncidentMonitoring, Text: "Recovery observed"}}
|
||||
if s.empty {
|
||||
updates = nil
|
||||
}
|
||||
mujica.IncidentCard(c, mujica.Incident{Title: "API latency", Status: *status, Impact: mujica.ImpactMinor, Updates: updates})
|
||||
devtools.IncidentCard(c, devtools.Incident{Title: "API latency", Status: *status, Impact: devtools.ImpactMinor, Updates: updates})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -109,11 +113,11 @@ func devIncidentCardDemo(c *ui.Context, log func(string)) {
|
||||
func devUptimeBarDemo(c *ui.Context, log func(string)) {
|
||||
dtSection(c, "UptimeBar", func() {
|
||||
s := devControls(c, "445", log, true)
|
||||
days := ui.Local(c.Root(), "445", func() []mujica.UptimeDay {
|
||||
out := make([]mujica.UptimeDay, 30)
|
||||
start := mujica.Date{Year: 2026, Month: 10, Day: 1}
|
||||
days := ui.Local(c.Root(), "445", func() []devtools.UptimeDay {
|
||||
out := make([]devtools.UptimeDay, 30)
|
||||
start := core.Date{Year: 2026, Month: 10, Day: 1}
|
||||
for i := range out {
|
||||
out[i] = mujica.UptimeDay{Date: start.AddDays(i), Uptime: 1}
|
||||
out[i] = devtools.UptimeDay{Date: start.AddDays(i), Uptime: 1}
|
||||
}
|
||||
out[7].Uptime, out[7].Note = 0.91, "2 h outage"
|
||||
out[12].Uptime = 0.98
|
||||
@@ -124,7 +128,7 @@ func devUptimeBarDemo(c *ui.Context, log func(string)) {
|
||||
if s.empty {
|
||||
data = nil
|
||||
}
|
||||
r := mujica.UptimeBar(c, data, mujica.UptimeBarOptions{Frame: mujica.ChartFrame{Label: "API uptime", Status: s.status, Error: "Source unreachable"}})
|
||||
r := devtools.UptimeBar(c, data, devtools.UptimeBarOptions{Frame: chart.ChartFrame{Label: "API uptime", Status: s.status, Error: "Source unreachable"}})
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("day %d", r.Point))
|
||||
}
|
||||
@@ -138,17 +142,17 @@ func devUptimeBarDemo(c *ui.Context, log func(string)) {
|
||||
func devServiceStatusDemo(c *ui.Context, log func(string)) {
|
||||
dtSection(c, "ServiceStatus", func() {
|
||||
s := devControls(c, "446", log, false)
|
||||
health := ui.Local(c.Root(), "446", func() mujica.ServiceHealth { return mujica.ServiceDegraded })
|
||||
choices := []mujica.SelectOption[mujica.ServiceHealth]{{Value: mujica.ServiceOperational, Label: "Operational"}, {Value: mujica.ServiceDegraded, Label: "Degraded"}, {Value: mujica.ServiceOutage, Label: "Outage"}}
|
||||
if mujica.Select(c, health, choices, mujica.SelectOptions{Label: "Search health"}).Changed() {
|
||||
health := ui.Local(c.Root(), "446", func() devtools.ServiceHealth { return devtools.ServiceDegraded })
|
||||
choices := []input.SelectOption[devtools.ServiceHealth]{{Value: devtools.ServiceOperational, Label: "Operational"}, {Value: devtools.ServiceDegraded, Label: "Degraded"}, {Value: devtools.ServiceOutage, Label: "Outage"}}
|
||||
if input.Select(c, health, choices, input.SelectOptions{Label: "Search health"}).Changed() {
|
||||
log("service health changed")
|
||||
}
|
||||
devPreview(c, s, func() {
|
||||
data := []mujica.ServiceItem{{Name: "API", Health: mujica.ServiceOperational}, {Name: "Search", Health: *health, Note: "Search cluster"}}
|
||||
data := []devtools.ServiceItem{{Name: "API", Health: devtools.ServiceOperational}, {Name: "Search", Health: *health, Note: "Search cluster"}}
|
||||
if s.empty {
|
||||
data = nil
|
||||
}
|
||||
mujica.ServiceStatus(c, data, mujica.ServiceStatusOptions{})
|
||||
devtools.ServiceStatus(c, data, devtools.ServiceStatusOptions{})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -157,15 +161,15 @@ func devResourceGaugeDemo(c *ui.Context, log func(string)) {
|
||||
dtSection(c, "ResourceGauge", func() {
|
||||
s := devControls(c, "449", log, true)
|
||||
used := ui.Local(c.Root(), "449", func() float64 { return 45 })
|
||||
if mujica.NumberInput(c, used, mujica.NumberInputOptions{Min: 0, Max: 100, Step: 5, Label: "CPU usage"}).Changed() {
|
||||
if input.NumberInput(c, used, input.NumberInputOptions{Min: 0, Max: 100, Step: 5, Label: "CPU usage"}).Changed() {
|
||||
log("resource reading changed")
|
||||
}
|
||||
devPreview(c, s, func() {
|
||||
data := []mujica.ResourceReading{{Name: "CPU", Used: *used, Total: 100}, {Name: "Memory", Used: 6.4, Total: 8, Unit: "GB"}, {Name: "Disk", Used: 470, Total: 500, Unit: "GB"}}
|
||||
data := []devtools.ResourceReading{{Name: "CPU", Used: *used, Total: 100}, {Name: "Memory", Used: 6.4, Total: 8, Unit: "GB"}, {Name: "Disk", Used: 470, Total: 500, Unit: "GB"}}
|
||||
if s.empty {
|
||||
data = nil
|
||||
}
|
||||
if mujica.ResourceGauge(c, data, mujica.ResourceGaugeOptions{Status: s.status, Error: "Source unreachable"}).Changed() {
|
||||
if devtools.ResourceGauge(c, data, devtools.ResourceGaugeOptions{Status: s.status, Error: "Source unreachable"}).Changed() {
|
||||
log("retry requested")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -6,21 +6,23 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/code"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/devtools"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func devLogStreamDemo(c *ui.Context, log func(string)) {
|
||||
dtSection(c, "LogStream", func() {
|
||||
s := devControls(c, "447", log, false)
|
||||
type state struct {
|
||||
lines []mujica.LogLine
|
||||
stream mujica.LogStreamState
|
||||
lines []code.LogLine
|
||||
stream devtools.LogStreamState
|
||||
}
|
||||
st := ui.Local(c.Root(), "447", func() state {
|
||||
return state{lines: []mujica.LogLine{{Level: mujica.LogInfo, Text: "server ready"}, {Level: mujica.LogWarn, Text: "slow query"}}}
|
||||
return state{lines: []code.LogLine{{Level: code.LogInfo, Text: "server ready"}, {Level: code.LogWarn, Text: "slow query"}}}
|
||||
})
|
||||
if mujica.Button(c, "Append line", mujica.ButtonOptions{}).Clicked() {
|
||||
st.lines = append(st.lines, mujica.LogLine{Time: c.Now(), Level: mujica.LogInfo, Text: fmt.Sprintf("request %d completed", len(st.lines))})
|
||||
if input.Button(c, "Append line", input.ButtonOptions{}).Clicked() {
|
||||
st.lines = append(st.lines, code.LogLine{Time: c.Now(), Level: code.LogInfo, Text: fmt.Sprintf("request %d completed", len(st.lines))})
|
||||
log("line appended")
|
||||
}
|
||||
devPreview(c, s, func() {
|
||||
@@ -28,7 +30,7 @@ func devLogStreamDemo(c *ui.Context, log func(string)) {
|
||||
if s.empty {
|
||||
data = nil
|
||||
}
|
||||
if mujica.LogStream(c, data, &st.stream, mujica.LogStreamOptions{}).Changed() {
|
||||
if devtools.LogStream(c, data, &st.stream, devtools.LogStreamOptions{}).Changed() {
|
||||
log("pause or level changed")
|
||||
}
|
||||
})
|
||||
@@ -38,15 +40,15 @@ func devLogStreamDemo(c *ui.Context, log func(string)) {
|
||||
func devTraceWaterfallDemo(c *ui.Context, log func(string)) {
|
||||
dtSection(c, "TraceWaterfall", func() {
|
||||
s := devControls(c, "448", log, false)
|
||||
spans := ui.Local(c.Root(), "448", func() []mujica.WaterfallSpan {
|
||||
return []mujica.WaterfallSpan{{ID: "root", Name: "GET /api", Service: "gateway", Duration: 320 * time.Millisecond}, {ID: "db", Parent: "root", Name: "Query", Service: "database", Start: 40 * time.Millisecond, Duration: 90 * time.Millisecond}, {ID: "pay", Parent: "root", Name: "Charge", Service: "payments", Start: 230 * time.Millisecond, Duration: 80 * time.Millisecond, Error: true}}
|
||||
spans := ui.Local(c.Root(), "448", func() []devtools.WaterfallSpan {
|
||||
return []devtools.WaterfallSpan{{ID: "root", Name: "GET /api", Service: "gateway", Duration: 320 * time.Millisecond}, {ID: "db", Parent: "root", Name: "Query", Service: "database", Start: 40 * time.Millisecond, Duration: 90 * time.Millisecond}, {ID: "pay", Parent: "root", Name: "Charge", Service: "payments", Start: 230 * time.Millisecond, Duration: 80 * time.Millisecond, Error: true}}
|
||||
})
|
||||
devPreview(c, s, func() {
|
||||
data := *spans
|
||||
if s.empty {
|
||||
data = nil
|
||||
}
|
||||
if mujica.TraceWaterfall(c, data, mujica.TraceWaterfallOptions{}).Changed() {
|
||||
if devtools.TraceWaterfall(c, data, devtools.TraceWaterfallOptions{}).Changed() {
|
||||
log("span selected")
|
||||
}
|
||||
})
|
||||
@@ -58,10 +60,10 @@ func devSystemMonitorDemo(c *ui.Context, log func(string)) {
|
||||
s := devControls(c, "450", log, true)
|
||||
type state struct {
|
||||
times []time.Time
|
||||
metrics []mujica.MonitorMetric
|
||||
metrics []devtools.MonitorMetric
|
||||
}
|
||||
st := ui.Local(c.Root(), "450", func() state {
|
||||
out := state{metrics: []mujica.MonitorMetric{{Name: "CPU", Unit: "%"}, {Name: "Memory", Unit: "GB"}}}
|
||||
out := state{metrics: []devtools.MonitorMetric{{Name: "CPU", Unit: "%"}, {Name: "Memory", Unit: "GB"}}}
|
||||
for i := range 20 {
|
||||
out.times = append(out.times, time.Date(2026, 10, 5, 9, 0, i*5, 0, time.UTC))
|
||||
out.metrics[0].Samples = append(out.metrics[0].Samples, 40+float64(i%7)*3)
|
||||
@@ -69,7 +71,7 @@ func devSystemMonitorDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
return out
|
||||
})
|
||||
if mujica.Button(c, "Append sample", mujica.ButtonOptions{}).Clicked() {
|
||||
if input.Button(c, "Append sample", input.ButtonOptions{}).Clicked() {
|
||||
st.times = append(append([]time.Time(nil), st.times...), st.times[len(st.times)-1].Add(5*time.Second))
|
||||
for i := range st.metrics {
|
||||
last := st.metrics[i].Samples[len(st.metrics[i].Samples)-1]
|
||||
@@ -82,7 +84,7 @@ func devSystemMonitorDemo(c *ui.Context, log func(string)) {
|
||||
if s.empty {
|
||||
times, metrics = nil, nil
|
||||
}
|
||||
if mujica.SystemMonitor(c, times, metrics, mujica.SystemMonitorOptions{Status: s.status, Error: "Agent offline"}).Changed() {
|
||||
if devtools.SystemMonitor(c, times, metrics, devtools.SystemMonitorOptions{Status: s.status, Error: "Agent offline"}).Changed() {
|
||||
log("retry requested")
|
||||
}
|
||||
})
|
||||
@@ -94,11 +96,11 @@ func devRefreshIntervalSelectorDemo(c *ui.Context, log func(string)) {
|
||||
s := devControls(c, "451", log, false)
|
||||
interval := ui.Local(c.Root(), "451", func() time.Duration { return 0 })
|
||||
refreshing := ui.Local(c.Root(), "451-refreshing", func() bool { return false })
|
||||
if mujica.Checkbox(c, refreshing, "Refreshing", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, refreshing, "Refreshing", input.CheckboxOptions{}).Changed() {
|
||||
log("refresh state changed")
|
||||
}
|
||||
devPreview(c, s, func() {
|
||||
r := mujica.RefreshIntervalSelector(c, interval, mujica.RefreshIntervalSelectorOptions{Refreshing: *refreshing})
|
||||
r := devtools.RefreshIntervalSelector(c, interval, devtools.RefreshIntervalSelectorOptions{Refreshing: *refreshing})
|
||||
if r.Refresh {
|
||||
log("refresh requested")
|
||||
}
|
||||
@@ -112,14 +114,14 @@ func devRefreshIntervalSelectorDemo(c *ui.Context, log func(string)) {
|
||||
func devDashboardFilterBarDemo(c *ui.Context, log func(string)) {
|
||||
dtSection(c, "DashboardFilterBar", func() {
|
||||
s := devControls(c, "452", log, false)
|
||||
filters := ui.Local(c.Root(), "452", func() mujica.DashboardFilters { return mujica.DashboardFilters{} })
|
||||
filters := ui.Local(c.Root(), "452", func() devtools.DashboardFilters { return devtools.DashboardFilters{} })
|
||||
devPreview(c, s, func() {
|
||||
opts := mujica.DashboardFilterBarOptions{Envs: []string{"production", "staging"}, Tags: []string{"api", "db", "web"}}
|
||||
opts := devtools.DashboardFilterBarOptions{Envs: []string{"production", "staging"}, Tags: []string{"api", "db", "web"}}
|
||||
if s.empty {
|
||||
opts.Envs, opts.Tags = nil, nil
|
||||
*filters = mujica.DashboardFilters{}
|
||||
*filters = devtools.DashboardFilters{}
|
||||
}
|
||||
if mujica.DashboardFilterBar(c, filters, opts).Changed() {
|
||||
if devtools.DashboardFilterBar(c, filters, opts).Changed() {
|
||||
log("filters changed")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -5,12 +5,12 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
)
|
||||
|
||||
func devPageTester(p page, mode mujica.Mode, events *[]string) *ui.Tester {
|
||||
func devPageTester(p page, mode core.Mode, events *[]string) *ui.Tester {
|
||||
return ui.NewTester(func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{Mode: mode})
|
||||
core.Use(c, core.Settings{Mode: mode})
|
||||
ui.Column(c).Padding(16).Children(func() { p.Demo(c, func(s string) { *events = append(*events, s) }) })
|
||||
}, 1000, 800)
|
||||
}
|
||||
@@ -21,7 +21,7 @@ func TestDevtoolsPagesControlsAndThemes(t *testing.T) {
|
||||
}
|
||||
for _, p := range devtoolsPages {
|
||||
t.Run(p.Name, func(t *testing.T) {
|
||||
for _, mode := range []mujica.Mode{mujica.Light, mujica.Dark} {
|
||||
for _, mode := range []core.Mode{core.Light, core.Dark} {
|
||||
var events []string
|
||||
tt := devPageTester(p, mode, &events)
|
||||
if p.Category != "Dashboards & Dev Tools" || p.Code == "" || p.Keys == "" {
|
||||
@@ -70,7 +70,7 @@ func TestDevtoolsDemoEvents(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
var events []string
|
||||
tt := devPageTester(p, mujica.Light, &events)
|
||||
tt := devPageTester(p, core.Light, &events)
|
||||
if err := tt.Click(tc.label); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/display"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
var displayPages = []page{
|
||||
@@ -43,7 +45,7 @@ type textState struct {
|
||||
|
||||
func textDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "011", func() textState { return textState{maxLines: 0, selectable: true, name: "Coronation.md"} })
|
||||
mujica.Heading(c, "Text", mujica.HeadingOptions{Subtitle: "换行、省略、选择复制与可编辑模式", Divider: true})
|
||||
display.Heading(c, "Text", display.HeadingOptions{Subtitle: "换行、省略、选择复制与可编辑模式", Divider: true})
|
||||
displaySection(c, "Parameters", func() {
|
||||
displayControls(c, func() {
|
||||
ui.Text(c, fmt.Sprintf("MaxLines %d", int(s.maxLines)))
|
||||
@@ -55,12 +57,12 @@ func textDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
displaySection(c, "Display", func() {
|
||||
ui.Box(c).Width(360).Children(func() {
|
||||
mujica.Text(c, "Velvet curtains fall over the long gallery. Every candle leans toward the open door, and the quartet plays on while the guests take their places beneath the chandeliers.",
|
||||
mujica.TextOptions{MaxLines: int(s.maxLines), Selectable: s.selectable})
|
||||
display.Text(c, "Velvet curtains fall over the long gallery. Every candle leans toward the open door, and the quartet plays on while the guests take their places beneath the chandeliers.",
|
||||
display.TextOptions{MaxLines: int(s.maxLines), Selectable: s.selectable})
|
||||
})
|
||||
})
|
||||
displaySection(c, "Editable", func() {
|
||||
e := mujica.EditableText(c, &s.name, mujica.EditableTextOptions{Saving: s.saving, Error: s.saveErr, Label: "File name"})
|
||||
e := display.EditableText(c, &s.name, display.EditableTextOptions{Saving: s.saving, Error: s.saveErr, Label: "File name"})
|
||||
if e.Changed() {
|
||||
if s.failNext {
|
||||
s.saveErr = "Save failed: the name is already taken."
|
||||
@@ -76,9 +78,9 @@ func textDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
}
|
||||
|
||||
const textCode = `mujica.Text(c, "Order ID: 4821-AX", mujica.TextOptions{Selectable: true})
|
||||
mujica.Text(c, "A long note that stops after two lines.", mujica.TextOptions{MaxLines: 2})
|
||||
if mujica.EditableText(c, &name, mujica.EditableTextOptions{Error: saveErr}).Changed() {
|
||||
const textCode = `display.Text(c, "Order ID: 4821-AX", display.TextOptions{Selectable: true})
|
||||
display.Text(c, "A long note that stops after two lines.", display.TextOptions{MaxLines: 2})
|
||||
if display.EditableText(c, &name, display.EditableTextOptions{Error: saveErr}).Changed() {
|
||||
saveErr = "" // save name here; set saveErr on failure
|
||||
}`
|
||||
|
||||
@@ -89,7 +91,7 @@ type headingState struct {
|
||||
|
||||
func headingDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "012", func() headingState { return headingState{level: 1, subtitle: true, divide: true} })
|
||||
mujica.Heading(c, "Heading", mujica.HeadingOptions{Subtitle: "三级衬线标题、副标题与装饰分隔线", Divider: true})
|
||||
display.Heading(c, "Heading", display.HeadingOptions{Subtitle: "三级衬线标题、副标题与装饰分隔线", Divider: true})
|
||||
displaySection(c, "Parameters", func() {
|
||||
displayControls(c, func() {
|
||||
ui.Text(c, fmt.Sprintf("Level %d", int(s.level)))
|
||||
@@ -105,17 +107,17 @@ func headingDemo(c *ui.Context, log func(string)) {
|
||||
if s.subtitle {
|
||||
sub = "Ceremonies of the winter season"
|
||||
}
|
||||
mujica.Heading(c, "The Grand Hall", mujica.HeadingOptions{Level: int(s.level), Subtitle: sub, Divider: s.divide})
|
||||
display.Heading(c, "The Grand Hall", display.HeadingOptions{Level: int(s.level), Subtitle: sub, Divider: s.divide})
|
||||
})
|
||||
displaySection(c, "Levels", func() {
|
||||
for l := 1; l <= 3; l++ {
|
||||
mujica.Heading(c, fmt.Sprintf("Level %d · 宫廷", l), mujica.HeadingOptions{Level: l})
|
||||
display.Heading(c, fmt.Sprintf("Level %d · 宫廷", l), display.HeadingOptions{Level: l})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const headingCode = `mujica.Heading(c, "The Grand Hall", mujica.HeadingOptions{Subtitle: "Winter ceremonies", Divider: true})
|
||||
mujica.Heading(c, "Guest Ledger", mujica.HeadingOptions{Level: 2})`
|
||||
const headingCode = `display.Heading(c, "The Grand Hall", display.HeadingOptions{Subtitle: "Winter ceremonies", Divider: true})
|
||||
display.Heading(c, "Guest Ledger", display.HeadingOptions{Level: 2})`
|
||||
|
||||
type iconState struct {
|
||||
name string
|
||||
@@ -124,12 +126,12 @@ type iconState struct {
|
||||
label bool
|
||||
}
|
||||
|
||||
var iconTones = map[string]mujica.IconTone{"Inherit": mujica.IconInherit, "Muted": mujica.IconMuted, "Accent": mujica.IconAccent,
|
||||
"Success": mujica.IconSuccess, "Warning": mujica.IconWarning, "Danger": mujica.IconDanger, "Info": mujica.IconInfo}
|
||||
var iconTones = map[string]display.IconTone{"Inherit": display.IconInherit, "Muted": display.IconMuted, "Accent": display.IconAccent,
|
||||
"Success": display.IconSuccess, "Warning": display.IconWarning, "Danger": display.IconDanger, "Info": display.IconInfo}
|
||||
|
||||
func iconDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "013", func() iconState { return iconState{name: "star", size: "24", tone: "Accent"} })
|
||||
mujica.Heading(c, "Icon", mujica.HeadingOptions{Subtitle: "按名称取图标;尺寸与语义颜色;缺失时可见回退", Divider: true})
|
||||
display.Heading(c, "Icon", display.HeadingOptions{Subtitle: "按名称取图标;尺寸与语义颜色;缺失时可见回退", Divider: true})
|
||||
displaySection(c, "Parameters", func() {
|
||||
displayControls(c, func() {
|
||||
if ui.Select(c, &s.name, []string{"star", "bell", "settings", "moon", "sun", "no-such-icon"}).Changed() {
|
||||
@@ -151,24 +153,24 @@ func iconDemo(c *ui.Context, log func(string)) {
|
||||
if s.label {
|
||||
label = "Preview icon"
|
||||
}
|
||||
mujica.Icon(c, s.name, mujica.IconOptions{Size: size, Tone: iconTones[s.tone], Label: label})
|
||||
display.Icon(c, s.name, display.IconOptions{Size: size, Tone: iconTones[s.tone], Label: label})
|
||||
})
|
||||
displaySection(c, "Tones and fallback", func() {
|
||||
ui.Row(c).Gap(14).Children(func() {
|
||||
for _, t := range []string{"Inherit", "Muted", "Accent", "Success", "Warning", "Danger", "Info"} {
|
||||
mujica.Icon(c, "circle-check", mujica.IconOptions{Size: 20, Tone: iconTones[t]}).Key(t)
|
||||
display.Icon(c, "circle-check", display.IconOptions{Size: 20, Tone: iconTones[t]}).Key(t)
|
||||
}
|
||||
mujica.Icon(c, "missing-name", mujica.IconOptions{Size: 20})
|
||||
display.Icon(c, "missing-name", display.IconOptions{Size: 20})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const iconCode = `ui.Row(c).Gap(8).Children(func() {
|
||||
mujica.Icon(c, "star", mujica.IconOptions{})
|
||||
mujica.Icon(c, "circle-check", mujica.IconOptions{Size: 20, Tone: mujica.IconSuccess, Label: "Done"})
|
||||
display.Icon(c, "star", display.IconOptions{})
|
||||
display.Icon(c, "circle-check", display.IconOptions{Size: 20, Tone: display.IconSuccess, Label: "Done"})
|
||||
})`
|
||||
|
||||
var displayPeople = []mujica.AvatarItem{
|
||||
var displayPeople = []display.AvatarItem{
|
||||
{Name: "Sakiko Togawa"}, {Name: "Uika Misumi"}, {Name: "Mutsumi Wakaba"}, {Name: "Umiri Yahata"},
|
||||
{Name: "Nyamu Yutenji"}, {Name: "Tomori Takamatsu"}, {Name: "Anon Chihaya"}, {Name: "Rana Kaname"},
|
||||
}
|
||||
@@ -177,7 +179,7 @@ type avatarGroupState struct{ max, count float64 }
|
||||
|
||||
func avatarGroupDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "016", func() avatarGroupState { return avatarGroupState{max: 3, count: 6} })
|
||||
mujica.Heading(c, "AvatarGroup", mujica.HeadingOptions{Subtitle: "最大显示数量、剩余数量与完整名单", Divider: true})
|
||||
display.Heading(c, "AvatarGroup", display.HeadingOptions{Subtitle: "最大显示数量、剩余数量与完整名单", Divider: true})
|
||||
displaySection(c, "Parameters", func() {
|
||||
displayControls(c, func() {
|
||||
ui.Text(c, fmt.Sprintf("Max %d", int(s.max)))
|
||||
@@ -191,9 +193,9 @@ func avatarGroupDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
})
|
||||
displaySection(c, "Preview", func() {
|
||||
for i, g := range []mujica.AvatarGroupView{
|
||||
mujica.AvatarGroup(c, displayPeople[:int(s.count)], mujica.AvatarGroupOptions{Max: int(s.max)}),
|
||||
mujica.AvatarGroup(c, displayPeople, mujica.AvatarGroupOptions{Max: 4, Size: 24}),
|
||||
for i, g := range []display.AvatarGroupView{
|
||||
display.AvatarGroup(c, displayPeople[:int(s.count)], display.AvatarGroupOptions{Max: int(s.max)}),
|
||||
display.AvatarGroup(c, displayPeople, display.AvatarGroupOptions{Max: 4, Size: 24}),
|
||||
} {
|
||||
if g.Toggled {
|
||||
log(fmt.Sprintf("group %d open=%v", i+1, g.Open))
|
||||
@@ -202,7 +204,7 @@ func avatarGroupDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
}
|
||||
|
||||
const avatarGroupCode = `mujica.AvatarGroup(c, people, mujica.AvatarGroupOptions{Max: 3})`
|
||||
const avatarGroupCode = `display.AvatarGroup(c, people, display.AvatarGroupOptions{Max: 3})`
|
||||
|
||||
type badgeState struct {
|
||||
count, max float64
|
||||
@@ -210,14 +212,14 @@ type badgeState struct {
|
||||
tone, pos string
|
||||
}
|
||||
|
||||
var badgeTones = map[string]mujica.BadgeTone{"Accent": mujica.BadgeAccent, "Neutral": mujica.BadgeNeutral, "Success": mujica.BadgeSuccess,
|
||||
"Warning": mujica.BadgeWarning, "Danger": mujica.BadgeDanger, "Info": mujica.BadgeInfo}
|
||||
var badgePositions = map[string]mujica.BadgePosition{"TopRight": mujica.BadgeTopRight, "TopLeft": mujica.BadgeTopLeft,
|
||||
"BottomRight": mujica.BadgeBottomRight, "BottomLeft": mujica.BadgeBottomLeft}
|
||||
var badgeTones = map[string]display.BadgeTone{"Accent": display.BadgeAccent, "Neutral": display.BadgeNeutral, "Success": display.BadgeSuccess,
|
||||
"Warning": display.BadgeWarning, "Danger": display.BadgeDanger, "Info": display.BadgeInfo}
|
||||
var badgePositions = map[string]display.BadgePosition{"TopRight": display.BadgeTopRight, "TopLeft": display.BadgeTopLeft,
|
||||
"BottomRight": display.BadgeBottomRight, "BottomLeft": display.BadgeBottomLeft}
|
||||
|
||||
func badgeDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "017", func() badgeState { return badgeState{count: 120, max: 99, tone: "Accent", pos: "TopRight"} })
|
||||
mujica.Heading(c, "Badge", mujica.HeadingOptions{Subtitle: "文字或计数、上限、位置与完整朗读", Divider: true})
|
||||
display.Heading(c, "Badge", display.HeadingOptions{Subtitle: "文字或计数、上限、位置与完整朗读", Divider: true})
|
||||
displaySection(c, "Parameters", func() {
|
||||
displayControls(c, func() {
|
||||
ui.Text(c, fmt.Sprintf("Count %d", int(s.count)))
|
||||
@@ -233,24 +235,24 @@ func badgeDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
})
|
||||
})
|
||||
opts := mujica.BadgeOptions{Text: s.text, Count: int(s.count), Max: int(s.max), Tone: badgeTones[s.tone], Label: "Unread"}
|
||||
opts := display.BadgeOptions{Text: s.text, Count: int(s.count), Max: int(s.max), Tone: badgeTones[s.tone], Label: "Unread"}
|
||||
displaySection(c, "Inline", func() {
|
||||
ui.Row(c).Gap(8).AlignItems(ui.Center).Children(func() {
|
||||
ui.Text(c, "Inbox")
|
||||
mujica.Badge(c, opts, nil)
|
||||
display.Badge(c, opts, nil)
|
||||
})
|
||||
})
|
||||
displaySection(c, "Corner", func() {
|
||||
opts.Position = badgePositions[s.pos]
|
||||
ui.Box(c).Padding(10).Children(func() {
|
||||
mujica.Badge(c, opts, func() { mujica.Avatar(c, "Sakiko Togawa", mujica.AvatarOptions{Size: 40}) })
|
||||
display.Badge(c, opts, func() { display.Avatar(c, "Sakiko Togawa", display.AvatarOptions{Size: 40}) })
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const badgeCode = `mujica.Badge(c, mujica.BadgeOptions{Text: "New", Tone: mujica.BadgeNeutral}, nil)
|
||||
mujica.Badge(c, mujica.BadgeOptions{Count: unread, Label: "Unread", Position: mujica.BadgeTopRight}, func() {
|
||||
mujica.Avatar(c, "Sakiko Togawa", mujica.AvatarOptions{})
|
||||
const badgeCode = `display.Badge(c, display.BadgeOptions{Text: "New", Tone: display.BadgeNeutral}, nil)
|
||||
display.Badge(c, display.BadgeOptions{Count: unread, Label: "Unread", Position: display.BadgeTopRight}, func() {
|
||||
display.Avatar(c, "Sakiko Togawa", display.AvatarOptions{})
|
||||
})`
|
||||
|
||||
type tagState struct {
|
||||
@@ -262,14 +264,14 @@ func tagDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "018", func() tagState {
|
||||
return tagState{tags: []string{"Baroque", "Gothic", "Rococo", "Court"}, icon: true, clickable: true, closable: true}
|
||||
})
|
||||
mujica.Heading(c, "Tag", mujica.HeadingOptions{Subtitle: "标签文字、图标;点击与关闭分离", Divider: true})
|
||||
display.Heading(c, "Tag", display.HeadingOptions{Subtitle: "标签文字、图标;点击与关闭分离", Divider: true})
|
||||
displaySection(c, "Parameters", func() {
|
||||
displayControls(c, func() {
|
||||
displayLog(log, ui.Checkbox(c, &s.icon, "Icon"), "icon", &s.icon)
|
||||
displayLog(log, ui.Checkbox(c, &s.clickable, "Clickable"), "clickable", &s.clickable)
|
||||
displayLog(log, ui.Checkbox(c, &s.closable, "Closable"), "closable", &s.closable)
|
||||
displayLog(log, ui.Checkbox(c, &s.dis, "Disabled"), "dis", &s.dis)
|
||||
if mujica.Button(c, "Reset tags", mujica.ButtonOptions{Variant: mujica.Ghost}).Clicked() {
|
||||
if input.Button(c, "Reset tags", input.ButtonOptions{Variant: input.Ghost}).Clicked() {
|
||||
s.tags = []string{"Baroque", "Gothic", "Rococo", "Court"}
|
||||
log("reset")
|
||||
}
|
||||
@@ -283,9 +285,9 @@ func tagDemo(c *ui.Context, log func(string)) {
|
||||
ui.Row(c).Gap(8).Wrap().Children(func() {
|
||||
for i := 0; i < len(s.tags); i++ {
|
||||
t := s.tags[i]
|
||||
var v mujica.TagView
|
||||
var v display.TagView
|
||||
ui.Box(c).Key(t).Children(func() {
|
||||
v = mujica.Tag(c, t, mujica.TagOptions{Icon: icon, Clickable: s.clickable, Closable: s.closable, Disabled: s.dis})
|
||||
v = display.Tag(c, t, display.TagOptions{Icon: icon, Clickable: s.clickable, Closable: s.closable, Disabled: s.dis})
|
||||
})
|
||||
if v.Clicked {
|
||||
log("clicked " + t)
|
||||
@@ -301,9 +303,9 @@ func tagDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
const tagCode = `for i, t := range tags {
|
||||
var v mujica.TagView
|
||||
var v display.TagView
|
||||
ui.Box(c).Key(t).Children(func() {
|
||||
v = mujica.Tag(c, t, mujica.TagOptions{Icon: "star", Clickable: true, Closable: true})
|
||||
v = display.Tag(c, t, display.TagOptions{Icon: "star", Clickable: true, Closable: true})
|
||||
})
|
||||
if v.Closed {
|
||||
tags = append(tags[:i:i], tags[i+1:]...)
|
||||
@@ -317,32 +319,32 @@ type linkState struct {
|
||||
|
||||
func linkDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "019", func() linkState { return linkState{url: "https://example.com/charter"} })
|
||||
mujica.Heading(c, "Link", mujica.HeadingOptions{Subtitle: "外部地址或应用内动作;键盘激活;打开失败反馈", Divider: true})
|
||||
display.Heading(c, "Link", display.HeadingOptions{Subtitle: "外部地址或应用内动作;键盘激活;打开失败反馈", Divider: true})
|
||||
displaySection(c, "Parameters", func() {
|
||||
displayControls(c, func() {
|
||||
displayLog(log, ui.TextInput(c, &s.url).Placeholder("URL (empty = in-app action)").Width(320), "url", &s.url)
|
||||
if mujica.Button(c, "Simulate open failure", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Simulate open failure", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.external = "No application can open this link."
|
||||
log("open failure set")
|
||||
}
|
||||
if mujica.Button(c, "Clear error", mujica.ButtonOptions{Variant: mujica.Ghost}).Clicked() {
|
||||
if input.Button(c, "Clear error", input.ButtonOptions{Variant: input.Ghost}).Clicked() {
|
||||
s.external = ""
|
||||
log("open failure cleared")
|
||||
}
|
||||
})
|
||||
})
|
||||
displaySection(c, "Preview", func() {
|
||||
if mujica.Link(c, "Read the court charter", mujica.LinkOptions{URL: s.url, Error: s.external}).Clicked() {
|
||||
if display.Link(c, "Read the court charter", display.LinkOptions{URL: s.url, Error: s.external}).Clicked() {
|
||||
log("activated " + s.url)
|
||||
}
|
||||
if mujica.Link(c, "Show the guest list", mujica.LinkOptions{}).Clicked() {
|
||||
if display.Link(c, "Show the guest list", display.LinkOptions{}).Clicked() {
|
||||
log("in-app action")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const linkCode = `mujica.Link(c, "Read the charter", mujica.LinkOptions{URL: "https://example.com/charter"})
|
||||
if mujica.Link(c, "Show details", mujica.LinkOptions{}).Clicked() {
|
||||
const linkCode = `display.Link(c, "Read the charter", display.LinkOptions{URL: "https://example.com/charter"})
|
||||
if display.Link(c, "Show details", display.LinkOptions{}).Clicked() {
|
||||
details = !details
|
||||
}`
|
||||
|
||||
@@ -350,7 +352,7 @@ type kbdState struct{ combo string }
|
||||
|
||||
func kbdDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "020", func() kbdState { return kbdState{combo: "Mod+Shift+P"} })
|
||||
mujica.Heading(c, "Kbd", mujica.HeadingOptions{Subtitle: "单键与组合键;按平台显示 Command 或 Ctrl", Divider: true})
|
||||
display.Heading(c, "Kbd", display.HeadingOptions{Subtitle: "单键与组合键;按平台显示 Command 或 Ctrl", Divider: true})
|
||||
displaySection(c, "Parameters", func() {
|
||||
displayControls(c, func() {
|
||||
if ui.TextInput(c, &s.combo).Placeholder("Keys joined by +").Width(220).Changed() {
|
||||
@@ -366,20 +368,20 @@ func kbdDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
}
|
||||
if len(keys) > 0 {
|
||||
mujica.Kbd(c, keys...)
|
||||
display.Kbd(c, keys...)
|
||||
}
|
||||
})
|
||||
displaySection(c, "Common", func() {
|
||||
for _, row := range [][]string{{"Mod", "K"}, {"Mod", "Shift", "P"}, {"Alt", "Up"}, {"Esc"}, {"Ctrl", "Tab"}} {
|
||||
ui.Row(c).Gap(12).AlignItems(ui.Center).Children(func() {
|
||||
mujica.Kbd(c, row...)
|
||||
ui.Text(c, strings.Join(row, " + ")).TextColor(mujica.Tokens(c).TextMuted)
|
||||
display.Kbd(c, row...)
|
||||
ui.Text(c, strings.Join(row, " + ")).TextColor(core.Tokens(c).TextMuted)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const kbdCode = `ui.Row(c).Gap(6).Children(func() {
|
||||
mujica.Text(c, "Command palette", mujica.TextOptions{})
|
||||
mujica.Kbd(c, "Mod", "Shift", "P")
|
||||
display.Text(c, "Command palette", display.TextOptions{})
|
||||
display.Kbd(c, "Mod", "Shift", "P")
|
||||
})`
|
||||
@@ -6,7 +6,8 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/display"
|
||||
)
|
||||
|
||||
type imageState struct {
|
||||
@@ -22,7 +23,7 @@ var displayPicture = func() *ui.SVG {
|
||||
|
||||
func imageDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "014", func() imageState { return imageState{crop: true} })
|
||||
mujica.Heading(c, "Image", mujica.HeadingOptions{Subtitle: "位图与 SVG;包含或裁切;占位、加载、错误;替代说明", Divider: true})
|
||||
display.Heading(c, "Image", display.HeadingOptions{Subtitle: "位图与 SVG;包含或裁切;占位、加载、错误;替代说明", Divider: true})
|
||||
displaySection(c, "Parameters", func() {
|
||||
displayControls(c, func() {
|
||||
displayLog(log, ui.Checkbox(c, &s.crop, "Crop"), "crop", &s.crop)
|
||||
@@ -42,7 +43,7 @@ func imageDemo(c *ui.Context, log func(string)) {
|
||||
if s.empty {
|
||||
src = nil
|
||||
}
|
||||
v := mujica.Image(c, src, mujica.ImageOptions{Width: 200, Height: 200, Crop: s.crop, Alt: "Gothic emblem", Loading: s.loading, Err: s.err})
|
||||
v := display.Image(c, src, display.ImageOptions{Width: 200, Height: 200, Crop: s.crop, Alt: "Gothic emblem", Loading: s.loading, Err: s.err})
|
||||
if v.Retry {
|
||||
s.fail, s.err = false, nil
|
||||
log("retry")
|
||||
@@ -50,18 +51,18 @@ func imageDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
displaySection(c, "States", func() {
|
||||
ui.Row(c).Gap(12).Wrap().Children(func() {
|
||||
mujica.Image(c, displayPicture, mujica.ImageOptions{Width: 120, Height: 120})
|
||||
mujica.Image(c, displayPicture, mujica.ImageOptions{Width: 120, Height: 120, Crop: true})
|
||||
mujica.Image(c, nil, mujica.ImageOptions{Width: 120, Height: 120})
|
||||
mujica.Image(c, nil, mujica.ImageOptions{Width: 120, Height: 120, Loading: true})
|
||||
if mujica.Image(c, nil, mujica.ImageOptions{Width: 160, Height: 140, Err: errors.New("404")}).Retry {
|
||||
display.Image(c, displayPicture, display.ImageOptions{Width: 120, Height: 120})
|
||||
display.Image(c, displayPicture, display.ImageOptions{Width: 120, Height: 120, Crop: true})
|
||||
display.Image(c, nil, display.ImageOptions{Width: 120, Height: 120})
|
||||
display.Image(c, nil, display.ImageOptions{Width: 120, Height: 120, Loading: true})
|
||||
if display.Image(c, nil, display.ImageOptions{Width: 160, Height: 140, Err: errors.New("404")}).Retry {
|
||||
log("retry (static example)")
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const imageCode = `img := mujica.Image(c, photo, mujica.ImageOptions{
|
||||
const imageCode = `img := display.Image(c, photo, display.ImageOptions{
|
||||
Width: 160, Height: 120, Crop: true, Alt: "Hall of mirrors",
|
||||
Loading: loading, Err: loadErr,
|
||||
})
|
||||
@@ -77,14 +78,14 @@ type avatarState struct {
|
||||
|
||||
var displayBitmap = func() *ui.Bitmap {
|
||||
return ui.NewBitmap(ui.Render(func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{Mode: mujica.Dark})
|
||||
core.Use(c, core.Settings{Mode: core.Dark})
|
||||
ui.Box(c).Fill().Background(ui.Hex("#7D2034"))
|
||||
}, 64, 64, 1))
|
||||
}()
|
||||
|
||||
func avatarDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "015", func() avatarState { return avatarState{name: "Sakiko Togawa", size: 48} })
|
||||
mujica.Heading(c, "Avatar", mujica.HeadingOptions{Subtitle: "图片、姓名首字母与默认图标回退", Divider: true})
|
||||
display.Heading(c, "Avatar", display.HeadingOptions{Subtitle: "图片、姓名首字母与默认图标回退", Divider: true})
|
||||
displaySection(c, "Parameters", func() {
|
||||
displayControls(c, func() {
|
||||
if ui.TextInput(c, &s.name).Width(200).Changed() {
|
||||
@@ -100,19 +101,19 @@ func avatarDemo(c *ui.Context, log func(string)) {
|
||||
if s.img {
|
||||
img = displayBitmap
|
||||
}
|
||||
mujica.Avatar(c, s.name, mujica.AvatarOptions{Size: float32(s.size), Image: img})
|
||||
display.Avatar(c, s.name, display.AvatarOptions{Size: float32(s.size), Image: img})
|
||||
})
|
||||
displaySection(c, "Fallbacks", func() {
|
||||
ui.Row(c).Gap(12).AlignItems(ui.Center).Children(func() {
|
||||
mujica.Avatar(c, "Photo", mujica.AvatarOptions{Image: displayBitmap, Size: 40})
|
||||
mujica.Avatar(c, "Uika Misumi", mujica.AvatarOptions{Size: 40})
|
||||
mujica.Avatar(c, "若叶睦", mujica.AvatarOptions{Size: 40})
|
||||
mujica.Avatar(c, "", mujica.AvatarOptions{Size: 40})
|
||||
display.Avatar(c, "Photo", display.AvatarOptions{Image: displayBitmap, Size: 40})
|
||||
display.Avatar(c, "Uika Misumi", display.AvatarOptions{Size: 40})
|
||||
display.Avatar(c, "若叶睦", display.AvatarOptions{Size: 40})
|
||||
display.Avatar(c, "", display.AvatarOptions{Size: 40})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const avatarCode = `ui.Row(c).Gap(8).Children(func() {
|
||||
mujica.Avatar(c, "Sakiko Togawa", mujica.AvatarOptions{Size: 40})
|
||||
mujica.Avatar(c, "", mujica.AvatarOptions{})
|
||||
display.Avatar(c, "Sakiko Togawa", display.AvatarOptions{Size: 40})
|
||||
display.Avatar(c, "", display.AvatarOptions{})
|
||||
})`
|
||||
@@ -5,13 +5,13 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/display"
|
||||
)
|
||||
|
||||
// displaySection shows a titled block of a demo.
|
||||
func displaySection(c *ui.Context, title string, fn func()) {
|
||||
ui.Column(c).Gap(10).Margin(0, 0, 20, 0).Children(func() {
|
||||
mujica.Heading(c, title, mujica.HeadingOptions{Level: 3})
|
||||
display.Heading(c, title, display.HeadingOptions{Level: 3})
|
||||
fn()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ var essentialsPages = []page{
|
||||
ID: "481", Name: "SplitButton", ZhName: "拆分按钮 主操作 下拉", Category: "Essentials",
|
||||
Demo: splitButtonDemo,
|
||||
Code: `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.SplitButton(c, "Save", mujica.SplitButtonOptions{}, func(m *mujica.PopupMenu) {
|
||||
if m.Item("Save as…", mujica.PopupMenuItemOptions{}) {
|
||||
core.Use(c, core.Settings{})
|
||||
r := overlay.SplitButton(c, "Save", overlay.SplitButtonOptions{}, func(m *overlay.PopupMenu) {
|
||||
if m.Item("Save as…", overlay.PopupMenuItemOptions{}) {
|
||||
// ask for a file name
|
||||
}
|
||||
})
|
||||
@@ -21,8 +21,8 @@ var essentialsPages = []page{
|
||||
ID: "482", Name: "CopyButton", ZhName: "复制按钮 剪贴板", Category: "Essentials",
|
||||
Demo: copyButtonDemo,
|
||||
Code: `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.CopyButton(c, "https://example.com/invite/42", mujica.CopyButtonOptions{Label: "Copy link"})
|
||||
core.Use(c, core.Settings{})
|
||||
input.CopyButton(c, "https://example.com/invite/42", input.CopyButtonOptions{Label: "Copy link"})
|
||||
}`,
|
||||
Keys: "Tab focuses; Enter or Space copies. The copied state is announced.",
|
||||
},
|
||||
@@ -30,8 +30,8 @@ var essentialsPages = []page{
|
||||
ID: "483", Name: "HoldToConfirm", ZhName: "按住确认 危险操作", Category: "Essentials",
|
||||
Demo: holdToConfirmDemo,
|
||||
Code: `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.HoldToConfirm(c, "Delete project", mujica.HoldToConfirmOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if input.HoldToConfirm(c, "Delete project", input.HoldToConfirmOptions{}).Changed() {
|
||||
// delete the project
|
||||
}
|
||||
}`,
|
||||
@@ -42,9 +42,9 @@ var essentialsPages = []page{
|
||||
Demo: bulkActionBarDemo,
|
||||
Code: `selected := 3
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.BulkActionBar(c, selected, mujica.BulkActionBarOptions{}, func() {
|
||||
mujica.Button(c, "Archive", mujica.ButtonOptions{Variant: mujica.Secondary})
|
||||
core.Use(c, core.Settings{})
|
||||
r := input.BulkActionBar(c, selected, input.BulkActionBarOptions{}, func() {
|
||||
input.Button(c, "Archive", input.ButtonOptions{Variant: input.Secondary})
|
||||
})
|
||||
if r.Changed() {
|
||||
selected = 0
|
||||
@@ -57,8 +57,8 @@ view := func(c *ui.Context) {
|
||||
Demo: scrubInputDemo,
|
||||
Code: `opacity := 0.6
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ScrubInput(c, "Opacity", &opacity, mujica.ScrubInputOptions{Max: 1, Step: 0.1})
|
||||
core.Use(c, core.Settings{})
|
||||
input.ScrubInput(c, "Opacity", &opacity, input.ScrubInputOptions{Max: 1, Step: 0.1})
|
||||
}`,
|
||||
Keys: "The label is pointer-only; in the field, Up and Down step and typing sets the value.",
|
||||
},
|
||||
@@ -67,8 +67,8 @@ view := func(c *ui.Context) {
|
||||
Demo: currencyInputDemo,
|
||||
Code: `cents := int64(123450)
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.CurrencyInput(c, ¢s, mujica.CurrencyInputOptions{Symbol: "$", Label: "Price"})
|
||||
core.Use(c, core.Settings{})
|
||||
input.CurrencyInput(c, ¢s, input.CurrencyInputOptions{Symbol: "$", Label: "Price"})
|
||||
}`,
|
||||
Keys: "Tab focuses the field, which shows plain digits while focused and groups thousands when left.",
|
||||
},
|
||||
@@ -77,22 +77,22 @@ view := func(c *ui.Context) {
|
||||
Demo: maskedInputDemo,
|
||||
Code: `phone := ""
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.MaskedInput(c, &phone, mujica.MaskedInputOptions{Mask: "(999) 999-9999", Label: "Phone"})
|
||||
core.Use(c, core.Settings{})
|
||||
input.MaskedInput(c, &phone, input.MaskedInputOptions{Mask: "(999) 999-9999", Label: "Phone"})
|
||||
}`,
|
||||
Keys: "Arrows, Home and End move the caret; Backspace and Delete remove; paste keeps the characters that fit; Enter submits.",
|
||||
},
|
||||
{
|
||||
ID: "488", Name: "MentionInput", ZhName: "提及输入 @ 候选", Category: "Essentials",
|
||||
Demo: mentionInputDemo,
|
||||
Code: `text, mentions := "", []mujica.Mention(nil)
|
||||
people := []mujica.MentionCandidate{{ID: "u1", Name: "Alice"}, {ID: "u2", Name: "Bob"}}
|
||||
Code: `text, mentions := "", []input.Mention(nil)
|
||||
people := []input.MentionCandidate{{ID: "u1", Name: "Alice"}, {ID: "u2", Name: "Bob"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.MentionInput(c, &text, &mentions, mujica.MentionInputOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
input.MentionInput(c, &text, &mentions, input.MentionInputOptions{
|
||||
Label: "Comment",
|
||||
Candidates: func(q string) []mujica.MentionCandidate {
|
||||
var out []mujica.MentionCandidate
|
||||
Candidates: func(q string) []input.MentionCandidate {
|
||||
var out []input.MentionCandidate
|
||||
for _, p := range people {
|
||||
if strings.HasPrefix(strings.ToLower(p.Name), strings.ToLower(q)) {
|
||||
out = append(out, p)
|
||||
@@ -109,10 +109,10 @@ view := func(c *ui.Context) {
|
||||
Demo: choiceChipsDemo,
|
||||
Code: `var status []string
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ChoiceChips(c, &status, []mujica.ToggleItem[string]{
|
||||
core.Use(c, core.Settings{})
|
||||
input.ChoiceChips(c, &status, []input.ToggleItem[string]{
|
||||
{Value: "open", Label: "Open"}, {Value: "closed", Label: "Closed"},
|
||||
}, mujica.ChoiceChipsOptions{Multiple: true, Label: "Status"})
|
||||
}, input.ChoiceChipsOptions{Multiple: true, Label: "Status"})
|
||||
}`,
|
||||
Keys: "The chips are one Tab stop; Left and Right move, skipping disabled chips; Space or Enter toggles.",
|
||||
},
|
||||
@@ -121,8 +121,8 @@ view := func(c *ui.Context) {
|
||||
Demo: knobDemo,
|
||||
Code: `gain := 35.0
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Knob(c, &gain, mujica.KnobOptions{Label: "Gain", Ticks: 11})
|
||||
core.Use(c, core.Settings{})
|
||||
input.Knob(c, &gain, input.KnobOptions{Label: "Gain", Ticks: 11})
|
||||
}`,
|
||||
Keys: "Arrows step the value; Home and End go to the ends; drag up or down with the pointer.",
|
||||
},
|
||||
@@ -131,8 +131,8 @@ view := func(c *ui.Context) {
|
||||
Demo: verticalSliderDemo,
|
||||
Code: `level := 40.0
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.VerticalSlider(c, &level, mujica.VerticalSliderOptions{Step: 10, Label: "Level"})
|
||||
core.Use(c, core.Settings{})
|
||||
input.VerticalSlider(c, &level, input.VerticalSliderOptions{Step: 10, Label: "Level"})
|
||||
}`,
|
||||
Keys: "Up and Right raise the value, Down and Left lower it, Home and End go to the ends.",
|
||||
},
|
||||
@@ -141,23 +141,23 @@ view := func(c *ui.Context) {
|
||||
Demo: colorPaletteDemo,
|
||||
Code: `color := ui.Hex("#7D2034")
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ColorPalette(c, &color, []mujica.ColorSwatch{
|
||||
core.Use(c, core.Settings{})
|
||||
input.ColorPalette(c, &color, []input.ColorSwatch{
|
||||
{Color: ui.Hex("#7D2034"), Name: "Wine"},
|
||||
{Color: ui.Hex("#AD9164"), Name: "Old gold"},
|
||||
{Color: ui.Hex("#355E80"), Name: "Slate blue"},
|
||||
}, mujica.ColorPaletteOptions{Custom: true, Label: "Tag color"})
|
||||
}, input.ColorPaletteOptions{Custom: true, Label: "Tag color"})
|
||||
}`,
|
||||
Keys: "The palette is one Tab stop; Left and Right move by one swatch, Up and Down by a row; Enter or Space chooses.",
|
||||
},
|
||||
{
|
||||
ID: "493", Name: "SignaturePad", ZhName: "签名板 手写", Category: "Essentials",
|
||||
Demo: signaturePadDemo,
|
||||
Code: `var strokes [][]mujica.SignaturePoint
|
||||
Code: `var strokes [][]input.SignaturePoint
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.SignaturePad(c, &strokes, mujica.SignaturePadOptions{}).Changed() {
|
||||
_ = mujica.SignaturePath(strokes) // SVG path data to store
|
||||
core.Use(c, core.Settings{})
|
||||
if input.SignaturePad(c, &strokes, input.SignaturePadOptions{}).Changed() {
|
||||
_ = input.SignaturePath(strokes) // SVG path data to store
|
||||
}
|
||||
}`,
|
||||
Keys: "Tab focuses the pad; Delete or Backspace clears it. Drawing needs the pointer.",
|
||||
@@ -167,9 +167,9 @@ view := func(c *ui.Context) {
|
||||
Demo: bannerDemo,
|
||||
Code: `open := true
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.Banner(c, &open, "Maintenance tonight at 22:00.", mujica.BannerOptions{
|
||||
Severity: mujica.SeverityWarning,
|
||||
core.Use(c, core.Settings{})
|
||||
r := input.Banner(c, &open, "Maintenance tonight at 22:00.", input.BannerOptions{
|
||||
Severity: core.SeverityWarning,
|
||||
Action: "Details",
|
||||
})
|
||||
if r.Action {
|
||||
@@ -183,8 +183,8 @@ view := func(c *ui.Context) {
|
||||
Demo: undoToastDemo,
|
||||
Code: `open := true
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.UndoToast(c, &open, "Note deleted", mujica.UndoToastOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
feedback.UndoToast(c, &open, "Note deleted", feedback.UndoToastOptions{
|
||||
OnUndo: func() {
|
||||
// restore the note
|
||||
},
|
||||
@@ -197,8 +197,8 @@ view := func(c *ui.Context) {
|
||||
Demo: masonryDemo,
|
||||
Code: `titles := []string{"Sea", "Forest", "Desert", "Glacier"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Masonry(c, len(titles), mujica.MasonryOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
input.Masonry(c, len(titles), input.MasonryOptions{
|
||||
Key: func(i int) string { return titles[i] },
|
||||
}, func(i int) {
|
||||
ui.Text(c, titles[i])
|
||||
@@ -211,11 +211,11 @@ view := func(c *ui.Context) {
|
||||
Demo: resizablePanelGroupDemo,
|
||||
Code: `var sizes []float32
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.ResizablePanelGroup(c, &sizes, []mujica.ResizablePanel{
|
||||
core.Use(c, core.Settings{})
|
||||
r := input.ResizablePanelGroup(c, &sizes, []input.ResizablePanel{
|
||||
{Label: "Files", Collapsible: true, Build: func() { ui.Text(c, "Files") }},
|
||||
{Label: "Editor", Build: func() { ui.Text(c, "Editor") }},
|
||||
}, mujica.ResizablePanelGroupOptions{})
|
||||
}, input.ResizablePanelGroupOptions{})
|
||||
if r.Committed {
|
||||
// save sizes
|
||||
}
|
||||
@@ -225,13 +225,13 @@ view := func(c *ui.Context) {
|
||||
{
|
||||
ID: "498", Name: "DockPanel", ZhName: "停靠面板 标签", Category: "Essentials",
|
||||
Demo: dockPanelDemo,
|
||||
Code: `var layout mujica.DockLayout
|
||||
Code: `var layout input.DockLayout
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.DockPanel(c, &layout, []mujica.DockItem{
|
||||
core.Use(c, core.Settings{})
|
||||
r := input.DockPanel(c, &layout, []input.DockItem{
|
||||
{ID: "files", Title: "Files", Build: func() { ui.Text(c, "Files") }},
|
||||
{ID: "terminal", Title: "Terminal", Build: func() { ui.Text(c, "$") }},
|
||||
}, mujica.DockPanelOptions{})
|
||||
}, input.DockPanelOptions{})
|
||||
if r.Changed() {
|
||||
// save layout
|
||||
}
|
||||
@@ -242,20 +242,20 @@ view := func(c *ui.Context) {
|
||||
ID: "499", Name: "QRCode", ZhName: "二维码", Category: "Essentials",
|
||||
Demo: qrCodeDemo,
|
||||
Code: `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.QRCode(c, "https://example.com/mujica", mujica.QRCodeOptions{Level: mujica.QRQuartile})
|
||||
core.Use(c, core.Settings{})
|
||||
input.QRCode(c, "https://example.com/mujica", input.QRCodeOptions{Level: input.QRQuartile})
|
||||
}`,
|
||||
Keys: "Not focusable; assistive technology reads its name.",
|
||||
},
|
||||
{
|
||||
ID: "500", Name: "ActivityFeed", ZhName: "活动流 动态", Category: "Essentials",
|
||||
Demo: activityFeedDemo,
|
||||
Code: `events := []mujica.Activity{
|
||||
Code: `events := []feedback.Activity{
|
||||
{ID: "1", Actor: "Lin", Action: "commented on", Object: "Quarterly report", Time: time.Now()},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ActivityFeed(c, events, mujica.ActivityFeedOptions{}).Size(380, 280)
|
||||
core.Use(c, core.Settings{})
|
||||
feedback.ActivityFeed(c, events, feedback.ActivityFeedOptions{}).Size(380, 280)
|
||||
}`,
|
||||
Keys: "Scroll with the wheel or the scrollbar; only rows in view are built.",
|
||||
},
|
||||
|
||||
@@ -6,7 +6,8 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/overlay"
|
||||
)
|
||||
|
||||
func splitButtonDemo(c *ui.Context, log func(string)) {
|
||||
@@ -20,14 +21,14 @@ func splitButtonDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
})
|
||||
feedbackSection(c, "Live", func() {
|
||||
v := mujica.Primary
|
||||
v := input.Primary
|
||||
if s.variant {
|
||||
v = mujica.Secondary
|
||||
v = input.Secondary
|
||||
}
|
||||
ui.Row(c).Children(func() {
|
||||
r := mujica.SplitButton(c, "Save", mujica.SplitButtonOptions{Variant: v, Disabled: s.disabled}, func(m *mujica.PopupMenu) {
|
||||
r := overlay.SplitButton(c, "Save", overlay.SplitButtonOptions{Variant: v, Disabled: s.disabled}, func(m *overlay.PopupMenu) {
|
||||
for _, it := range []string{"Save as…", "Save a copy", "Save all"} {
|
||||
if m.Item(it, mujica.PopupMenuItemOptions{}) {
|
||||
if m.Item(it, overlay.PopupMenuItemOptions{}) {
|
||||
log("split button: menu " + it)
|
||||
}
|
||||
}
|
||||
@@ -53,10 +54,10 @@ func copyButtonDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
feedbackSection(c, "Live", func() {
|
||||
ui.Row(c).Gap(12).Children(func() {
|
||||
if mujica.CopyButton(c, s.text, mujica.CopyButtonOptions{Disabled: s.disabled}).Changed() {
|
||||
if input.CopyButton(c, s.text, input.CopyButtonOptions{Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("copy button: copied %d characters", len([]rune(s.text))))
|
||||
}
|
||||
if mujica.CopyButton(c, s.text, mujica.CopyButtonOptions{Label: "Copy link", Disabled: s.disabled}).Changed() {
|
||||
if input.CopyButton(c, s.text, input.CopyButtonOptions{Label: "Copy link", Disabled: s.disabled}).Changed() {
|
||||
log("copy button: copied with label")
|
||||
}
|
||||
})
|
||||
@@ -82,7 +83,7 @@ func holdToConfirmDemo(c *ui.Context, log func(string)) {
|
||||
feedbackSection(c, "Live (turn on reduced motion to see the countdown text)", func() {
|
||||
ui.Row(c).Children(func() {
|
||||
d := time.Duration(s.seconds * float64(time.Second))
|
||||
if mujica.HoldToConfirm(c, "Delete project", mujica.HoldToConfirmOptions{Duration: d, Disabled: s.disabled}).Changed() {
|
||||
if input.HoldToConfirm(c, "Delete project", input.HoldToConfirmOptions{Duration: d, Disabled: s.disabled}).Changed() {
|
||||
s.deleted++
|
||||
log("hold to confirm: confirmed")
|
||||
}
|
||||
@@ -113,11 +114,11 @@ func bulkActionBarDemo(c *ui.Context, log func(string)) {
|
||||
n++
|
||||
}
|
||||
}
|
||||
r := mujica.BulkActionBar(c, n, mujica.BulkActionBarOptions{Disabled: s.disabled}, func() {
|
||||
if mujica.Button(c, "Archive", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
r := input.BulkActionBar(c, n, input.BulkActionBarOptions{Disabled: s.disabled}, func() {
|
||||
if input.Button(c, "Archive", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
log(fmt.Sprintf("bulk bar: archive %d", n))
|
||||
}
|
||||
if mujica.Button(c, "Delete", mujica.ButtonOptions{Variant: mujica.Danger}).Clicked() {
|
||||
if input.Button(c, "Delete", input.ButtonOptions{Variant: input.Danger}).Clicked() {
|
||||
log(fmt.Sprintf("bulk bar: delete %d", n))
|
||||
}
|
||||
})
|
||||
@@ -137,10 +138,10 @@ func scrubInputDemo(c *ui.Context, log func(string)) {
|
||||
ui.Column(c).Gap(16).FillWidth().Children(func() {
|
||||
ui.Checkbox(c, &s.disabled, "Disabled")
|
||||
feedbackSection(c, "Drag a label sideways, or type", func() {
|
||||
if mujica.ScrubInput(c, "Opacity", &s.opacity, mujica.ScrubInputOptions{Max: 1, Step: 0.05, Disabled: s.disabled}).Changed() {
|
||||
if input.ScrubInput(c, "Opacity", &s.opacity, input.ScrubInputOptions{Max: 1, Step: 0.05, Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("scrub: opacity %.2f", s.opacity))
|
||||
}
|
||||
if mujica.ScrubInput(c, "Size", &s.size, mujica.ScrubInputOptions{Min: 8, Max: 72, Disabled: s.disabled}).Changed() {
|
||||
if input.ScrubInput(c, "Size", &s.size, input.ScrubInputOptions{Min: 8, Max: 72, Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("scrub: size %.0f", s.size))
|
||||
}
|
||||
})
|
||||
@@ -157,14 +158,14 @@ func currencyInputDemo(c *ui.Context, log func(string)) {
|
||||
ui.Checkbox(c, &s.disabled, "Disabled")
|
||||
feedbackSection(c, "Live", func() {
|
||||
ui.Row(c).Gap(12).Children(func() {
|
||||
if mujica.CurrencyInput(c, &s.cents, mujica.CurrencyInputOptions{Symbol: "$", Label: "Price", Disabled: s.disabled}).Changed() {
|
||||
if input.CurrencyInput(c, &s.cents, input.CurrencyInputOptions{Symbol: "$", Label: "Price", Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("currency: %d cents", s.cents))
|
||||
}
|
||||
if mujica.CurrencyInput(c, &s.yen, mujica.CurrencyInputOptions{Symbol: "¥", NoFraction: true, Label: "Yen", Disabled: s.disabled}).Changed() {
|
||||
if input.CurrencyInput(c, &s.yen, input.CurrencyInputOptions{Symbol: "¥", NoFraction: true, Label: "Yen", Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("currency: %d yen", s.yen))
|
||||
}
|
||||
})
|
||||
ui.Text(c, "Stored as "+mujica.CurrencyFormat(s.cents, 2, true)+" and "+mujica.CurrencyFormat(s.yen, 0, true))
|
||||
ui.Text(c, "Stored as "+input.CurrencyFormat(s.cents, 2, true)+" and "+input.CurrencyFormat(s.yen, 0, true))
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -186,7 +187,7 @@ func maskedInputDemo(c *ui.Context, log func(string)) {
|
||||
ui.Checkbox(c, &s.disabled, "Disabled")
|
||||
})
|
||||
feedbackSection(c, "Live (try an input method: nothing commits until it does)", func() {
|
||||
r := mujica.MaskedInput(c, &s.value, mujica.MaskedInputOptions{Mask: masks[s.mask], Label: "Masked field", Disabled: s.disabled})
|
||||
r := input.MaskedInput(c, &s.value, input.MaskedInputOptions{Mask: masks[s.mask], Label: "Masked field", Disabled: s.disabled})
|
||||
r.Element.Width(260)
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("masked: %d characters filled", len([]rune(s.value))))
|
||||
@@ -204,7 +205,7 @@ func choiceChipsDemo(c *ui.Context, log func(string)) {
|
||||
multiple, disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "489", func() state { return state{multiple: true, value: []string{"open"}} })
|
||||
items := []mujica.ToggleItem[string]{{Value: "open", Label: "Open"}, {Value: "draft", Label: "Draft"}, {Value: "review", Label: "In review"}, {Value: "closed", Label: "Closed", Disabled: true}, {Value: "merged", Label: "Merged"}}
|
||||
items := []input.ToggleItem[string]{{Value: "open", Label: "Open"}, {Value: "draft", Label: "Draft"}, {Value: "review", Label: "In review"}, {Value: "closed", Label: "Closed", Disabled: true}, {Value: "merged", Label: "Merged"}}
|
||||
ui.Column(c).Gap(16).FillWidth().Children(func() {
|
||||
feedbackSection(c, "Parameters", func() {
|
||||
ui.Row(c).Gap(16).Children(func() {
|
||||
@@ -215,7 +216,7 @@ func choiceChipsDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
})
|
||||
feedbackSection(c, "Live", func() {
|
||||
if mujica.ChoiceChips(c, &s.value, items, mujica.ChoiceChipsOptions{Multiple: s.multiple, Disabled: s.disabled, Label: "Status"}).Changed() {
|
||||
if input.ChoiceChips(c, &s.value, items, input.ChoiceChipsOptions{Multiple: s.multiple, Disabled: s.disabled, Label: "Status"}).Changed() {
|
||||
log(fmt.Sprintf("chips: %v", s.value))
|
||||
}
|
||||
})
|
||||
@@ -231,10 +232,10 @@ func knobDemo(c *ui.Context, log func(string)) {
|
||||
ui.Column(c).Gap(16).FillWidth().Children(func() {
|
||||
ui.Checkbox(c, &s.disabled, "Disabled")
|
||||
ui.Row(c).Gap(32).Children(func() {
|
||||
if mujica.Knob(c, &s.gain, mujica.KnobOptions{Label: "Gain", Ticks: 11, Disabled: s.disabled}).Changed() {
|
||||
if input.Knob(c, &s.gain, input.KnobOptions{Label: "Gain", Ticks: 11, Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("knob: gain %.1f", s.gain))
|
||||
}
|
||||
if mujica.Knob(c, &s.mix, mujica.KnobOptions{Max: 1, Step: 0.1, Size: 72, Label: "Mix", Disabled: s.disabled}).Changed() {
|
||||
if input.Knob(c, &s.mix, input.KnobOptions{Max: 1, Step: 0.1, Size: 72, Label: "Mix", Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("knob: mix %.1f", s.mix))
|
||||
}
|
||||
})
|
||||
@@ -254,11 +255,11 @@ func verticalSliderDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
ui.Row(c).Gap(16).Children(func() {
|
||||
for i := range s.levels {
|
||||
opts := mujica.VerticalSliderOptions{Step: 10, ShowValue: s.showValue, Disabled: s.disable, Label: fmt.Sprintf("Band %d", i+1)}
|
||||
opts := input.VerticalSliderOptions{Step: 10, ShowValue: s.showValue, Disabled: s.disable, Label: fmt.Sprintf("Band %d", i+1)}
|
||||
if i == 0 {
|
||||
opts.Marks = []mujica.SliderMark{{Value: 0, Label: "0"}, {Value: 50, Label: "50"}, {Value: 100, Label: "100"}}
|
||||
opts.Marks = []input.SliderMark{{Value: 0, Label: "0"}, {Value: 50, Label: "50"}, {Value: 100, Label: "100"}}
|
||||
}
|
||||
if mujica.VerticalSlider(c, &s.levels[i], opts).Changed() {
|
||||
if input.VerticalSlider(c, &s.levels[i], opts).Changed() {
|
||||
log(fmt.Sprintf("vertical slider: band %d = %.0f", i+1, s.levels[i]))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,24 +8,26 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/feedback"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func mentionInputDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
text string
|
||||
mentions []mujica.Mention
|
||||
mentions []input.Mention
|
||||
disabled bool
|
||||
}
|
||||
people := []mujica.MentionCandidate{{ID: "u1", Name: "Alice"}, {ID: "u2", Name: "Albert"}, {ID: "u3", Name: "Bob"}, {ID: "u4", Name: "Sakiko"}, {ID: "u5", Name: "Mutsumi"}}
|
||||
people := []input.MentionCandidate{{ID: "u1", Name: "Alice"}, {ID: "u2", Name: "Albert"}, {ID: "u3", Name: "Bob"}, {ID: "u4", Name: "Sakiko"}, {ID: "u5", Name: "Mutsumi"}}
|
||||
s := ui.Local(c.Root(), "488", func() state { return state{} })
|
||||
ui.Column(c).Gap(16).FillWidth().Children(func() {
|
||||
ui.Checkbox(c, &s.disabled, "Disabled")
|
||||
feedbackSection(c, "Type @ to mention someone", func() {
|
||||
r := mujica.MentionInput(c, &s.text, &s.mentions, mujica.MentionInputOptions{
|
||||
r := input.MentionInput(c, &s.text, &s.mentions, input.MentionInputOptions{
|
||||
Placeholder: "Write a comment…", Label: "Comment", Disabled: s.disabled,
|
||||
Candidates: func(q string) []mujica.MentionCandidate {
|
||||
var out []mujica.MentionCandidate
|
||||
Candidates: func(q string) []input.MentionCandidate {
|
||||
var out []input.MentionCandidate
|
||||
for _, p := range people {
|
||||
if strings.HasPrefix(strings.ToLower(p.Name), strings.ToLower(q)) {
|
||||
out = append(out, p)
|
||||
@@ -54,7 +56,7 @@ func colorPaletteDemo(c *ui.Context, log func(string)) {
|
||||
custom, disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "492", func() state { return state{color: ui.Hex("#7D2034"), custom: true} })
|
||||
swatches := []mujica.ColorSwatch{
|
||||
swatches := []input.ColorSwatch{
|
||||
{Color: ui.Hex("#7D2034"), Name: "Wine"}, {Color: ui.Hex("#A12635"), Name: "Crimson"}, {Color: ui.Hex("#AD9164"), Name: "Old gold"},
|
||||
{Color: ui.Hex("#805714"), Name: "Bronze"}, {Color: ui.Hex("#326247"), Name: "Ivy"}, {Color: ui.Hex("#355E80"), Name: "Slate blue"},
|
||||
{Color: ui.Hex("#4B3A5A"), Name: "Plum"}, {Color: ui.Hex("#211C1D"), Name: "Ink"}, {Color: ui.Hex("#6A5B55"), Name: "Taupe"},
|
||||
@@ -65,7 +67,7 @@ func colorPaletteDemo(c *ui.Context, log func(string)) {
|
||||
ui.Checkbox(c, &s.custom, "Custom entry")
|
||||
ui.Checkbox(c, &s.disabled, "Disabled")
|
||||
})
|
||||
if mujica.ColorPalette(c, &s.color, swatches, mujica.ColorPaletteOptions{Columns: 6, Custom: s.custom, Disabled: s.disabled, Label: "Tag color"}).Changed() {
|
||||
if input.ColorPalette(c, &s.color, swatches, input.ColorPaletteOptions{Columns: 6, Custom: s.custom, Disabled: s.disabled, Label: "Tag color"}).Changed() {
|
||||
log(fmt.Sprintf("palette: %v", s.color))
|
||||
}
|
||||
})
|
||||
@@ -73,14 +75,14 @@ func colorPaletteDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func signaturePadDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
strokes [][]mujica.SignaturePoint
|
||||
strokes [][]input.SignaturePoint
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "493", func() state { return state{} })
|
||||
ui.Column(c).Gap(16).Width(480).Children(func() {
|
||||
ui.Checkbox(c, &s.disabled, "Disabled")
|
||||
if mujica.SignaturePad(c, &s.strokes, mujica.SignaturePadOptions{Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("signature: %d strokes, %d bytes of path", len(s.strokes), len(mujica.SignaturePath(s.strokes))))
|
||||
if input.SignaturePad(c, &s.strokes, input.SignaturePadOptions{Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("signature: %d strokes, %d bytes of path", len(s.strokes), len(input.SignaturePath(s.strokes))))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -99,7 +101,7 @@ func bannerDemo(c *ui.Context, log func(string)) {
|
||||
ui.Checkbox(c, &s.closable, "Closable")
|
||||
})
|
||||
})
|
||||
opts := mujica.BannerOptions{Severity: feedbackSeverities[s.sev]}
|
||||
opts := input.BannerOptions{Severity: feedbackSeverities[s.sev]}
|
||||
if s.action {
|
||||
opts.Action = "Details"
|
||||
}
|
||||
@@ -107,14 +109,14 @@ func bannerDemo(c *ui.Context, log func(string)) {
|
||||
if s.closable {
|
||||
open = &s.op
|
||||
}
|
||||
r := mujica.Banner(c, open, "Scheduled maintenance tonight from 22:00 to 23:00.", opts)
|
||||
r := input.Banner(c, open, "Scheduled maintenance tonight from 22:00 to 23:00.", opts)
|
||||
if r.Action {
|
||||
log("banner: action")
|
||||
}
|
||||
if r.Closed {
|
||||
log("banner: closed")
|
||||
}
|
||||
if !s.op && mujica.Button(c, "Show again", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if !s.op && input.Button(c, "Show again", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.op = true
|
||||
log("banner: reopened")
|
||||
}
|
||||
@@ -139,7 +141,7 @@ func undoToastDemo(c *ui.Context, log func(string)) {
|
||||
for i, n := range s.notes {
|
||||
ui.Row(c).Key(n).Gap(12).AlignItems(ui.Center).Children(func() {
|
||||
ui.Text(c, n).Grow(1)
|
||||
if mujica.Button(c, "Delete", mujica.ButtonOptions{Variant: mujica.Ghost}).Clicked() {
|
||||
if input.Button(c, "Delete", input.ButtonOptions{Variant: input.Ghost}).Clicked() {
|
||||
s.removed, s.open = n, true
|
||||
s.notes = append(s.notes[:i:i], s.notes[i+1:]...)
|
||||
log("undo toast: deleted " + n)
|
||||
@@ -151,7 +153,7 @@ func undoToastDemo(c *ui.Context, log func(string)) {
|
||||
return
|
||||
}
|
||||
removed := s.removed
|
||||
r := mujica.UndoToast(c, &s.open, "Deleted “"+removed+"”", mujica.UndoToastOptions{
|
||||
r := feedback.UndoToast(c, &s.open, "Deleted “"+removed+"”", feedback.UndoToastOptions{
|
||||
Duration: time.Duration(s.seconds * float64(time.Second)),
|
||||
OnUndo: func() { s.notes = append(s.notes, removed) },
|
||||
})
|
||||
@@ -168,7 +170,7 @@ func masonryDemo(c *ui.Context, log func(string)) {
|
||||
count, width float64
|
||||
}
|
||||
s := ui.Local(c.Root(), "496", func() state { return state{count: 40, width: 180} })
|
||||
k := mujica.Tokens(c)
|
||||
k := core.Tokens(c)
|
||||
ui.Column(c).Gap(16).FillWidth().Children(func() {
|
||||
ui.Row(c).Gap(12).AlignItems(ui.Center).Children(func() {
|
||||
ui.Text(c, fmt.Sprintf("Items %.0f", s.count))
|
||||
@@ -178,7 +180,7 @@ func masonryDemo(c *ui.Context, log func(string)) {
|
||||
ui.Text(c, fmt.Sprintf("Column %.0f", s.width))
|
||||
ui.Slider(c, &s.width, 120, 320).Width(160)
|
||||
})
|
||||
mujica.Masonry(c, int(s.count), mujica.MasonryOptions{Key: strconv.Itoa, ColumnWidth: float32(s.width)}, func(i int) {
|
||||
input.Masonry(c, int(s.count), input.MasonryOptions{Key: strconv.Itoa, ColumnWidth: float32(s.width)}, func(i int) {
|
||||
card := ui.Column(c).Height(float32(60+i*37%90)).Padding(10).Background(k.Surface).Border(1, k.Border).Radius(4).Cursor(ui.CursorPointer)
|
||||
card.Children(func() { ui.Text(c, "Card "+strconv.Itoa(i+1)) })
|
||||
if card.Clicked() {
|
||||
@@ -194,7 +196,7 @@ func resizablePanelGroupDemo(c *ui.Context, log func(string)) {
|
||||
vertical, disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "497", func() state { return state{} })
|
||||
k := mujica.Tokens(c)
|
||||
k := core.Tokens(c)
|
||||
pane := func(name string) func() {
|
||||
return func() {
|
||||
ui.Column(c).Fill().Padding(12).Background(k.Surface).Children(func() { ui.Text(c, name) })
|
||||
@@ -207,11 +209,11 @@ func resizablePanelGroupDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
ui.Checkbox(c, &s.disabled, "Disabled")
|
||||
})
|
||||
r := mujica.ResizablePanelGroup(c, &s.sizes, []mujica.ResizablePanel{
|
||||
r := input.ResizablePanelGroup(c, &s.sizes, []input.ResizablePanel{
|
||||
{Label: "Files", Collapsible: true, Build: pane("Files")},
|
||||
{Label: "Editor", Min: 160, Build: pane("Editor")},
|
||||
{Label: "Outline", Collapsible: true, Build: pane("Outline")},
|
||||
}, mujica.ResizablePanelGroupOptions{Vertical: s.vertical, Disabled: s.disabled})
|
||||
}, input.ResizablePanelGroupOptions{Vertical: s.vertical, Disabled: s.disabled})
|
||||
r.Element.FillWidth().Height(320)
|
||||
if r.Committed {
|
||||
log(fmt.Sprintf("panels: save %.2f", s.sizes))
|
||||
@@ -221,19 +223,19 @@ func resizablePanelGroupDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func dockPanelDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
layout mujica.DockLayout
|
||||
layout input.DockLayout
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "498", func() state {
|
||||
return state{layout: mujica.DockLayout{Zones: [4][]string{{"editor"}, {"files"}, {"outline"}, {"terminal", "problems"}}}}
|
||||
return state{layout: input.DockLayout{Zones: [4][]string{{"editor"}, {"files"}, {"outline"}, {"terminal", "problems"}}}}
|
||||
})
|
||||
var items []mujica.DockItem
|
||||
var items []input.DockItem
|
||||
for _, id := range []string{"files", "editor", "outline", "terminal", "problems"} {
|
||||
items = append(items, mujica.DockItem{ID: id, Title: strings.ToUpper(id[:1]) + id[1:], Build: func() { ui.Text(c, "Contents of "+id) }})
|
||||
items = append(items, input.DockItem{ID: id, Title: strings.ToUpper(id[:1]) + id[1:], Build: func() { ui.Text(c, "Contents of "+id) }})
|
||||
}
|
||||
ui.Column(c).Gap(16).FillWidth().Children(func() {
|
||||
ui.Checkbox(c, &s.disabled, "Disabled")
|
||||
r := mujica.DockPanel(c, &s.layout, items, mujica.DockPanelOptions{Disabled: s.disabled})
|
||||
r := input.DockPanel(c, &s.layout, items, input.DockPanelOptions{Disabled: s.disabled})
|
||||
r.Element.FillWidth().Height(440)
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("dock: save %v", s.layout.Zones))
|
||||
@@ -248,7 +250,7 @@ func qrCodeDemo(c *ui.Context, log func(string)) {
|
||||
size float64
|
||||
}
|
||||
s := ui.Local(c.Root(), "499", func() state { return state{text: "https://example.com/mujica", level: 1, size: 200} })
|
||||
levels := []mujica.QRLevel{mujica.QRLow, mujica.QRMedium, mujica.QRQuartile, mujica.QRHigh}
|
||||
levels := []input.QRLevel{input.QRLow, input.QRMedium, input.QRQuartile, input.QRHigh}
|
||||
ui.Column(c).Gap(16).FillWidth().Children(func() {
|
||||
feedbackSection(c, "Parameters", func() {
|
||||
if ui.TextInput(c, &s.text).Width(360).Changed() {
|
||||
@@ -262,31 +264,31 @@ func qrCodeDemo(c *ui.Context, log func(string)) {
|
||||
ui.Slider(c, &s.size, 100, 320).Width(200)
|
||||
})
|
||||
})
|
||||
mujica.QRCode(c, s.text, mujica.QRCodeOptions{Level: levels[s.level], Size: float32(s.size)})
|
||||
if m, err := mujica.EncodeQR([]byte(s.text), levels[s.level]); err == nil {
|
||||
input.QRCode(c, s.text, input.QRCodeOptions{Level: levels[s.level], Size: float32(s.size)})
|
||||
if m, err := input.EncodeQR([]byte(s.text), levels[s.level]); err == nil {
|
||||
ui.Text(c, fmt.Sprintf("Version %d, mask %d, %d × %d modules", m.Version, m.Mask, m.Size(), m.Size()))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func activityFeedDemo(c *ui.Context, log func(string)) {
|
||||
type state struct{ events []mujica.Activity }
|
||||
type state struct{ events []feedback.Activity }
|
||||
s := ui.Local(c.Root(), "500", func() state {
|
||||
now := time.Now()
|
||||
var ev []mujica.Activity
|
||||
var ev []feedback.Activity
|
||||
actors := []string{"Sakiko", "Mutsumi", "Uika", "Umiri", "Nyamu"}
|
||||
verbs := []string{"commented on", "edited", "approved", "closed", "opened"}
|
||||
for i := range 400 {
|
||||
ev = append(ev, mujica.Activity{ID: strconv.Itoa(i), Actor: actors[i%5], Action: verbs[i*3%5], Object: fmt.Sprintf("Score #%d", 400-i), Time: now.Add(-time.Duration(i) * 97 * time.Minute)})
|
||||
ev = append(ev, feedback.Activity{ID: strconv.Itoa(i), Actor: actors[i%5], Action: verbs[i*3%5], Object: fmt.Sprintf("Score #%d", 400-i), Time: now.Add(-time.Duration(i) * 97 * time.Minute)})
|
||||
}
|
||||
return state{events: ev}
|
||||
})
|
||||
ui.Column(c).Gap(16).FillWidth().Children(func() {
|
||||
if mujica.Button(c, "Add event", mujica.ButtonOptions{Variant: mujica.Secondary}).AlignSelf(ui.Start).Clicked() {
|
||||
e := mujica.Activity{ID: fmt.Sprint("n", len(s.events)), Actor: "You", Action: "added", Object: "a new note", Time: time.Now()}
|
||||
s.events = append([]mujica.Activity{e}, s.events...)
|
||||
if input.Button(c, "Add event", input.ButtonOptions{Variant: input.Secondary}).AlignSelf(ui.Start).Clicked() {
|
||||
e := feedback.Activity{ID: fmt.Sprint("n", len(s.events)), Actor: "You", Action: "added", Object: "a new note", Time: time.Now()}
|
||||
s.events = append([]feedback.Activity{e}, s.events...)
|
||||
log("activity: added " + e.ID)
|
||||
}
|
||||
mujica.ActivityFeed(c, s.events, mujica.ActivityFeedOptions{}).FillWidth().Height(420)
|
||||
feedback.ActivityFeed(c, s.events, feedback.ActivityFeedOptions{}).FillWidth().Height(420)
|
||||
})
|
||||
}
|
||||
@@ -5,8 +5,8 @@ var feedbackPages = []page{
|
||||
ID: "091", Name: "Alert", ZhName: "提示条 警告 通知横幅", Category: "Feedback",
|
||||
Demo: alertDemo,
|
||||
Code: `open := true
|
||||
r := mujica.Alert(c, &open, "The archive could not be read.", mujica.AlertOptions{
|
||||
Severity: mujica.SeverityError,
|
||||
r := feedback.Alert(c, &open, "The archive could not be read.", feedback.AlertOptions{
|
||||
Severity: core.SeverityError,
|
||||
Title: "Import failed",
|
||||
Action: "Retry",
|
||||
})
|
||||
@@ -18,11 +18,11 @@ if r.Action {
|
||||
{
|
||||
ID: "092", Name: "Toast", ZhName: "轻提示 消息队列 吐司", Category: "Feedback",
|
||||
Demo: toastDemo,
|
||||
Code: `if mujica.Button(c, "Delete note", mujica.ButtonOptions{Variant: mujica.Danger}).Clicked() {
|
||||
mujica.ShowToast(c, mujica.ToastMessage{ID: "note-1", Message: "Note deleted", Action: "Undo"})
|
||||
Code: `if input.Button(c, "Delete note", input.ButtonOptions{Variant: input.Danger}).Clicked() {
|
||||
feedback.ShowToast(c, feedback.ToastMessage{ID: "note-1", Message: "Note deleted", Action: "Undo"})
|
||||
}
|
||||
// Last in the view:
|
||||
if r := mujica.Toaster(c); r.Action == "note-1" {
|
||||
if r := feedback.Toaster(c); r.Action == "note-1" {
|
||||
// restore the note
|
||||
}`,
|
||||
Keys: "Toasts never take the focus. Tab reaches their buttons after the page; Escape closes the focused toast. Hover or focus pauses its timer.",
|
||||
@@ -30,9 +30,9 @@ if r := mujica.Toaster(c); r.Action == "note-1" {
|
||||
{
|
||||
ID: "093", Name: "Progress", ZhName: "进度条 环形进度 计量条", Category: "Feedback",
|
||||
Demo: progressDemo,
|
||||
Code: `mujica.Progress(c, 0.62, mujica.ProgressOptions{Label: "Uploading", ShowValue: true})
|
||||
mujica.Progress(c, 0, mujica.ProgressOptions{Shape: mujica.ProgressRing, Indeterminate: true, Label: "Connecting"})
|
||||
mujica.Meter(c, 93, mujica.MeterOptions{
|
||||
Code: `feedback.Progress(c, 0.62, feedback.ProgressOptions{Label: "Uploading", ShowValue: true})
|
||||
feedback.Progress(c, 0, feedback.ProgressOptions{Shape: feedback.ProgressRing, Indeterminate: true, Label: "Connecting"})
|
||||
feedback.Meter(c, 93, feedback.MeterOptions{
|
||||
Max: 100, Label: "Disk", ShowValue: true,
|
||||
Levels: &ui.MeterLevels{Warning: 70, Critical: 90},
|
||||
})`,
|
||||
@@ -41,16 +41,16 @@ mujica.Meter(c, 93, mujica.MeterOptions{
|
||||
{
|
||||
ID: "094", Name: "Spinner", ZhName: "加载标记 加载中 转圈", Category: "Feedback",
|
||||
Demo: spinnerDemo,
|
||||
Code: `mujica.Spinner(c, mujica.SpinnerOptions{Size: 20, Label: "Syncing", ShowLabel: true})`,
|
||||
Code: `core.Spinner(c, core.SpinnerOptions{Size: 20, Label: "Syncing", ShowLabel: true})`,
|
||||
Keys: "Not focusable; read as an indeterminate progress indicator named by Label.",
|
||||
},
|
||||
{
|
||||
ID: "095", Name: "Skeleton", ZhName: "骨架屏 占位", Category: "Feedback",
|
||||
Demo: skeletonDemo,
|
||||
Code: `ui.Row(c).Gap(12).Children(func() {
|
||||
mujica.Skeleton(c, mujica.SkeletonOptions{Shape: mujica.SkeletonCircle})
|
||||
feedback.Skeleton(c, feedback.SkeletonOptions{Shape: feedback.SkeletonCircle})
|
||||
ui.Column(c).Grow(1).Children(func() {
|
||||
mujica.Skeleton(c, mujica.SkeletonOptions{Lines: 2})
|
||||
feedback.Skeleton(c, feedback.SkeletonOptions{Lines: 2})
|
||||
})
|
||||
})`,
|
||||
Keys: "Decorative; not focusable and hidden from assistive technology.",
|
||||
@@ -58,8 +58,8 @@ mujica.Meter(c, 93, mujica.MeterOptions{
|
||||
{
|
||||
ID: "096", Name: "EmptyState", ZhName: "空状态 无数据 无搜索结果", Category: "Feedback",
|
||||
Demo: emptyStateDemo,
|
||||
Code: `r := mujica.EmptyState(c, mujica.EmptyStateOptions{
|
||||
Kind: mujica.EmptyNoResults,
|
||||
Code: `r := feedback.EmptyState(c, feedback.EmptyStateOptions{
|
||||
Kind: feedback.EmptyNoResults,
|
||||
Query: query,
|
||||
Action: "Clear search",
|
||||
})
|
||||
@@ -71,8 +71,8 @@ if r.Action {
|
||||
{
|
||||
ID: "097", Name: "Result", ZhName: "结果页 成功 失败 无权限", Category: "Feedback",
|
||||
Demo: resultDemo,
|
||||
Code: `r := mujica.Result(c, mujica.ResultOptions{
|
||||
Kind: mujica.ResultForbidden,
|
||||
Code: `r := feedback.Result(c, feedback.ResultOptions{
|
||||
Kind: feedback.ResultForbidden,
|
||||
Description: "You need the Editor role to change billing.",
|
||||
Actions: []string{"Request access", "Go back"},
|
||||
})
|
||||
@@ -85,14 +85,14 @@ case 1: // navigate back
|
||||
{
|
||||
ID: "098", Name: "StatusIndicator", ZhName: "状态标记 在线 离线", Category: "Feedback",
|
||||
Demo: statusDemo,
|
||||
Code: `mujica.StatusIndicator(c, mujica.StatusSuccess, mujica.StatusIndicatorOptions{Label: "API online"})
|
||||
mujica.StatusIndicator(c, mujica.StatusOffline, mujica.StatusIndicatorOptions{})`,
|
||||
Code: `feedback.StatusIndicator(c, feedback.StatusSuccess, feedback.StatusIndicatorOptions{Label: "API online"})
|
||||
feedback.StatusIndicator(c, feedback.StatusOffline, feedback.StatusIndicatorOptions{})`,
|
||||
Keys: "Not focusable; read as a status with its word.",
|
||||
},
|
||||
{
|
||||
ID: "099", Name: "NotificationCenter", ZhName: "通知中心 消息列表", Category: "Feedback",
|
||||
Demo: notificationDemo,
|
||||
Code: `r := mujica.NotificationCenter(c, &items, mujica.NotificationCenterOptions{})
|
||||
Code: `r := feedback.NotificationCenter(c, &items, feedback.NotificationCenterOptions{})
|
||||
if r.Action == "a" {
|
||||
// open the build log
|
||||
}`,
|
||||
@@ -101,9 +101,9 @@ if r.Action == "a" {
|
||||
{
|
||||
ID: "100", Name: "LoadingOverlay", ZhName: "加载遮罩 区域加载", Category: "Feedback",
|
||||
Demo: loadingOverlayDemo,
|
||||
Code: `r := mujica.LoadingOverlay(c, loading, mujica.LoadingOverlayOptions{Cancel: "Cancel"}, func() {
|
||||
Code: `r := feedback.LoadingOverlay(c, loading, feedback.LoadingOverlayOptions{Cancel: "Cancel"}, func() {
|
||||
ui.Text(c, "Quarterly report")
|
||||
mujica.Button(c, "Export", mujica.ButtonOptions{})
|
||||
input.Button(c, "Export", input.ButtonOptions{})
|
||||
})
|
||||
if r.Canceled {
|
||||
loading = false
|
||||
|
||||
@@ -6,19 +6,21 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/theme"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/feedback"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||
)
|
||||
|
||||
// feedbackSection titles a part of a demo.
|
||||
func feedbackSection(c *ui.Context, title string, fn func()) {
|
||||
ui.Column(c).Gap(8).FillWidth().Children(func() {
|
||||
ui.Text(c, title).FontSize(theme.CaptionSize).TextColor(mujica.Tokens(c).TextMuted)
|
||||
ui.Text(c, title).FontSize(theme.CaptionSize).TextColor(core.Tokens(c).TextMuted)
|
||||
fn()
|
||||
})
|
||||
}
|
||||
|
||||
var feedbackSeverities = []mujica.Severity{mujica.SeverityInfo, mujica.SeveritySuccess, mujica.SeverityWarning, mujica.SeverityError}
|
||||
var feedbackSeverities = []core.Severity{core.SeverityInfo, core.SeveritySuccess, core.SeverityWarning, core.SeverityError}
|
||||
|
||||
func alertDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
@@ -38,7 +40,7 @@ func alertDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
})
|
||||
feedbackSection(c, "Live", func() {
|
||||
opts := mujica.AlertOptions{Severity: feedbackSeverities[s.sev]}
|
||||
opts := feedback.AlertOptions{Severity: feedbackSeverities[s.sev]}
|
||||
if s.title {
|
||||
opts.Title = "Import failed"
|
||||
}
|
||||
@@ -49,21 +51,21 @@ func alertDemo(c *ui.Context, log func(string)) {
|
||||
if s.closable {
|
||||
open = &s.open
|
||||
}
|
||||
r := mujica.Alert(c, open, "The archive could not be read.", opts)
|
||||
r := feedback.Alert(c, open, "The archive could not be read.", opts)
|
||||
if r.Action {
|
||||
log("alert: action Retry")
|
||||
}
|
||||
if r.Closed {
|
||||
log("alert: closed")
|
||||
}
|
||||
if s.closable && !s.open && mujica.Button(c, "Show again", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if s.closable && !s.open && input.Button(c, "Show again", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.open = true
|
||||
log("alert: reopened")
|
||||
}
|
||||
})
|
||||
feedbackSection(c, "Severities", func() {
|
||||
for _, sev := range feedbackSeverities {
|
||||
mujica.Alert(c, nil, "Severity "+sev.String()+" uses its own icon, bar and name.", mujica.AlertOptions{Severity: sev}).Element.Key(sev)
|
||||
feedback.Alert(c, nil, "Severity "+sev.String()+" uses its own icon, bar and name.", feedback.AlertOptions{Severity: sev}).Element.Key(sev)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -89,28 +91,28 @@ func toastDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
feedbackSection(c, "Actions", func() {
|
||||
ui.Row(c).Gap(12).Wrap().Children(func() {
|
||||
if mujica.Button(c, "Show toast", mujica.ButtonOptions{}).Clicked() {
|
||||
if input.Button(c, "Show toast", input.ButtonOptions{}).Clicked() {
|
||||
s.n++
|
||||
m := mujica.ToastMessage{ID: fmt.Sprint(s.n), Severity: feedbackSeverities[s.sev], Message: fmt.Sprintf("Message %d", s.n)}
|
||||
m := feedback.ToastMessage{ID: fmt.Sprint(s.n), Severity: feedbackSeverities[s.sev], Message: fmt.Sprintf("Message %d", s.n)}
|
||||
if s.action {
|
||||
m.Action = "Undo"
|
||||
}
|
||||
if m.Severity != mujica.SeverityError {
|
||||
if m.Severity != core.SeverityError {
|
||||
m.Duration = time.Duration(s.seconds * float64(time.Second))
|
||||
}
|
||||
mujica.ShowToast(c, m)
|
||||
feedback.ShowToast(c, m)
|
||||
log("toast: show " + m.ID)
|
||||
}
|
||||
if mujica.Button(c, "Show five at once", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Show five at once", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
for range 5 {
|
||||
s.n++
|
||||
mujica.ShowToast(c, mujica.ToastMessage{ID: fmt.Sprint(s.n), Message: fmt.Sprintf("Queued %d", s.n)})
|
||||
feedback.ShowToast(c, feedback.ToastMessage{ID: fmt.Sprint(s.n), Message: fmt.Sprintf("Queued %d", s.n)})
|
||||
}
|
||||
log("toast: five queued, three visible")
|
||||
}
|
||||
if mujica.Button(c, "Repeat same ID", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Repeat same ID", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.n++
|
||||
mujica.ShowToast(c, mujica.ToastMessage{ID: "sync", Message: fmt.Sprintf("Synced %d times", s.n)})
|
||||
feedback.ShowToast(c, feedback.ToastMessage{ID: "sync", Message: fmt.Sprintf("Synced %d times", s.n)})
|
||||
log("toast: dedupe id sync")
|
||||
}
|
||||
})
|
||||
@@ -118,7 +120,7 @@ func toastDemo(c *ui.Context, log func(string)) {
|
||||
ui.TextInput(c, &s.note).Width(320)
|
||||
})
|
||||
})
|
||||
if r := mujica.Toaster(c); r.Action != "" {
|
||||
if r := feedback.Toaster(c); r.Action != "" {
|
||||
log("toast: action " + r.Action)
|
||||
} else if r.Closed != "" {
|
||||
log("toast: closed " + r.Closed)
|
||||
@@ -149,8 +151,8 @@ func progressDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
})
|
||||
feedbackSection(c, "Progress", func() {
|
||||
mujica.Progress(c, s.value, mujica.ProgressOptions{
|
||||
Shape: mujica.ProgressShape(s.ring), Indeterminate: s.indet, Label: "Uploading", ShowValue: s.showValue,
|
||||
feedback.Progress(c, s.value, feedback.ProgressOptions{
|
||||
Shape: feedback.ProgressShape(s.ring), Indeterminate: s.indet, Label: "Uploading", ShowValue: s.showValue,
|
||||
})
|
||||
})
|
||||
feedbackSection(c, "Meter with thresholds 70 and 90", func() {
|
||||
@@ -161,8 +163,8 @@ func progressDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
})
|
||||
v := float64(int(s.meter))
|
||||
mujica.Meter(c, v, mujica.MeterOptions{Max: 100, Label: "Disk", ShowValue: true, Levels: &ui.MeterLevels{Warning: 70, Critical: 90}})
|
||||
mujica.Meter(c, 100-v, mujica.MeterOptions{Max: 100, Label: "Battery (low is bad)", ShowValue: true, Levels: &ui.MeterLevels{Warning: 30, Critical: 10}})
|
||||
feedback.Meter(c, v, feedback.MeterOptions{Max: 100, Label: "Disk", ShowValue: true, Levels: &ui.MeterLevels{Warning: 70, Critical: 90}})
|
||||
feedback.Meter(c, 100-v, feedback.MeterOptions{Max: 100, Label: "Battery (low is bad)", ShowValue: true, Levels: &ui.MeterLevels{Warning: 30, Critical: 10}})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -188,13 +190,13 @@ func spinnerDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
feedbackSection(c, "Live", func() {
|
||||
if s.active {
|
||||
mujica.Spinner(c, mujica.SpinnerOptions{Size: float32(s.size), Label: "Syncing", ShowLabel: s.showLabel})
|
||||
core.Spinner(c, core.SpinnerOptions{Size: float32(s.size), Label: "Syncing", ShowLabel: s.showLabel})
|
||||
} else {
|
||||
ui.Text(c, "Done")
|
||||
}
|
||||
})
|
||||
feedbackSection(c, "In a button", func() {
|
||||
mujica.Button(c, "Saving", mujica.ButtonOptions{Loading: true})
|
||||
input.Button(c, "Saving", input.ButtonOptions{Loading: true})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -216,15 +218,15 @@ func skeletonDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
})
|
||||
feedbackSection(c, "Card (same size loading and loaded)", func() {
|
||||
ui.Row(c).Gap(12).Padding(16).Width(420).Background(mujica.Tokens(c).Surface).Radius(theme.CardRadius).Children(func() {
|
||||
ui.Row(c).Gap(12).Padding(16).Width(420).Background(core.Tokens(c).Surface).Radius(theme.CardRadius).Children(func() {
|
||||
if s.loading {
|
||||
mujica.Skeleton(c, mujica.SkeletonOptions{Shape: mujica.SkeletonCircle})
|
||||
feedback.Skeleton(c, feedback.SkeletonOptions{Shape: feedback.SkeletonCircle})
|
||||
ui.Column(c).Grow(1).Children(func() {
|
||||
mujica.Skeleton(c, mujica.SkeletonOptions{Lines: int(s.lines)})
|
||||
feedback.Skeleton(c, feedback.SkeletonOptions{Lines: int(s.lines)})
|
||||
})
|
||||
return
|
||||
}
|
||||
ui.Box(c).Size(40, 40).Radius(20).Background(mujica.Tokens(c).Selection)
|
||||
ui.Box(c).Size(40, 40).Radius(20).Background(core.Tokens(c).Selection)
|
||||
ui.Column(c).Grow(1).Children(func() {
|
||||
for i := range int(s.lines) {
|
||||
ui.Text(c, fmt.Sprintf("Loaded line %d", i+1)).LineHeight(theme.LineHeight)
|
||||
@@ -233,7 +235,7 @@ func skeletonDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
})
|
||||
feedbackSection(c, "Rect", func() {
|
||||
mujica.Skeleton(c, mujica.SkeletonOptions{Shape: mujica.SkeletonRect, Width: 420, Height: 96})
|
||||
feedback.Skeleton(c, feedback.SkeletonOptions{Shape: feedback.SkeletonRect, Width: 420, Height: 96})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -257,14 +259,14 @@ func emptyStateDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
})
|
||||
feedbackSection(c, "Live", func() {
|
||||
opts := mujica.EmptyStateOptions{Kind: mujica.EmptyKind(s.kind), NoIcon: !s.icon}
|
||||
opts := feedback.EmptyStateOptions{Kind: feedback.EmptyKind(s.kind), NoIcon: !s.icon}
|
||||
if s.kind == 1 {
|
||||
opts.Query = s.query
|
||||
}
|
||||
if s.action {
|
||||
opts.Action = []string{"New project", "Clear search"}[s.kind]
|
||||
}
|
||||
if mujica.EmptyState(c, opts).Action {
|
||||
if feedback.EmptyState(c, opts).Action {
|
||||
log("empty state: action " + opts.Action)
|
||||
s.query = ""
|
||||
}
|
||||
@@ -281,7 +283,7 @@ func resultDemo(c *ui.Context, log func(string)) {
|
||||
feedbackSection(c, "Parameters", func() {
|
||||
ui.Segmented(c, &s.kind, "Success", "Failure", "Forbidden")
|
||||
})
|
||||
r := mujica.Result(c, mujica.ResultOptions{Kind: mujica.ResultKind(s.kind), Description: descs[s.kind], Actions: actions[s.kind]})
|
||||
r := feedback.Result(c, feedback.ResultOptions{Kind: feedback.ResultKind(s.kind), Description: descs[s.kind], Actions: actions[s.kind]})
|
||||
if r.Action >= 0 {
|
||||
log("result: action " + actions[s.kind][r.Action])
|
||||
}
|
||||
@@ -297,21 +299,21 @@ func statusDemo(c *ui.Context, log func(string)) {
|
||||
ui.Column(c).Gap(16).FillWidth().Children(func() {
|
||||
feedbackSection(c, "Parameters", func() {
|
||||
if ui.Segmented(c, &s.status, "Success", "Warning", "Error", "Offline", "Busy").Changed() {
|
||||
log("status: " + mujica.Status(s.status).String())
|
||||
log("status: " + feedback.Status(s.status).String())
|
||||
}
|
||||
ui.Checkbox(c, &s.custom, "Custom label")
|
||||
})
|
||||
feedbackSection(c, "Live", func() {
|
||||
opts := mujica.StatusIndicatorOptions{}
|
||||
opts := feedback.StatusIndicatorOptions{}
|
||||
if s.custom {
|
||||
opts.Label = "Database replica"
|
||||
}
|
||||
mujica.StatusIndicator(c, mujica.Status(s.status), opts)
|
||||
feedback.StatusIndicator(c, feedback.Status(s.status), opts)
|
||||
})
|
||||
feedbackSection(c, "All states (shape and word, not color alone)", func() {
|
||||
ui.Row(c).Gap(20).Wrap().Children(func() {
|
||||
for st := mujica.StatusSuccess; st <= mujica.StatusBusy; st++ {
|
||||
mujica.StatusIndicator(c, st, mujica.StatusIndicatorOptions{}).Key(st)
|
||||
for st := feedback.StatusSuccess; st <= feedback.StatusBusy; st++ {
|
||||
feedback.StatusIndicator(c, st, feedback.StatusIndicatorOptions{}).Key(st)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -320,11 +322,11 @@ func statusDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func notificationDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
items []mujica.Notification
|
||||
items []feedback.Notification
|
||||
n int
|
||||
}
|
||||
s := ui.Local(c.Root(), "099", func() state {
|
||||
return state{items: []mujica.Notification{
|
||||
return state{items: []feedback.Notification{
|
||||
{ID: "a", Title: "Build failed", Body: "main · 3 tests failing", Time: "2 min ago", Action: "View log"},
|
||||
{ID: "b", Title: "Invite accepted", Body: "Mira joined the workspace", Time: "1 h ago"},
|
||||
{ID: "c", Title: "Backup complete", Time: "Yesterday", Read: true},
|
||||
@@ -332,15 +334,15 @@ func notificationDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
ui.Column(c).Gap(16).FillWidth().Children(func() {
|
||||
feedbackSection(c, "Caller-owned list", func() {
|
||||
if mujica.Button(c, "Add notification", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Add notification", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.n++
|
||||
id := fmt.Sprintf("new-%d", s.n)
|
||||
s.items = append([]mujica.Notification{{ID: id, Title: fmt.Sprintf("Comment %d on the design doc", s.n), Time: "now"}}, s.items...)
|
||||
s.items = append([]feedback.Notification{{ID: id, Title: fmt.Sprintf("Comment %d on the design doc", s.n), Time: "now"}}, s.items...)
|
||||
log("notifications: added " + id)
|
||||
}
|
||||
})
|
||||
ui.Column(c).Width(520).Height(420).Children(func() {
|
||||
r := mujica.NotificationCenter(c, &s.items, mujica.NotificationCenterOptions{})
|
||||
r := feedback.NotificationCenter(c, &s.items, feedback.NotificationCenterOptions{})
|
||||
for _, ev := range [][2]string{{"action", r.Action}, {"toggled", r.Toggled}, {"opened", r.Opened}, {"removed", r.Removed}} {
|
||||
if ev[1] != "" {
|
||||
log("notifications: " + ev[0] + " " + ev[1])
|
||||
@@ -372,15 +374,15 @@ func loadingOverlayDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
})
|
||||
feedbackSection(c, "Covered region", func() {
|
||||
opts := mujica.LoadingOverlayOptions{Label: "Generating report…"}
|
||||
opts := feedback.LoadingOverlayOptions{Label: "Generating report…"}
|
||||
if s.cancel {
|
||||
opts.Cancel = "Cancel"
|
||||
}
|
||||
r := mujica.LoadingOverlay(c, s.loading, opts, func() {
|
||||
ui.Column(c).Gap(8).Padding(16).FillWidth().AlignItems(ui.Start).Background(mujica.Tokens(c).Surface).Radius(theme.CardRadius).Children(func() {
|
||||
r := feedback.LoadingOverlay(c, s.loading, opts, func() {
|
||||
ui.Column(c).Gap(8).Padding(16).FillWidth().AlignItems(ui.Start).Background(core.Tokens(c).Surface).Radius(theme.CardRadius).Children(func() {
|
||||
ui.Text(c, "Quarterly report").Font(theme.SerifFont).FontSize(theme.H3Size)
|
||||
ui.Text(c, fmt.Sprintf("Exported %d times.", s.saves))
|
||||
if mujica.Button(c, "Export", mujica.ButtonOptions{}).Clicked() {
|
||||
if input.Button(c, "Export", input.ButtonOptions{}).Clicked() {
|
||||
s.saves++
|
||||
log("overlay: export clicked")
|
||||
}
|
||||
@@ -393,7 +395,7 @@ func loadingOverlayDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
})
|
||||
feedbackSection(c, "Outside the region (always usable)", func() {
|
||||
if mujica.Button(c, "Outside button", mujica.ButtonOptions{Variant: mujica.Secondary}).AlignSelf(ui.Start).Clicked() {
|
||||
if input.Button(c, "Outside button", input.ButtonOptions{Variant: input.Secondary}).AlignSelf(ui.Start).Clicked() {
|
||||
log("overlay: outside clicked")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -7,7 +7,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/display"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/files"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
var filesFolders = map[string][]string{
|
||||
@@ -16,24 +20,24 @@ var filesFolders = map[string][]string{
|
||||
"/court/decrees": {"42.pdf", "crest.png"},
|
||||
}
|
||||
|
||||
func filesEntries(dir string) []mujica.FileEntry {
|
||||
var entries []mujica.FileEntry
|
||||
func filesEntries(dir string) []files.FileEntry {
|
||||
var entries []files.FileEntry
|
||||
for _, n := range filesFolders[dir] {
|
||||
name := strings.TrimSuffix(n, "/")
|
||||
entries = append(entries, mujica.FileEntry{Path: path.Join(dir, name), Name: name, Dir: name != n, Size: 2048})
|
||||
entries = append(entries, files.FileEntry{Path: path.Join(dir, name), Name: name, Dir: name != n, Size: 2048})
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
func fileExplorerDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
explorer mujica.FileExplorerState
|
||||
explorer files.FileExplorerState
|
||||
disabled, empty bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "381", func() state { return state{} })
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
mediaCheck(c, log, &s.empty, "Empty folder")
|
||||
r := mujica.FileExplorer(c, &s.explorer, mujica.FileExplorerOptions{
|
||||
r := files.FileExplorer(c, &s.explorer, files.FileExplorerOptions{
|
||||
Roots: []string{"/court"}, Disabled: s.disabled,
|
||||
Folders: func(dir string) []string {
|
||||
var dirs []string
|
||||
@@ -44,7 +48,7 @@ func fileExplorerDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
return dirs
|
||||
},
|
||||
Entries: func(dir string) []mujica.FileEntry {
|
||||
Entries: func(dir string) []files.FileEntry {
|
||||
if s.empty {
|
||||
return nil
|
||||
}
|
||||
@@ -65,8 +69,8 @@ func fileExplorerDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func fileGridDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
grid mujica.FileGridState
|
||||
entries []mujica.FileEntry
|
||||
grid files.FileGridState
|
||||
entries []files.FileEntry
|
||||
disabled, thumbs, empty bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "382", func() state {
|
||||
@@ -79,11 +83,11 @@ func fileGridDemo(c *ui.Context, log func(string)) {
|
||||
if s.empty {
|
||||
entries = nil
|
||||
}
|
||||
r := mujica.FileGrid(c, entries, &s.grid, mujica.FileGridOptions{Disabled: s.disabled, Thumbnail: func(e mujica.FileEntry) bool {
|
||||
if !s.thumbs || mujica.FileKindOf(e.Name) != mujica.FileImage {
|
||||
r := files.FileGrid(c, entries, &s.grid, files.FileGridOptions{Disabled: s.disabled, Thumbnail: func(e files.FileEntry) bool {
|
||||
if !s.thumbs || files.FileKindOf(e.Name) != files.FileImage {
|
||||
return false
|
||||
}
|
||||
mujica.Image(c, mediaBitmaps[0], mujica.ImageOptions{Alt: e.Name}).Element.Fill()
|
||||
display.Image(c, mediaBitmaps[0], display.ImageOptions{Alt: e.Name}).Element.Fill()
|
||||
return true
|
||||
}})
|
||||
r.Element.Height(320).FillWidth()
|
||||
@@ -102,20 +106,20 @@ func fileIconDemo(c *ui.Context, log func(string)) {
|
||||
size float64
|
||||
}
|
||||
s := ui.Local(c.Root(), "383", func() state { return state{name: "seal.go", size: 32} })
|
||||
if mujica.TextInput(c, &s.name, mujica.TextInputOptions{Label: "File name"}).Changed() {
|
||||
if input.TextInput(c, &s.name, input.TextInputOptions{Label: "File name"}).Changed() {
|
||||
log("File name changed")
|
||||
}
|
||||
mediaCheck(c, log, &s.folder, "Folder")
|
||||
if s.folder {
|
||||
mediaCheck(c, log, &s.open, "Open folder")
|
||||
}
|
||||
if mujica.NumberInput(c, &s.size, mujica.NumberInputOptions{Min: 20, Max: 64, Step: 4, Label: "Icon size"}).Changed() {
|
||||
if input.NumberInput(c, &s.size, input.NumberInputOptions{Min: 20, Max: 64, Step: 4, Label: "Icon size"}).Changed() {
|
||||
log(fmt.Sprintf("Size: %.0f", s.size))
|
||||
}
|
||||
mujica.FileIcon(c, s.name, mujica.FileIconOptions{Folder: s.folder, Open: s.folder && s.open, Size: float32(s.size)})
|
||||
files.FileIcon(c, s.name, files.FileIconOptions{Folder: s.folder, Open: s.folder && s.open, Size: float32(s.size)})
|
||||
ui.Row(c).Gap(12).Children(func() {
|
||||
for _, name := range []string{"seal.go", "notes.md", "crest.png", "deed.pdf", "ledger.zip", "hymn.mp3", "rite.mov", "blob"} {
|
||||
mujica.FileIcon(c, name, mujica.FileIconOptions{Size: 32})
|
||||
files.FileIcon(c, name, files.FileIconOptions{Size: 32})
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -123,24 +127,24 @@ func fileIconDemo(c *ui.Context, log func(string)) {
|
||||
func filePreviewDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
kind int
|
||||
status mujica.DataStatus
|
||||
status data.DataStatus
|
||||
closed bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "384", func() state { return state{} })
|
||||
if mujica.Select(c, &s.kind, []mujica.SelectOption[int]{{Value: 0, Label: "Code"}, {Value: 1, Label: "Text"}, {Value: 2, Label: "Image"}, {Value: 3, Label: "Unsupported"}}, mujica.SelectOptions{Label: "Preview type"}).Changed() {
|
||||
if input.Select(c, &s.kind, []input.SelectOption[int]{{Value: 0, Label: "Code"}, {Value: 1, Label: "Text"}, {Value: 2, Label: "Image"}, {Value: 3, Label: "Unsupported"}}, input.SelectOptions{Label: "Preview type"}).Changed() {
|
||||
log("Preview type changed")
|
||||
s.closed = false
|
||||
}
|
||||
filesStatus(c, log, &s.status)
|
||||
if s.closed {
|
||||
if mujica.Button(c, "Show preview", mujica.ButtonOptions{}).Clicked() {
|
||||
if input.Button(c, "Show preview", input.ButtonOptions{}).Clicked() {
|
||||
s.closed = false
|
||||
log("Preview reopened")
|
||||
}
|
||||
return
|
||||
}
|
||||
f := []mujica.PreviewFile{{Name: "seal.go", Text: "package court\n\nfunc Seal() {}"}, {Name: "notes.md", Text: "The seal of the court."}, {Name: "crest.png", Image: mediaBitmaps[0]}, {Name: "deed.pdf", Size: 4096}}[s.kind]
|
||||
r := mujica.FilePreview(c, f, mujica.FilePreviewOptions{Status: s.status, Error: "Reading failed"})
|
||||
f := []files.PreviewFile{{Name: "seal.go", Text: "package court\n\nfunc Seal() {}"}, {Name: "notes.md", Text: "The seal of the court."}, {Name: "crest.png", Image: mediaBitmaps[0]}, {Name: "deed.pdf", Size: 4096}}[s.kind]
|
||||
r := files.FilePreview(c, f, files.FilePreviewOptions{Status: s.status, Error: "Reading failed"})
|
||||
r.Element.Height(280).FillWidth()
|
||||
if r.Closed {
|
||||
s.closed = true
|
||||
@@ -150,13 +154,13 @@ func filePreviewDemo(c *ui.Context, log func(string)) {
|
||||
log("Open: " + f.Name)
|
||||
}
|
||||
if r.Retried {
|
||||
s.status = mujica.DataReady
|
||||
s.status = data.DataReady
|
||||
log("Read retried")
|
||||
}
|
||||
}
|
||||
|
||||
func filesStatus(c *ui.Context, log func(string), value *mujica.DataStatus) {
|
||||
if mujica.Select(c, value, []mujica.SelectOption[mujica.DataStatus]{{Value: mujica.DataReady, Label: "Ready"}, {Value: mujica.DataLoading, Label: "Loading"}, {Value: mujica.DataFailed, Label: "Failed"}}, mujica.SelectOptions{Label: "Load status"}).Changed() {
|
||||
func filesStatus(c *ui.Context, log func(string), value *data.DataStatus) {
|
||||
if input.Select(c, value, []input.SelectOption[data.DataStatus]{{Value: data.DataReady, Label: "Ready"}, {Value: data.DataLoading, Label: "Loading"}, {Value: data.DataFailed, Label: "Failed"}}, input.SelectOptions{Label: "Load status"}).Changed() {
|
||||
log(fmt.Sprintf("Status: %d", *value))
|
||||
}
|
||||
}
|
||||
@@ -168,14 +172,14 @@ func pathBarDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
s := ui.Local(c.Root(), "385", func() state { return state{path: "/court/chronicles/royal/archives/seals"} })
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.TextInput(c, &s.path, mujica.TextInputOptions{Label: "Path"}).Changed() {
|
||||
if input.TextInput(c, &s.path, input.TextInputOptions{Label: "Path"}).Changed() {
|
||||
log("Path changed")
|
||||
}
|
||||
if strings.Trim(s.path, "/") == "" {
|
||||
ui.Text(c, "Enter a path")
|
||||
return
|
||||
}
|
||||
if r := mujica.PathBar(c, s.path, mujica.PathBarOptions{Disabled: s.disabled}); r.Navigated != "" {
|
||||
if r := files.PathBar(c, s.path, files.PathBarOptions{Disabled: s.disabled}); r.Navigated != "" {
|
||||
s.path = r.Navigated
|
||||
log("Navigate: " + r.Navigated)
|
||||
}
|
||||
@@ -189,11 +193,11 @@ func renameInlineDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "386", func() state { return state{name: "seal.txt"} })
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
mediaCheck(c, log, &s.folder, "Folder")
|
||||
if mujica.Button(c, "Rename", mujica.ButtonOptions{Disabled: s.disabled}).Clicked() {
|
||||
if input.Button(c, "Rename", input.ButtonOptions{Disabled: s.disabled}).Clicked() {
|
||||
s.editing = true
|
||||
log("Rename started")
|
||||
}
|
||||
r := mujica.RenameInline(c, &s.name, &s.editing, mujica.RenameInlineOptions{Disabled: s.disabled, Folder: s.folder, Validate: func(name string) string {
|
||||
r := files.RenameInline(c, &s.name, &s.editing, files.RenameInlineOptions{Disabled: s.disabled, Folder: s.folder, Validate: func(name string) string {
|
||||
if strings.ContainsAny(name, "/\\") {
|
||||
return "Name contains a path separator"
|
||||
}
|
||||
@@ -209,23 +213,23 @@ func renameInlineDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func fileOperationProgressDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
op mujica.FileOperation
|
||||
op files.FileOperation
|
||||
disabled, conflict bool
|
||||
canceled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "387", func() state {
|
||||
return state{op: mujica.FileOperation{Kind: mujica.FileCopy, Total: 10, Current: "seal.go"}}
|
||||
return state{op: files.FileOperation{Kind: files.FileCopy, Total: 10, Current: "seal.go"}}
|
||||
})
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
mediaCheck(c, log, &s.conflict, "Conflict")
|
||||
if mujica.Select(c, &s.op.Kind, []mujica.SelectOption[mujica.FileOperationKind]{{Value: mujica.FileCopy, Label: "Copy"}, {Value: mujica.FileMove, Label: "Move"}, {Value: mujica.FileDelete, Label: "Delete"}}, mujica.SelectOptions{Label: "Operation"}).Changed() {
|
||||
if input.Select(c, &s.op.Kind, []input.SelectOption[files.FileOperationKind]{{Value: files.FileCopy, Label: "Copy"}, {Value: files.FileMove, Label: "Move"}, {Value: files.FileDelete, Label: "Delete"}}, input.SelectOptions{Label: "Operation"}).Changed() {
|
||||
log("Operation changed")
|
||||
}
|
||||
if mujica.Button(c, "Advance", mujica.ButtonOptions{Disabled: s.op.Done == s.op.Total}).Clicked() {
|
||||
if input.Button(c, "Advance", input.ButtonOptions{Disabled: s.op.Done == s.op.Total}).Clicked() {
|
||||
s.op.Done++
|
||||
log(fmt.Sprintf("Done: %d", s.op.Done))
|
||||
}
|
||||
if mujica.Button(c, "Reset", mujica.ButtonOptions{}).Clicked() {
|
||||
if input.Button(c, "Reset", input.ButtonOptions{}).Clicked() {
|
||||
s.op.Done = 0
|
||||
s.canceled = false
|
||||
log("Operation reset")
|
||||
@@ -234,51 +238,51 @@ func fileOperationProgressDemo(c *ui.Context, log func(string)) {
|
||||
if s.conflict {
|
||||
op.Conflict = "seal.go"
|
||||
}
|
||||
r := mujica.FileOperationProgress(c, op, mujica.FileOperationOptions{Disabled: s.disabled || s.canceled})
|
||||
r := files.FileOperationProgress(c, op, files.FileOperationOptions{Disabled: s.disabled || s.canceled})
|
||||
if r.Canceled {
|
||||
s.canceled = true
|
||||
log("Operation canceled")
|
||||
}
|
||||
if r.Resolved != mujica.ConflictNone {
|
||||
if r.Resolved != files.ConflictNone {
|
||||
s.conflict = false
|
||||
log(fmt.Sprintf("Conflict: %d; apply all: %v", r.Resolved, r.ApplyAll))
|
||||
}
|
||||
}
|
||||
|
||||
func filesTransfers() []mujica.QueuedTransfer {
|
||||
return []mujica.QueuedTransfer{
|
||||
{ID: "a", Name: "ledger.zip", Upload: true, Status: mujica.TransferActive, Done: 30, Total: 120},
|
||||
{ID: "b", Name: "crest.png", Status: mujica.TransferPaused, Done: 1, Total: 4},
|
||||
{ID: "c", Name: "deed.pdf", Upload: true, Status: mujica.TransferFailed, Total: 2, Error: "Network lost"},
|
||||
func filesTransfers() []files.QueuedTransfer {
|
||||
return []files.QueuedTransfer{
|
||||
{ID: "a", Name: "ledger.zip", Upload: true, Status: files.TransferActive, Done: 30, Total: 120},
|
||||
{ID: "b", Name: "crest.png", Status: files.TransferPaused, Done: 1, Total: 4},
|
||||
{ID: "c", Name: "deed.pdf", Upload: true, Status: files.TransferFailed, Total: 2, Error: "Network lost"},
|
||||
}
|
||||
}
|
||||
|
||||
func transferQueueDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
items []mujica.QueuedTransfer
|
||||
queue mujica.TransferQueueState
|
||||
items []files.QueuedTransfer
|
||||
queue files.TransferQueueState
|
||||
disabled bool
|
||||
revision uint64
|
||||
}
|
||||
s := ui.Local(c.Root(), "388", func() state { return state{items: filesTransfers()} })
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.Button(c, "Reset", mujica.ButtonOptions{}).Clicked() {
|
||||
if input.Button(c, "Reset", input.ButtonOptions{}).Clicked() {
|
||||
s.items = filesTransfers()
|
||||
log("Queue reset")
|
||||
}
|
||||
r := mujica.TransferQueue(c, s.items, &s.queue, mujica.TransferQueueOptions{Disabled: s.disabled, Revision: s.revision})
|
||||
r := files.TransferQueue(c, s.items, &s.queue, files.TransferQueueOptions{Disabled: s.disabled, Revision: s.revision})
|
||||
r.Element.Height(260).FillWidth()
|
||||
if r.Action == mujica.TransferNone {
|
||||
if r.Action == files.TransferNone {
|
||||
return
|
||||
}
|
||||
for i := range s.items {
|
||||
if s.items[i].ID == r.ID {
|
||||
switch r.Action {
|
||||
case mujica.TransferPause:
|
||||
s.items[i].Status = mujica.TransferPaused
|
||||
case mujica.TransferResume, mujica.TransferRetry:
|
||||
s.items[i].Status = mujica.TransferActive
|
||||
case mujica.TransferRemove:
|
||||
case files.TransferPause:
|
||||
s.items[i].Status = files.TransferPaused
|
||||
case files.TransferResume, files.TransferRetry:
|
||||
s.items[i].Status = files.TransferActive
|
||||
case files.TransferRemove:
|
||||
s.items = append(s.items[:i:i], s.items[i+1:]...)
|
||||
}
|
||||
break
|
||||
@@ -291,30 +295,30 @@ func transferQueueDemo(c *ui.Context, log func(string)) {
|
||||
func storageUsageDemo(c *ui.Context, log func(string)) {
|
||||
type state struct{ photos float64 }
|
||||
s := ui.Local(c.Root(), "389", func() state { return state{photos: 40} })
|
||||
if mujica.NumberInput(c, &s.photos, mujica.NumberInputOptions{Min: 0, Max: 92, Step: 1, Label: "Photos (GB)"}).Changed() {
|
||||
if input.NumberInput(c, &s.photos, input.NumberInputOptions{Min: 0, Max: 92, Step: 1, Label: "Photos (GB)"}).Changed() {
|
||||
log(fmt.Sprintf("Photos: %.0f GB", s.photos))
|
||||
}
|
||||
mujica.StorageUsage(c, []mujica.StorageSegment{{Label: "Documents", Bytes: 24 << 30}, {Label: "Photos", Bytes: int64(s.photos) * (1 << 30)}, {Label: "Apps", Bytes: 12 << 30}}, mujica.StorageUsageOptions{Capacity: 128 << 30, Label: "Court disk"}).FillWidth()
|
||||
files.StorageUsage(c, []files.StorageSegment{{Label: "Documents", Bytes: 24 << 30}, {Label: "Photos", Bytes: int64(s.photos) * (1 << 30)}, {Label: "Apps", Bytes: 12 << 30}}, files.StorageUsageOptions{Capacity: 128 << 30, Label: "Court disk"}).FillWidth()
|
||||
}
|
||||
|
||||
func filesRecent() []mujica.RecentFile {
|
||||
func filesRecent() []files.RecentFile {
|
||||
now := time.Date(2026, 10, 5, 15, 0, 0, 0, time.UTC)
|
||||
return []mujica.RecentFile{{Path: "/court/seal.go", Opened: now}, {Path: "/court/notes.md", Opened: now.AddDate(0, 0, -1)}, {Path: "/court/crest.png", Opened: now.AddDate(0, 0, -4)}, {Path: "/court/charter.pdf", Opened: now.AddDate(0, -1, 0)}}
|
||||
return []files.RecentFile{{Path: "/court/seal.go", Opened: now}, {Path: "/court/notes.md", Opened: now.AddDate(0, 0, -1)}, {Path: "/court/crest.png", Opened: now.AddDate(0, 0, -4)}, {Path: "/court/charter.pdf", Opened: now.AddDate(0, -1, 0)}}
|
||||
}
|
||||
|
||||
func recentFilesDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
files []mujica.RecentFile
|
||||
recent mujica.RecentFilesState
|
||||
files []files.RecentFile
|
||||
recent files.RecentFilesState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "390", func() state { return state{files: filesRecent()} })
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.Button(c, "Reset", mujica.ButtonOptions{}).Clicked() {
|
||||
if input.Button(c, "Reset", input.ButtonOptions{}).Clicked() {
|
||||
s.files = filesRecent()
|
||||
log("Recent files reset")
|
||||
}
|
||||
r := mujica.RecentFiles(c, s.files, &s.recent, mujica.RecentFilesOptions{Disabled: s.disabled, Now: time.Date(2026, 10, 5, 15, 0, 0, 0, time.UTC)})
|
||||
r := files.RecentFiles(c, s.files, &s.recent, files.RecentFilesOptions{Disabled: s.disabled, Now: time.Date(2026, 10, 5, 15, 0, 0, 0, time.UTC)})
|
||||
r.Element.Height(320).FillWidth()
|
||||
if r.Opened != nil {
|
||||
log("Open: " + r.Opened.Path)
|
||||
@@ -336,13 +340,13 @@ func recentFilesDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func archiveViewerDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
archive mujica.ArchiveViewerState
|
||||
entries []mujica.ArchiveEntry
|
||||
status mujica.DataStatus
|
||||
archive files.ArchiveViewerState
|
||||
entries []files.ArchiveEntry
|
||||
status data.DataStatus
|
||||
empty bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "391", func() state {
|
||||
return state{entries: []mujica.ArchiveEntry{{Path: "court/seal.go", Size: 4000, Packed: 1000}, {Path: "court/decrees/42.pdf", Size: 6000, Packed: 5000}, {Path: "README.md", Size: 1000, Packed: 400}}}
|
||||
return state{entries: []files.ArchiveEntry{{Path: "court/seal.go", Size: 4000, Packed: 1000}, {Path: "court/decrees/42.pdf", Size: 6000, Packed: 5000}, {Path: "README.md", Size: 1000, Packed: 400}}}
|
||||
})
|
||||
filesStatus(c, log, &s.status)
|
||||
mediaCheck(c, log, &s.empty, "Empty")
|
||||
@@ -350,13 +354,13 @@ func archiveViewerDemo(c *ui.Context, log func(string)) {
|
||||
if s.empty {
|
||||
entries = nil
|
||||
}
|
||||
r := mujica.ArchiveViewer(c, entries, &s.archive, mujica.ArchiveViewerOptions{Status: s.status, Error: "Invalid archive"})
|
||||
r := files.ArchiveViewer(c, entries, &s.archive, files.ArchiveViewerOptions{Status: s.status, Error: "Invalid archive"})
|
||||
r.Element.Height(280).FillWidth()
|
||||
if r.Opened != nil {
|
||||
log("Open archive entry: " + r.Opened.Path)
|
||||
}
|
||||
if r.Retried {
|
||||
s.status = mujica.DataReady
|
||||
s.status = data.DataReady
|
||||
log("Archive read retried")
|
||||
}
|
||||
}
|
||||
@@ -1,135 +1,135 @@
|
||||
package main
|
||||
|
||||
const filesFileExplorerCode = `var state mujica.FileExplorerState
|
||||
opts := mujica.FileExplorerOptions{
|
||||
const filesFileExplorerCode = `var state files.FileExplorerState
|
||||
opts := files.FileExplorerOptions{
|
||||
Roots: []string{"/court"},
|
||||
Folders: func(string) []string { return nil },
|
||||
Entries: func(string) []mujica.FileEntry {
|
||||
return []mujica.FileEntry{{Path: "/court/seal.go", Name: "seal.go", Size: 1024}}
|
||||
Entries: func(string) []files.FileEntry {
|
||||
return []files.FileEntry{{Path: "/court/seal.go", Name: "seal.go", Size: 1024}}
|
||||
},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.FileExplorer(c, &state, opts).Element.Height(240)
|
||||
core.Use(c, core.Settings{})
|
||||
files.FileExplorer(c, &state, opts).Element.Height(240)
|
||||
}
|
||||
ui.Render(view, 600, 300, 1)`
|
||||
|
||||
const filesFileGridCode = `entries := []mujica.FileEntry{{Path: "/court/seal.go", Name: "seal.go"}}
|
||||
var state mujica.FileGridState
|
||||
const filesFileGridCode = `entries := []files.FileEntry{{Path: "/court/seal.go", Name: "seal.go"}}
|
||||
var state files.FileGridState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.FileGrid(c, entries, &state, mujica.FileGridOptions{}).Element.Height(240)
|
||||
core.Use(c, core.Settings{})
|
||||
files.FileGrid(c, entries, &state, files.FileGridOptions{}).Element.Height(240)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const filesFileIconCode = `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.FileIcon(c, "seal.go", mujica.FileIconOptions{Size: 32})
|
||||
core.Use(c, core.Settings{})
|
||||
files.FileIcon(c, "seal.go", files.FileIconOptions{Size: 32})
|
||||
}
|
||||
ui.Render(view, 100, 80, 1)`
|
||||
|
||||
const filesFilePreviewCode = `file := mujica.PreviewFile{Name: "seal.go", Text: "package court\nfunc Seal() {}"}
|
||||
const filesFilePreviewCode = `file := files.PreviewFile{Name: "seal.go", Text: "package court\nfunc Seal() {}"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.FilePreview(c, file, mujica.FilePreviewOptions{}).Element.Height(240)
|
||||
core.Use(c, core.Settings{})
|
||||
files.FilePreview(c, file, files.FilePreviewOptions{}).Element.Height(240)
|
||||
}
|
||||
ui.Render(view, 500, 300, 1)`
|
||||
|
||||
const filesPathBarCode = `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.PathBar(c, "/court/chronicles/seals", mujica.PathBarOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
files.PathBar(c, "/court/chronicles/seals", files.PathBarOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 100, 1)`
|
||||
|
||||
const filesRenameInlineCode = `name, editing := "seal.txt", true
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.RenameInline(c, &name, &editing, mujica.RenameInlineOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
files.RenameInline(c, &name, &editing, files.RenameInlineOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 100, 1)`
|
||||
|
||||
const filesFileOperationProgressCode = `op := mujica.FileOperation{Kind: mujica.FileCopy, Done: 2, Total: 10, Current: "seal.go"}
|
||||
const filesFileOperationProgressCode = `op := files.FileOperation{Kind: files.FileCopy, Done: 2, Total: 10, Current: "seal.go"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.FileOperationProgress(c, op, mujica.FileOperationOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
files.FileOperationProgress(c, op, files.FileOperationOptions{})
|
||||
}
|
||||
ui.Render(view, 500, 240, 1)`
|
||||
|
||||
const filesTransferQueueCode = `items := []mujica.QueuedTransfer{{ID: "a", Name: "ledger.zip", Upload: true, Status: mujica.TransferActive, Done: 25, Total: 100}}
|
||||
var state mujica.TransferQueueState
|
||||
const filesTransferQueueCode = `items := []files.QueuedTransfer{{ID: "a", Name: "ledger.zip", Upload: true, Status: files.TransferActive, Done: 25, Total: 100}}
|
||||
var state files.TransferQueueState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.TransferQueue(c, items, &state, mujica.TransferQueueOptions{}).Element.Height(240)
|
||||
core.Use(c, core.Settings{})
|
||||
files.TransferQueue(c, items, &state, files.TransferQueueOptions{}).Element.Height(240)
|
||||
}
|
||||
ui.Render(view, 500, 300, 1)`
|
||||
|
||||
const filesStorageUsageCode = `segments := []mujica.StorageSegment{{Label: "Documents", Bytes: 24 << 30}, {Label: "Photos", Bytes: 40 << 30}}
|
||||
const filesStorageUsageCode = `segments := []files.StorageSegment{{Label: "Documents", Bytes: 24 << 30}, {Label: "Photos", Bytes: 40 << 30}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.StorageUsage(c, segments, mujica.StorageUsageOptions{Capacity: 128 << 30, Label: "Court disk"})
|
||||
core.Use(c, core.Settings{})
|
||||
files.StorageUsage(c, segments, files.StorageUsageOptions{Capacity: 128 << 30, Label: "Court disk"})
|
||||
}
|
||||
ui.Render(view, 500, 240, 1)`
|
||||
|
||||
const filesRecentFilesCode = `now := time.Date(2026, 10, 5, 12, 0, 0, 0, time.UTC)
|
||||
files := []mujica.RecentFile{{Path: "/court/seal.go", Opened: now}}
|
||||
var state mujica.RecentFilesState
|
||||
recent := []files.RecentFile{{Path: "/court/seal.go", Opened: now}}
|
||||
var state files.RecentFilesState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.RecentFiles(c, files, &state, mujica.RecentFilesOptions{Now: now}).Element.Height(240)
|
||||
core.Use(c, core.Settings{})
|
||||
files.RecentFiles(c, recent, &state, files.RecentFilesOptions{Now: now}).Element.Height(240)
|
||||
}
|
||||
ui.Render(view, 500, 300, 1)`
|
||||
|
||||
const filesArchiveViewerCode = `entries := []mujica.ArchiveEntry{{Path: "court/seal.go", Size: 4000, Packed: 1000}}
|
||||
var state mujica.ArchiveViewerState
|
||||
const filesArchiveViewerCode = `entries := []files.ArchiveEntry{{Path: "court/seal.go", Size: 4000, Packed: 1000}}
|
||||
var state files.ArchiveViewerState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ArchiveViewer(c, entries, &state, mujica.ArchiveViewerOptions{}).Element.Height(240)
|
||||
core.Use(c, core.Settings{})
|
||||
files.ArchiveViewer(c, entries, &state, files.ArchiveViewerOptions{}).Element.Height(240)
|
||||
}
|
||||
ui.Render(view, 600, 300, 1)`
|
||||
|
||||
const filesPresenceAvatarsCode = `people := []mujica.Collaborator{{ID: "ada", Name: "Ada Lovelace"}}
|
||||
const filesPresenceAvatarsCode = `people := []files.Collaborator{{ID: "ada", Name: "Ada Lovelace"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.PresenceAvatars(c, people, mujica.PresenceAvatarsOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
files.PresenceAvatars(c, people, files.PresenceAvatarsOptions{})
|
||||
}
|
||||
ui.Render(view, 300, 100, 1)`
|
||||
|
||||
const filesReactionsCode = `items := []mujica.Reaction{{Emoji: "\U0001f44d", Count: 3, Mine: true}}
|
||||
const filesReactionsCode = `items := []files.Reaction{{Emoji: "\U0001f44d", Count: 3, Mine: true}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Reactions(c, items, mujica.ReactionsOptions{Choices: []string{"\U0001f44d", "\U0001f389"}})
|
||||
core.Use(c, core.Settings{})
|
||||
files.Reactions(c, items, files.ReactionsOptions{Choices: []string{"\U0001f44d", "\U0001f389"}})
|
||||
}
|
||||
ui.Render(view, 400, 100, 1)`
|
||||
|
||||
const filesShareDialogCode = `open := false
|
||||
members := []mujica.ShareMember{{ID: "ada", Name: "Ada", Permission: mujica.PermissionEdit, Owner: true}}
|
||||
members := []files.ShareMember{{ID: "ada", Name: "Ada", Permission: files.PermissionEdit, Owner: true}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.Button(c, "Share", mujica.ButtonOptions{}).Clicked() {
|
||||
core.Use(c, core.Settings{})
|
||||
if input.Button(c, "Share", input.ButtonOptions{}).Clicked() {
|
||||
open = true
|
||||
}
|
||||
mujica.ShareDialog(c, &open, &members, mujica.ShareDialogOptions{Title: "Ledger", Link: "https://court.org/ledger"})
|
||||
files.ShareDialog(c, &open, &members, files.ShareDialogOptions{Title: "Ledger", Link: "https://court.org/ledger"})
|
||||
}
|
||||
ui.Render(view, 600, 400, 1)`
|
||||
|
||||
const filesPermissionSelectCode = `value := mujica.PermissionView
|
||||
const filesPermissionSelectCode = `value := files.PermissionView
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.PermissionSelect(c, &value, mujica.PermissionSelectOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
files.PermissionSelect(c, &value, files.PermissionSelectOptions{})
|
||||
}
|
||||
ui.Render(view, 300, 100, 1)`
|
||||
|
||||
const filesLiveIndicatorCode = `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.LiveIndicator(c, mujica.LiveOffline, mujica.LiveIndicatorOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
files.LiveIndicator(c, files.LiveOffline, files.LiveIndicatorOptions{})
|
||||
}
|
||||
ui.Render(view, 500, 100, 1)`
|
||||
|
||||
const filesRemoteCursorCode = `carets := []mujica.RemoteCaret{{ID: "ada", Name: "Ada", Offset: 4, Anchor: 4}}
|
||||
const filesRemoteCursorCode = `carets := []files.RemoteCaret{{ID: "ada", Name: "Ada", Offset: 4, Anchor: 4}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
core.Use(c, core.Settings{})
|
||||
ui.Box(c).Size(300, 100).Children(func() {
|
||||
mujica.RemoteCursor(c, carets, mujica.RemoteCursorOptions{
|
||||
files.RemoteCursor(c, carets, files.RemoteCursorOptions{
|
||||
Caret: func(offset int) ui.Rect { return ui.Rect{X: float32(offset) * 8, Y: 20, H: 20} },
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,11 +5,14 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
"mujicaui/theme"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/files"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||
)
|
||||
|
||||
var filesPeople = []mujica.Collaborator{
|
||||
var filesPeople = []files.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"},
|
||||
}
|
||||
@@ -23,14 +26,14 @@ func presenceAvatarsDemo(c *ui.Context, log func(string)) {
|
||||
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() {
|
||||
if input.NumberInput(c, &s.max, input.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})
|
||||
r := files.PresenceAvatars(c, people, files.PresenceAvatarsOptions{Following: s.following, Max: int(s.max), Disabled: s.disabled})
|
||||
if r.Follow != "" {
|
||||
s.following = r.Follow
|
||||
log("Follow: " + r.Follow)
|
||||
@@ -45,18 +48,18 @@ var filesReactionChoices = []string{"\U0001f44d", "\u2764\ufe0f", "\U0001f389",
|
||||
|
||||
func reactionsDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
items []mujica.Reaction
|
||||
items []files.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}}}
|
||||
return state{items: []files.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() {
|
||||
if input.Button(c, "Clear reactions", input.ButtonOptions{}).Clicked() {
|
||||
s.items = nil
|
||||
log("Reactions cleared")
|
||||
}
|
||||
r := mujica.Reactions(c, s.items, mujica.ReactionsOptions{Choices: filesReactionChoices, Disabled: s.disabled})
|
||||
r := files.Reactions(c, s.items, files.ReactionsOptions{Choices: filesReactionChoices, Disabled: s.disabled})
|
||||
emoji := r.Toggled
|
||||
if emoji == "" {
|
||||
emoji = r.Added
|
||||
@@ -80,32 +83,32 @@ func reactionsDemo(c *ui.Context, log func(string)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
s.items = append(s.items, mujica.Reaction{Emoji: emoji, Count: 1, Mine: true})
|
||||
s.items = append(s.items, files.Reaction{Emoji: emoji, Count: 1, Mine: true})
|
||||
log("Reaction added")
|
||||
}
|
||||
|
||||
func shareDialogDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
members []mujica.ShareMember
|
||||
members []files.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},
|
||||
return state{members: []files.ShareMember{
|
||||
{ID: "owner", Name: "Ada Lovelace", Email: "ada@court.org", Owner: true, Permission: files.PermissionEdit},
|
||||
{ID: "b", Name: "Blaise Pascal", Email: "blaise@court.org", Permission: files.PermissionView},
|
||||
}}
|
||||
})
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.Button(c, "Share Ledger", mujica.ButtonOptions{Disabled: s.disabled}).Clicked() {
|
||||
if input.Button(c, "Share Ledger", input.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") }})
|
||||
r := files.ShareDialog(c, &s.open, &s.members, files.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})
|
||||
s.members = append(s.members, files.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 != "" {
|
||||
@@ -124,33 +127,33 @@ func shareDialogDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func permissionSelectDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
value mujica.Permission
|
||||
value files.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() {
|
||||
if files.PermissionSelect(c, &s.value, files.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
|
||||
status files.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() {
|
||||
if input.Select(c, &s.status, []input.SelectOption[files.LiveStatus]{{Value: files.LiveSynced, Label: "Synced"}, {Value: files.LiveSyncing, Label: "Syncing"}, {Value: files.LiveReconnecting, Label: "Reconnecting"}, {Value: files.LiveOffline, Label: "Offline"}, {Value: files.LiveError, Label: "Error"}}, input.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}
|
||||
opts := files.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
|
||||
if files.LiveIndicator(c, s.status, opts).Changed() {
|
||||
s.status = files.LiveReconnecting
|
||||
log("Sync retried")
|
||||
}
|
||||
}
|
||||
@@ -161,26 +164,26 @@ func remoteCursorDemo(c *ui.Context, log func(string)) {
|
||||
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() {
|
||||
if input.NumberInput(c, &s.offset, input.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)}}
|
||||
carets := []files.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})
|
||||
carets = append(carets, files.RemoteCaret{ID: "blaise", Name: "Blaise", Offset: 8, Anchor: 8})
|
||||
}
|
||||
ui.Box(c).Height(140).FillWidth().Background(mujica.Tokens(c).Surface).Children(func() {
|
||||
ui.Box(c).Height(140).FillWidth().Background(core.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)
|
||||
ui.Text(c, line).Font(theme.MonoFont).FontSize(core.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{
|
||||
files.RemoteCursor(c, carets, files.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
|
||||
|
||||
@@ -6,16 +6,17 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
)
|
||||
|
||||
func filesDemoTester(t *testing.T, id string, mode mujica.Mode) (*ui.Tester, *[]string) {
|
||||
func filesDemoTester(t *testing.T, id string, mode core.Mode) (*ui.Tester, *[]string) {
|
||||
t.Helper()
|
||||
for _, p := range filesPages {
|
||||
if p.ID == id {
|
||||
events := &[]string{}
|
||||
tt := ui.NewTester(func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{Mode: mode})
|
||||
core.Use(c, core.Settings{Mode: mode})
|
||||
ui.Scroll(c).Fill().Padding(24).Gap(12).Children(func() { p.Demo(c, func(s string) { *events = append(*events, s) }) })
|
||||
}, 900, 800)
|
||||
tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1})
|
||||
@@ -59,7 +60,7 @@ func TestFilesGalleryActions(t *testing.T) {
|
||||
{"400", "Selection", "Selection = false"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
for _, mode := range []mujica.Mode{mujica.Light, mujica.Dark} {
|
||||
for _, mode := range []core.Mode{core.Light, core.Dark} {
|
||||
t.Run(fmt.Sprintf("%s-%s", tc.id, mode), func(t *testing.T) {
|
||||
tt, events := filesDemoTester(t, tc.id, mode)
|
||||
if err := tt.Click(tc.action); err != nil {
|
||||
@@ -88,7 +89,7 @@ func TestFilesGalleryActions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFilesGalleryStorageParameter(t *testing.T) {
|
||||
tt, events := filesDemoTester(t, "389", mujica.Light)
|
||||
tt, events := filesDemoTester(t, "389", core.Light)
|
||||
if err := tt.Click("Photos (GB)"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -101,7 +102,7 @@ func TestFilesGalleryStorageParameter(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFilesGalleryGridRange(t *testing.T) {
|
||||
tt, events := filesDemoTester(t, "382", mujica.Light)
|
||||
tt, events := filesDemoTester(t, "382", core.Light)
|
||||
if err := tt.Click("notes.md"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"github.com/egoist/mygo"
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
// formStates shows a component's states side by side.
|
||||
@@ -30,7 +30,7 @@ func formToggles(c *ui.Context, names []string, values ...*bool) {
|
||||
}
|
||||
|
||||
type formDemo struct {
|
||||
st mujica.FormState
|
||||
st input.FormState
|
||||
name, email string
|
||||
tags []string
|
||||
horizontal, slowServer bool
|
||||
@@ -75,7 +75,7 @@ type pickerDemo struct {
|
||||
}
|
||||
|
||||
type dropDemo struct {
|
||||
files []mujica.DroppedFile
|
||||
files []input.DroppedFile
|
||||
images bool
|
||||
disabled bool
|
||||
}
|
||||
@@ -85,7 +85,7 @@ type transferDemo struct {
|
||||
disabled bool
|
||||
}
|
||||
|
||||
var transferDemoItems = []mujica.TransferItem{
|
||||
var transferDemoItems = []input.TransferItem{
|
||||
{ID: "sakiko", Label: "Sakiko Togawa"}, {ID: "uika", Label: "Uika Misumi"}, {ID: "mutsumi", Label: "Mutsumi Wakaba"},
|
||||
{ID: "umiri", Label: "Umiri Yahata"}, {ID: "nyamu", Label: "Nyamu Yutenji"}, {ID: "tomori", Label: "Tomori Takamatsu", Disabled: true},
|
||||
{ID: "anon", Label: "Anon Chihaya"}, {ID: "rana", Label: "Rana Kaname"}, {ID: "soyo", Label: "Soyo Nagasaki"},
|
||||
@@ -117,8 +117,8 @@ var formPages = []page{
|
||||
}
|
||||
}
|
||||
formToggles(c, []string{"Labels beside fields", "Server rejects the submit"}, &d.horizontal, &d.slowServer)
|
||||
res := mujica.Form(c, &d.st, mujica.FormOptions{Horizontal: d.horizontal}, func(f *mujica.FormScope) {
|
||||
mujica.FormItem(c, f, "name", &d.name, mujica.FormItemOptions[string]{Label: "Stage name", Required: true, Initial: "Sakiko",
|
||||
res := input.Form(c, &d.st, input.FormOptions{Horizontal: d.horizontal}, func(f *input.FormScope) {
|
||||
input.FormItem(c, f, "name", &d.name, input.FormItemOptions[string]{Label: "Stage name", Required: true, Initial: "Sakiko",
|
||||
Description: "Checked against the archive; \"Oblivionis\" is taken.",
|
||||
Async: func(v string, version uint64) {
|
||||
log(fmt.Sprintf("async check started v%d", version))
|
||||
@@ -132,23 +132,23 @@ var formPages = []page{
|
||||
c.Invalidate()
|
||||
}()
|
||||
}}, func() *ui.Element {
|
||||
return mujica.InputGroup(c, &d.name, mujica.InputGroupOptions{Invalid: d.st.Error("name") != ""}).Input
|
||||
return input.InputGroup(c, &d.name, input.InputGroupOptions{Invalid: d.st.Error("name") != ""}).Input
|
||||
})
|
||||
mujica.FormItem(c, f, "email", &d.email, mujica.FormItemOptions[string]{Label: "Email", Required: true,
|
||||
input.FormItem(c, f, "email", &d.email, input.FormItemOptions[string]{Label: "Email", Required: true,
|
||||
Validate: func(v string) string {
|
||||
if !strings.Contains(v, "@") {
|
||||
return "Enter an email address."
|
||||
}
|
||||
return ""
|
||||
}}, func() *ui.Element {
|
||||
g := mujica.InputGroup(c, &d.email, mujica.InputGroupOptions{Invalid: d.st.Error("email") != ""})
|
||||
g := input.InputGroup(c, &d.email, input.InputGroupOptions{Invalid: d.st.Error("email") != ""})
|
||||
if g.Submitted() {
|
||||
d.st.Submit()
|
||||
}
|
||||
return g.Input
|
||||
})
|
||||
mujica.FormItem(c, f, "tags", &d.tags, mujica.FormItemOptions[[]string]{Label: "Roles", Required: true, Initial: []string{"keyboard"}}, func() *ui.Element {
|
||||
return mujica.TagsInput(c, &d.tags, mujica.TagsInputOptions{Max: 4, Invalid: d.st.Error("tags") != ""}).Input
|
||||
input.FormItem(c, f, "tags", &d.tags, input.FormItemOptions[[]string]{Label: "Roles", Required: true, Initial: []string{"keyboard"}}, func() *ui.Element {
|
||||
return input.TagsInput(c, &d.tags, input.TagsInputOptions{Max: 4, Invalid: d.st.Error("tags") != ""}).Input
|
||||
})
|
||||
})
|
||||
if res.Submitted() {
|
||||
@@ -176,9 +176,9 @@ var formPages = []page{
|
||||
if d.withError {
|
||||
e = "This name is already in the guest list."
|
||||
}
|
||||
mujica.FormField(c, "Guest name", mujica.FormFieldOptions{Required: d.required, Horizontal: d.horizontal,
|
||||
input.FormField(c, "Guest name", input.FormFieldOptions{Required: d.required, Horizontal: d.horizontal,
|
||||
Description: "As written on the invitation.", Error: e}, func() *ui.Element {
|
||||
g := mujica.InputGroup(c, &d.value, mujica.InputGroupOptions{Invalid: d.withError})
|
||||
g := input.InputGroup(c, &d.value, input.InputGroupOptions{Invalid: d.withError})
|
||||
if g.Changed() {
|
||||
log(fmt.Sprintf("value %q", d.value))
|
||||
}
|
||||
@@ -190,9 +190,9 @@ var formPages = []page{
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "043", func() groupDemo { return groupDemo{value: "mujica"} })
|
||||
formToggles(c, []string{"Disabled", "Read-only", "Invalid"}, &d.disabled, &d.readOnly, &d.invalid)
|
||||
opts := mujica.InputGroupOptions{Prefix: "https://", Suffix: ".org", Label: "Site", Disabled: d.disabled, ReadOnly: d.readOnly, Invalid: d.invalid,
|
||||
Buttons: []mujica.InputGroupButton{{Label: "Clear", Icon: icons.Must("x"), IconOnly: true}, {Label: "Copy", Icon: icons.Must("copy")}}}
|
||||
g := mujica.InputGroup(c, &d.value, opts)
|
||||
opts := input.InputGroupOptions{Prefix: "https://", Suffix: ".org", Label: "Site", Disabled: d.disabled, ReadOnly: d.readOnly, Invalid: d.invalid,
|
||||
Buttons: []input.InputGroupButton{{Label: "Clear", Icon: icons.Must("x"), IconOnly: true}, {Label: "Copy", Icon: icons.Must("copy")}}}
|
||||
g := input.InputGroup(c, &d.value, opts)
|
||||
switch g.Clicked {
|
||||
case 0:
|
||||
d.value = ""
|
||||
@@ -208,9 +208,9 @@ var formPages = []page{
|
||||
log("submitted")
|
||||
}
|
||||
formStates(c, func() {
|
||||
for _, o := range []mujica.InputGroupOptions{{Label: "Disabled", Disabled: true}, {Label: "Read-only", ReadOnly: true}, {Label: "Invalid", Invalid: true}} {
|
||||
for _, o := range []input.InputGroupOptions{{Label: "Disabled", Disabled: true}, {Label: "Read-only", ReadOnly: true}, {Label: "Invalid", Invalid: true}} {
|
||||
v := o.Label
|
||||
mujica.InputGroup(c, &v, o)
|
||||
input.InputGroup(c, &v, o)
|
||||
}
|
||||
})
|
||||
}},
|
||||
@@ -226,7 +226,7 @@ var formPages = []page{
|
||||
if len(d.code) > n {
|
||||
d.code = d.code[:n]
|
||||
}
|
||||
p := mujica.PinInput(c, &d.code, mujica.PinInputOptions{Length: n, Disabled: d.disabled, Invalid: d.invalid})
|
||||
p := input.PinInput(c, &d.code, input.PinInputOptions{Length: n, Disabled: d.disabled, Invalid: d.invalid})
|
||||
if p.Changed() {
|
||||
log(fmt.Sprintf("code %q", d.code))
|
||||
}
|
||||
@@ -235,8 +235,8 @@ var formPages = []page{
|
||||
}
|
||||
formStates(c, func() {
|
||||
a, b := "12", "0420"
|
||||
mujica.PinInput(c, &a, mujica.PinInputOptions{Length: 4, Disabled: true, Label: "Disabled code"})
|
||||
mujica.PinInput(c, &b, mujica.PinInputOptions{Length: 4, Invalid: true, Label: "Invalid code"})
|
||||
input.PinInput(c, &a, input.PinInputOptions{Length: 4, Disabled: true, Label: "Disabled code"})
|
||||
input.PinInput(c, &b, input.PinInputOptions{Length: 4, Invalid: true, Label: "Invalid code"})
|
||||
})
|
||||
}},
|
||||
{ID: "045", Name: "TagsInput", ZhName: "标签输入", Category: "Forms", Code: formCode["TagsInput"],
|
||||
@@ -244,7 +244,7 @@ var formPages = []page{
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "045", func() tagsDemo { return tagsDemo{tags: []string{"rose", "thorn"}} })
|
||||
formToggles(c, []string{"At most 5", "Disabled"}, &d.limit, &d.disabled)
|
||||
opts := mujica.TagsInputOptions{Placeholder: "Add a flower", Label: "Flowers", Disabled: d.disabled,
|
||||
opts := input.TagsInputOptions{Placeholder: "Add a flower", Label: "Flowers", Disabled: d.disabled,
|
||||
Validate: func(s string) string {
|
||||
if len([]rune(s)) > 16 {
|
||||
return "Keep tags under 16 characters."
|
||||
@@ -254,7 +254,7 @@ var formPages = []page{
|
||||
if d.limit {
|
||||
opts.Max = 5
|
||||
}
|
||||
if mujica.TagsInput(c, &d.tags, opts).Changed() {
|
||||
if input.TagsInput(c, &d.tags, opts).Changed() {
|
||||
log(fmt.Sprintf("tags %q", d.tags))
|
||||
}
|
||||
}},
|
||||
@@ -270,13 +270,13 @@ var formPages = []page{
|
||||
if !d.half {
|
||||
d.value = float64(int(d.value + 0.5))
|
||||
}
|
||||
if mujica.Rating(c, &d.value, mujica.RatingOptions{Step: step, ReadOnly: d.readOnly, Disabled: d.disabled, ShowValue: true}).Changed() {
|
||||
if input.Rating(c, &d.value, input.RatingOptions{Step: step, ReadOnly: d.readOnly, Disabled: d.disabled, ShowValue: true}).Changed() {
|
||||
log(fmt.Sprintf("rating %v", d.value))
|
||||
}
|
||||
formStates(c, func() {
|
||||
a, b := 4.5, 2.0
|
||||
mujica.Rating(c, &a, mujica.RatingOptions{Step: 0.5, ReadOnly: true, Label: "Read-only rating"})
|
||||
mujica.Rating(c, &b, mujica.RatingOptions{Disabled: true, Label: "Disabled rating"})
|
||||
input.Rating(c, &a, input.RatingOptions{Step: 0.5, ReadOnly: true, Label: "Read-only rating"})
|
||||
input.Rating(c, &b, input.RatingOptions{Disabled: true, Label: "Disabled rating"})
|
||||
})
|
||||
}},
|
||||
{ID: "047", Name: "ColorPicker", ZhName: "颜色选择器", Category: "Forms", Code: formCode["ColorPicker"],
|
||||
@@ -284,7 +284,7 @@ var formPages = []page{
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "047", func() colorDemo { return colorDemo{color: ui.Hex("#7D2034")} })
|
||||
formToggles(c, []string{"Disabled"}, &d.disabled)
|
||||
if mujica.ColorPicker(c, &d.color, mujica.ColorPickerOptions{Label: "Banner color", Disabled: d.disabled}).Changed() {
|
||||
if input.ColorPicker(c, &d.color, input.ColorPickerOptions{Label: "Banner color", Disabled: d.disabled}).Changed() {
|
||||
log(fmt.Sprintf("color %v", d.color))
|
||||
}
|
||||
}},
|
||||
@@ -293,11 +293,11 @@ var formPages = []page{
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "048", func() pickerDemo { return pickerDemo{multiple: true} })
|
||||
formToggles(c, []string{"Folders", "Multiple", "PDF and MIDI only", "Disabled"}, &d.directory, &d.multiple, &d.filter, &d.disabled)
|
||||
opts := mujica.FilePickerOptions{Directory: d.directory, Multiple: d.multiple, Disabled: d.disabled, Title: "Choose scores"}
|
||||
opts := input.FilePickerOptions{Directory: d.directory, Multiple: d.multiple, Disabled: d.disabled, Title: "Choose scores"}
|
||||
if d.filter {
|
||||
opts.Filters = []mygo.FileFilter{{Name: "Scores", Extensions: []string{"pdf", "mid"}}}
|
||||
}
|
||||
p := mujica.FilePicker(c, &d.paths, opts)
|
||||
p := input.FilePicker(c, &d.paths, opts)
|
||||
if p.Changed() {
|
||||
log(fmt.Sprintf("picked %d: %q", len(d.paths), d.paths))
|
||||
}
|
||||
@@ -310,11 +310,11 @@ var formPages = []page{
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "049", func() dropDemo { return dropDemo{} })
|
||||
formToggles(c, []string{"Images only", "Disabled"}, &d.images, &d.disabled)
|
||||
opts := mujica.FileDropZoneOptions{MaxFiles: 4, MaxSize: 10 << 20, Disabled: d.disabled}
|
||||
opts := input.FileDropZoneOptions{MaxFiles: 4, MaxSize: 10 << 20, Disabled: d.disabled}
|
||||
if d.images {
|
||||
opts.Extensions = []string{"png", "jpg", "jpeg", "gif"}
|
||||
}
|
||||
if mujica.FileDropZone(c, &d.files, opts).Changed() {
|
||||
if input.FileDropZone(c, &d.files, opts).Changed() {
|
||||
log(fmt.Sprintf("files %d", len(d.files)))
|
||||
}
|
||||
}},
|
||||
@@ -323,7 +323,7 @@ var formPages = []page{
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
d := ui.Local(c.Root(), "050", func() transferDemo { return transferDemo{chosen: []string{"sakiko"}} })
|
||||
formToggles(c, []string{"Disabled"}, &d.disabled)
|
||||
if mujica.Transfer(c, &d.chosen, transferDemoItems, mujica.TransferOptions{SourceTitle: "Auditions", TargetTitle: "Band", Disabled: d.disabled}).Changed() {
|
||||
if input.Transfer(c, &d.chosen, transferDemoItems, input.TransferOptions{SourceTitle: "Auditions", TargetTitle: "Band", Disabled: d.disabled}).Changed() {
|
||||
log(fmt.Sprintf("band %q", d.chosen))
|
||||
}
|
||||
}},
|
||||
@@ -331,13 +331,13 @@ var formPages = []page{
|
||||
|
||||
// formCode holds each page's copyable snippet, the same as its doc and Example.
|
||||
var formCode = map[string]string{
|
||||
"Form": `var state mujica.FormState
|
||||
"Form": `var state input.FormState
|
||||
name := ""
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
res := mujica.Form(c, &state, mujica.FormOptions{}, func(f *mujica.FormScope) {
|
||||
mujica.FormItem(c, f, "name", &name, mujica.FormItemOptions[string]{Label: "Name", Required: true}, func() *ui.Element {
|
||||
return mujica.InputGroup(c, &name, mujica.InputGroupOptions{Invalid: state.Error("name") != ""}).Input
|
||||
core.Use(c, core.Settings{})
|
||||
res := input.Form(c, &state, input.FormOptions{}, func(f *input.FormScope) {
|
||||
input.FormItem(c, f, "name", &name, input.FormItemOptions[string]{Label: "Name", Required: true}, func() *ui.Element {
|
||||
return input.InputGroup(c, &name, input.InputGroupOptions{Invalid: state.Error("name") != ""}).Input
|
||||
})
|
||||
})
|
||||
if res.Submitted() {
|
||||
@@ -347,18 +347,18 @@ view := func(c *ui.Context) {
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"FormField": `email := ""
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.FormField(c, "Email", mujica.FormFieldOptions{Required: true, Description: "For receipts."}, func() *ui.Element {
|
||||
return mujica.InputGroup(c, &email, mujica.InputGroupOptions{}).Input
|
||||
core.Use(c, core.Settings{})
|
||||
input.FormField(c, "Email", input.FormFieldOptions{Required: true, Description: "For receipts."}, func() *ui.Element {
|
||||
return input.InputGroup(c, &email, input.InputGroupOptions{}).Input
|
||||
})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"InputGroup": `site := ""
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
g := mujica.InputGroup(c, &site, mujica.InputGroupOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
g := input.InputGroup(c, &site, input.InputGroupOptions{
|
||||
Prefix: "https://", Label: "Site",
|
||||
Buttons: []mujica.InputGroupButton{{Label: "Open"}},
|
||||
Buttons: []input.InputGroupButton{{Label: "Open"}},
|
||||
})
|
||||
if g.Clicked == 0 {
|
||||
c.OpenURL("https://" + site)
|
||||
@@ -367,52 +367,52 @@ view := func(c *ui.Context) {
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"PinInput": `code := ""
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.PinInput(c, &code, mujica.PinInputOptions{Length: 6}).Completed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if input.PinInput(c, &code, input.PinInputOptions{Length: 6}).Completed() {
|
||||
fmt.Println("verify", code)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"TagsInput": `tags := []string{"gothic"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.TagsInput(c, &tags, mujica.TagsInputOptions{Max: 5, Placeholder: "Add a tag", Label: "Tags"})
|
||||
core.Use(c, core.Settings{})
|
||||
input.TagsInput(c, &tags, input.TagsInputOptions{Max: 5, Placeholder: "Add a tag", Label: "Tags"})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"Rating": `stars := 3.5
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Rating(c, &stars, mujica.RatingOptions{Step: 0.5, ShowValue: true})
|
||||
core.Use(c, core.Settings{})
|
||||
input.Rating(c, &stars, input.RatingOptions{Step: 0.5, ShowValue: true})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"ColorPicker": `tint := ui.Hex("#7D2034")
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ColorPicker(c, &tint, mujica.ColorPickerOptions{Label: "Tint"})
|
||||
core.Use(c, core.Settings{})
|
||||
input.ColorPicker(c, &tint, input.ColorPickerOptions{Label: "Tint"})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"FilePicker": `var paths []string
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.FilePicker(c, &paths, mujica.FilePickerOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
input.FilePicker(c, &paths, input.FilePickerOptions{
|
||||
Multiple: true,
|
||||
Filters: []mygo.FileFilter{{Name: "Scores", Extensions: []string{"pdf", "mid"}}},
|
||||
})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"FileDropZone": `var files []mujica.DroppedFile
|
||||
"FileDropZone": `var files []input.DroppedFile
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.FileDropZone(c, &files, mujica.FileDropZoneOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
input.FileDropZone(c, &files, input.FileDropZoneOptions{
|
||||
Extensions: []string{"png", "jpg"}, MaxFiles: 3, MaxSize: 5 << 20,
|
||||
})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
"Transfer": `chosen := []string{"uika"}
|
||||
items := []mujica.TransferItem{{ID: "uika", Label: "Uika"}, {ID: "mutsumi", Label: "Mutsumi"}, {ID: "nyamu", Label: "Nyamu", Disabled: true}}
|
||||
items := []input.TransferItem{{ID: "uika", Label: "Uika"}, {ID: "mutsumi", Label: "Mutsumi"}, {ID: "nyamu", Label: "Nyamu", Disabled: true}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Transfer(c, &chosen, items, mujica.TransferOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
input.Transfer(c, &chosen, items, input.TransferOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
}
|
||||
@@ -4,12 +4,12 @@ var gitPages = []page{
|
||||
{
|
||||
ID: "301", Name: "DiffViewer", ZhName: "差异查看器 比较 diff", Category: "Git",
|
||||
Demo: gitDiffViewerDemo,
|
||||
Code: `var state mujica.DiffViewerState
|
||||
Code: `var state git.DiffViewerState
|
||||
from := "func main() {\n\tprintln(\"hi\")\n}\n"
|
||||
to := "func main() {\n\tprintln(\"hello\")\n}\n"
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.DiffViewer(c, &state, from, to, mujica.DiffViewerOptions{Language: "go", SideBySide: true}).Height(240)
|
||||
core.Use(c, core.Settings{})
|
||||
git.DiffViewer(c, &state, from, to, git.DiffViewerOptions{Language: "go", SideBySide: true}).Height(240)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
Keys: "Tab focuses the list; arrows and Page Up/Down scroll. Fold buttons take Tab; Enter or Space shows the hidden lines.",
|
||||
@@ -18,8 +18,8 @@ ui.Render(view, 400, 300, 1)`,
|
||||
ID: "302", Name: "InlineDiff", ZhName: "行内差异 词级差异", Category: "Git",
|
||||
Demo: gitInlineDiffDemo,
|
||||
Code: `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.InlineDiff(c, "The court meets at noon", "The court meets at dusk", mujica.InlineDiffOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
git.InlineDiff(c, "The court meets at noon", "The court meets at dusk", git.InlineDiffOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
Keys: "Not focusable; each line is read with its full text.",
|
||||
@@ -27,11 +27,11 @@ ui.Render(view, 400, 300, 1)`,
|
||||
{
|
||||
ID: "303", Name: "ThreeWayMerge", ZhName: "三方合并 合并冲突", Category: "Git",
|
||||
Demo: gitThreeWayMergeDemo,
|
||||
Code: `var state mujica.ThreeWayMergeState
|
||||
Code: `var state git.ThreeWayMergeState
|
||||
base, local, remote := "a\nb\nc", "a\nB1\nc", "a\nB2\nc"
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.ThreeWayMerge(c, &state, base, local, remote, mujica.ThreeWayMergeOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := git.ThreeWayMerge(c, &state, base, local, remote, git.ThreeWayMergeOptions{})
|
||||
r.Element.Height(280)
|
||||
if done, total := state.Resolved(); r.Changed() && done == total {
|
||||
_ = state.Result() // write the merged file
|
||||
@@ -43,11 +43,11 @@ ui.Render(view, 400, 300, 1)`,
|
||||
{
|
||||
ID: "304", Name: "ConflictResolver", ZhName: "冲突解决 冲突标记", Category: "Git",
|
||||
Demo: gitConflictResolverDemo,
|
||||
Code: `var state mujica.ConflictResolverState
|
||||
Code: `var state git.ConflictResolverState
|
||||
text := "a\n<<<<<<< HEAD\nmine\n=======\ntheirs\n>>>>>>> feature\nb"
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.ConflictResolver(c, &state, text, mujica.ConflictResolverOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if git.ConflictResolver(c, &state, text, git.ConflictResolverOptions{}).Changed() {
|
||||
_ = state.Result() // save the file once every block is resolved
|
||||
}
|
||||
}
|
||||
@@ -57,14 +57,14 @@ ui.Render(view, 400, 300, 1)`,
|
||||
{
|
||||
ID: "305", Name: "ChangesList", ZhName: "更改列表 暂存 未暂存", Category: "Git",
|
||||
Demo: gitChangesListDemo,
|
||||
Code: `var state mujica.ChangesListState
|
||||
files := []mujica.ChangedFile{
|
||||
{Path: "main.go", Status: mujica.GitModified, Staged: true},
|
||||
{Path: "notes.txt", Status: mujica.GitUntracked},
|
||||
Code: `var state git.ChangesListState
|
||||
files := []git.ChangedFile{
|
||||
{Path: "main.go", Status: git.GitModified, Staged: true},
|
||||
{Path: "notes.txt", Status: git.GitUntracked},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.ChangesList(c, &state, files, mujica.ChangesListOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := git.ChangesList(c, &state, files, git.ChangesListOptions{})
|
||||
r.Element.Height(240)
|
||||
if action, path, ok := r.Action(); ok {
|
||||
_, _ = action, path // run git add or git restore --staged
|
||||
@@ -76,11 +76,11 @@ ui.Render(view, 400, 300, 1)`,
|
||||
{
|
||||
ID: "306", Name: "CommitInput", ZhName: "提交输入 提交信息", Category: "Git",
|
||||
Demo: gitCommitInputDemo,
|
||||
Code: `msg := mujica.CommitMessage{}
|
||||
Code: `msg := git.CommitMessage{}
|
||||
busy := false
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.CommitInput(c, &msg, mujica.CommitInputOptions{Busy: busy})
|
||||
core.Use(c, core.Settings{})
|
||||
r := git.CommitInput(c, &msg, git.CommitInputOptions{Busy: busy})
|
||||
if r.Committed() || r.Amended() {
|
||||
busy = true // run git commit, then clear busy
|
||||
}
|
||||
@@ -91,16 +91,16 @@ ui.Render(view, 400, 300, 1)`,
|
||||
{
|
||||
ID: "307", Name: "CommitGraph", ZhName: "提交图谱 泳道 分支图", Category: "Git",
|
||||
Demo: gitCommitGraphDemo,
|
||||
Code: `var state mujica.CommitGraphState
|
||||
commits := []mujica.Commit{
|
||||
Code: `var state git.CommitGraphState
|
||||
commits := []git.Commit{
|
||||
{Hash: "c3", Parents: []string{"c2", "b1"}, Subject: "Merge branch", Refs: []string{"main"}},
|
||||
{Hash: "b1", Parents: []string{"c1"}, Subject: "Feature"},
|
||||
{Hash: "c2", Parents: []string{"c1"}, Subject: "Fix"},
|
||||
{Hash: "c1", Subject: "Initial commit"},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.CommitGraph(c, &state, commits, mujica.CommitGraphOptions{}).Element.Height(240)
|
||||
core.Use(c, core.Settings{})
|
||||
git.CommitGraph(c, &state, commits, git.CommitGraphOptions{}).Element.Height(240)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
Keys: "Up/Down/Home/End choose a commit; typing jumps by subject; Enter opens it.",
|
||||
@@ -108,11 +108,11 @@ ui.Render(view, 400, 300, 1)`,
|
||||
{
|
||||
ID: "308", Name: "CommitList", ZhName: "提交列表 提交历史", Category: "Git",
|
||||
Demo: gitCommitListDemo,
|
||||
Code: `var state mujica.ListState[string]
|
||||
commits := []mujica.Commit{{Hash: "a1b2c3d4", Author: "Sakiko", When: time.Now(), Subject: "Add the commit list"}}
|
||||
Code: `var state data.ListState[string]
|
||||
commits := []git.Commit{{Hash: "a1b2c3d4", Author: "Sakiko", When: time.Now(), Subject: "Add the commit list"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.CommitList(c, &state, commits, mujica.CommitListOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := git.CommitList(c, &state, commits, git.CommitListOptions{})
|
||||
r.Element.Height(240)
|
||||
if hash, ok := state.Selected(); ok && r.Submitted() {
|
||||
_ = hash // show the commit
|
||||
@@ -125,10 +125,10 @@ ui.Render(view, 400, 300, 1)`,
|
||||
ID: "309", Name: "BranchSelector", ZhName: "分支选择器 切换分支", Category: "Git",
|
||||
Demo: gitBranchSelectorDemo,
|
||||
Code: `branch := "main"
|
||||
branches := []mujica.Branch{{Name: "main", Current: true}, {Name: "origin/main", Remote: true}}
|
||||
branches := []git.Branch{{Name: "main", Current: true}, {Name: "origin/main", Remote: true}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.BranchSelector(c, &branch, branches, mujica.BranchSelectorOptions{AllowCreate: true})
|
||||
core.Use(c, core.Settings{})
|
||||
r := git.BranchSelector(c, &branch, branches, git.BranchSelectorOptions{AllowCreate: true})
|
||||
if name, ok := r.Created(); ok {
|
||||
_ = name // run git switch -c name
|
||||
}
|
||||
@@ -139,12 +139,12 @@ ui.Render(view, 400, 300, 1)`,
|
||||
{
|
||||
ID: "310", Name: "BranchList", ZhName: "分支列表 上游 领先落后", Category: "Git",
|
||||
Demo: gitBranchListDemo,
|
||||
Code: `var state mujica.ListState[string]
|
||||
branches := []mujica.Branch{{Name: "main", Current: true, Upstream: "origin/main", Ahead: 2}}
|
||||
actions := []mujica.GitAction{{ID: "delete", Label: "Delete", Icon: "trash-2"}}
|
||||
Code: `var state data.ListState[string]
|
||||
branches := []git.Branch{{Name: "main", Current: true, Upstream: "origin/main", Ahead: 2}}
|
||||
actions := []git.GitAction{{ID: "delete", Label: "Delete", Icon: "trash-2"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.BranchList(c, &state, branches, mujica.BranchListOptions{Actions: actions})
|
||||
core.Use(c, core.Settings{})
|
||||
r := git.BranchList(c, &state, branches, git.BranchListOptions{Actions: actions})
|
||||
r.Element.Height(200)
|
||||
if action, name, ok := r.Action(); ok {
|
||||
_, _ = action, name // run the action on the branch
|
||||
@@ -156,11 +156,11 @@ ui.Render(view, 400, 300, 1)`,
|
||||
{
|
||||
ID: "311", Name: "TagList", ZhName: "标签列表 tag", Category: "Git",
|
||||
Demo: gitTagListDemo,
|
||||
Code: `var state mujica.ListState[string]
|
||||
tags := []mujica.GitTag{{Name: "v1.1.0", Target: "a1b2c3d4", Annotation: "Release"}}
|
||||
Code: `var state data.ListState[string]
|
||||
tags := []git.GitTag{{Name: "v1.1.0", Target: "a1b2c3d4", Annotation: "Release"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.TagList(c, &state, tags, mujica.TagListOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := git.TagList(c, &state, tags, git.TagListOptions{})
|
||||
r.Element.Height(200)
|
||||
if _, hash, ok := r.Action(); ok {
|
||||
_ = hash // show the tagged commit
|
||||
@@ -172,11 +172,11 @@ ui.Render(view, 400, 300, 1)`,
|
||||
{
|
||||
ID: "312", Name: "StashList", ZhName: "贮藏列表 stash", Category: "Git",
|
||||
Demo: gitStashListDemo,
|
||||
Code: `var state mujica.ListState[string]
|
||||
stashes := []mujica.Stash{{Ref: "stash@{0}", Message: "WIP on main", Branch: "main"}}
|
||||
Code: `var state data.ListState[string]
|
||||
stashes := []git.Stash{{Ref: "stash@{0}", Message: "WIP on main", Branch: "main"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.StashList(c, &state, stashes, mujica.StashListOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := git.StashList(c, &state, stashes, git.StashListOptions{})
|
||||
r.Element.Height(200)
|
||||
if action, ref, ok := r.Action(); ok {
|
||||
_, _ = action, ref // run git stash apply, pop or drop
|
||||
@@ -188,14 +188,14 @@ ui.Render(view, 400, 300, 1)`,
|
||||
{
|
||||
ID: "313", Name: "BlameView", ZhName: "逐行追溯 blame 作者", Category: "Git",
|
||||
Demo: gitBlameViewDemo,
|
||||
Code: `var state mujica.BlameViewState
|
||||
lines := []mujica.BlameLine{
|
||||
Code: `var state git.BlameViewState
|
||||
lines := []git.BlameLine{
|
||||
{Hash: "a1b2c3d4", Author: "Sakiko", When: time.Now(), Summary: "Add main", Text: "package main"},
|
||||
{Hash: "a1b2c3d4", Author: "Sakiko", When: time.Now(), Summary: "Add main", Text: "func main() {}"},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.BlameView(c, &state, lines, mujica.BlameViewOptions{Language: "go"}).Height(240)
|
||||
core.Use(c, core.Settings{})
|
||||
git.BlameView(c, &state, lines, git.BlameViewOptions{Language: "go"}).Height(240)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
Keys: "Tab focuses the code; arrows scroll; typing jumps to a line starting with the text. Hover the left column for details.",
|
||||
@@ -203,11 +203,11 @@ ui.Render(view, 400, 300, 1)`,
|
||||
{
|
||||
ID: "314", Name: "FileHistory", ZhName: "文件历史 单文件提交", Category: "Git",
|
||||
Demo: gitFileHistoryDemo,
|
||||
Code: `var state mujica.ListState[string]
|
||||
commits := []mujica.Commit{{Hash: "a1b2c3d4", Author: "Sakiko", When: time.Now(), Subject: "Edit main.go"}}
|
||||
Code: `var state data.ListState[string]
|
||||
commits := []git.Commit{{Hash: "a1b2c3d4", Author: "Sakiko", When: time.Now(), Subject: "Edit main.go"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.FileHistory(c, &state, "main.go", commits, mujica.FileHistoryOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := git.FileHistory(c, &state, "main.go", commits, git.FileHistoryOptions{})
|
||||
r.Element.Height(240)
|
||||
if _, hash, ok := r.Action(); ok {
|
||||
_ = hash // open the diff of main.go at hash
|
||||
@@ -219,11 +219,11 @@ ui.Render(view, 400, 300, 1)`,
|
||||
{
|
||||
ID: "315", Name: "PullRequestCard", ZhName: "拉取请求卡片 合并请求 PR", Category: "Git",
|
||||
Demo: gitPullRequestCardDemo,
|
||||
Code: `pr := mujica.PullRequest{Number: 42, Title: "Add git components", Author: "Sakiko", Head: "feature", Base: "main",
|
||||
ChecksPassed: 3, Reviewers: []mujica.Reviewer{{Name: "Uika", State: mujica.ReviewApproved}}, Added: 120, Deleted: 8, Files: 6}
|
||||
Code: `pr := git.PullRequest{Number: 42, Title: "Add git components", Author: "Sakiko", Head: "feature", Base: "main",
|
||||
ChecksPassed: 3, Reviewers: []git.Reviewer{{Name: "Uika", State: git.ReviewApproved}}, Added: 120, Deleted: 8, Files: 6}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.PullRequestCard(c, pr, mujica.PullRequestCardOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if git.PullRequestCard(c, pr, git.PullRequestCardOptions{}).Changed() {
|
||||
// open the pull request
|
||||
}
|
||||
}
|
||||
@@ -233,15 +233,15 @@ ui.Render(view, 400, 300, 1)`,
|
||||
{
|
||||
ID: "316", Name: "ReviewComment", ZhName: "评审评论 代码评审", Category: "Git",
|
||||
Demo: gitReviewCommentDemo,
|
||||
Code: `thread := mujica.ReviewThread{File: "main.go", Line: 12, Messages: []mujica.ReviewMessage{
|
||||
Code: `thread := git.ReviewThread{File: "main.go", Line: 12, Messages: []git.ReviewMessage{
|
||||
{Author: "Uika", When: time.Now(), Body: "Can this return early?"},
|
||||
}}
|
||||
draft := ""
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.ReviewComment(c, thread, &draft, mujica.ReviewCommentOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := git.ReviewComment(c, thread, &draft, git.ReviewCommentOptions{})
|
||||
if text, ok := r.Replied(); ok {
|
||||
thread.Messages = append(thread.Messages, mujica.ReviewMessage{Author: "Sakiko", When: time.Now(), Body: text})
|
||||
thread.Messages = append(thread.Messages, git.ReviewMessage{Author: "Sakiko", When: time.Now(), Body: text})
|
||||
}
|
||||
if r.Resolved() || r.Reopened() {
|
||||
thread.Resolved = !thread.Resolved
|
||||
@@ -254,8 +254,8 @@ ui.Render(view, 400, 300, 1)`,
|
||||
ID: "317", Name: "DiffStat", ZhName: "差异统计 增删行数", Category: "Git",
|
||||
Demo: gitDiffStatDemo,
|
||||
Code: `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.DiffStat(c, 120, 8, mujica.DiffStatOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
git.DiffStat(c, 120, 8, git.DiffStatOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
Keys: "Not focusable; read as additions and deletions.",
|
||||
@@ -264,8 +264,8 @@ ui.Render(view, 400, 300, 1)`,
|
||||
ID: "318", Name: "GitStatusBadge", ZhName: "Git 状态标记 文件状态", Category: "Git",
|
||||
Demo: gitStatusBadgeDemo,
|
||||
Code: `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.GitStatusBadge(c, mujica.GitModified, mujica.GitStatusBadgeOptions{ShowLabel: true})
|
||||
core.Use(c, core.Settings{})
|
||||
git.GitStatusBadge(c, git.GitModified, git.GitStatusBadgeOptions{ShowLabel: true})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
Keys: "Not focusable; each badge is read by its status name.",
|
||||
@@ -274,10 +274,10 @@ ui.Render(view, 400, 300, 1)`,
|
||||
ID: "319", Name: "CommandBlock", ZhName: "命令块 终端输出 退出码", Category: "Git",
|
||||
Demo: gitCommandBlockDemo,
|
||||
Code: `open := true
|
||||
run := mujica.CommandRun{Command: "go test ./...", Output: "\x1b[32mok\x1b[0m example 0.4s", Duration: 400 * time.Millisecond}
|
||||
run := agent.CommandRun{Command: "go test ./...", Output: "\x1b[32mok\x1b[0m example 0.4s", Duration: 400 * time.Millisecond}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.CommandBlock(c, run, &open, mujica.CommandBlockOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := git.CommandBlock(c, run, &open, git.CommandBlockOptions{})
|
||||
if what, ok := r.Copied(); ok {
|
||||
_ = what // "command" or "output" is on the clipboard
|
||||
}
|
||||
@@ -289,8 +289,8 @@ ui.Render(view, 400, 300, 1)`,
|
||||
ID: "320", Name: "AnsiText", ZhName: "ANSI 文本 终端颜色", Category: "Git",
|
||||
Demo: gitAnsiTextDemo,
|
||||
Code: `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.AnsiText(c, "\x1b[1;32mPASS\x1b[0m 12 tests\n\x1b[31mFAIL\x1b[0m 1 test", mujica.AnsiTextOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
git.AnsiText(c, "\x1b[1;32mPASS\x1b[0m 12 tests\n\x1b[31mFAIL\x1b[0m 1 test", git.AnsiTextOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`,
|
||||
Keys: "Not focusable; shows the text with its styles.",
|
||||
|
||||
@@ -6,7 +6,10 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/agent"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/git"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/navigation"
|
||||
)
|
||||
|
||||
func gitPullRequestCardDemo(c *ui.Context, log func(string)) {
|
||||
@@ -16,18 +19,18 @@ func gitPullRequestCardDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
s := ui.Local(c.Root(), "315", func() state { return state{} })
|
||||
navigationDemo(c, func() {
|
||||
mujica.SegmentedControl(c, &s.st, []mujica.Segment{{Label: "Open"}, {Label: "Draft"}, {Label: "Merged"}, {Label: "Closed"}}, mujica.SegmentedControlOptions{Label: "State"})
|
||||
navigation.SegmentedControl(c, &s.st, []navigation.Segment{{Label: "Open"}, {Label: "Draft"}, {Label: "Merged"}, {Label: "Closed"}}, navigation.SegmentedControlOptions{Label: "State"})
|
||||
navigationCheck(c, &s.failed, "Failing check", log)
|
||||
navigationCheck(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
pr := mujica.PullRequest{Number: 318, Title: "Git components 301–320", Author: "Sakiko", State: mujica.PullRequestState(s.st),
|
||||
pr := git.PullRequest{Number: 318, Title: "Git components 301–320", Author: "Sakiko", State: git.PullRequestState(s.st),
|
||||
Head: "feature/git", Base: "main", ChecksPassed: 12, ChecksPending: 1,
|
||||
Reviewers: []mujica.Reviewer{{Name: "Uika", State: mujica.ReviewApproved}, {Name: "Mutsumi", State: mujica.ReviewChangesRequested}, {Name: "Umiri"}},
|
||||
Reviewers: []git.Reviewer{{Name: "Uika", State: git.ReviewApproved}, {Name: "Mutsumi", State: git.ReviewChangesRequested}, {Name: "Umiri"}},
|
||||
Added: 2412, Deleted: 310, Files: 46}
|
||||
if s.failed {
|
||||
pr.ChecksFailed = 1
|
||||
}
|
||||
if mujica.PullRequestCard(c, pr, mujica.PullRequestCardOptions{Disabled: s.disabled}).Changed() {
|
||||
if git.PullRequestCard(c, pr, git.PullRequestCardOptions{Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("Open #%d", pr.Number))
|
||||
}
|
||||
})
|
||||
@@ -35,12 +38,12 @@ func gitPullRequestCardDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func gitReviewCommentDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
thread mujica.ReviewThread
|
||||
thread git.ReviewThread
|
||||
draft string
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "316", func() state {
|
||||
return state{thread: mujica.ReviewThread{File: "commit_graph.go", Line: 88, Messages: []mujica.ReviewMessage{
|
||||
return state{thread: git.ReviewThread{File: "commit_graph.go", Line: 88, Messages: []git.ReviewMessage{
|
||||
{Author: "Uika", When: gitDemoTime, Body: "Should merge lanes reuse the parent's color?"},
|
||||
{Author: "Sakiko", When: gitDemoTime.Add(time.Hour), Body: "Yes, the first parent keeps it."},
|
||||
}}}
|
||||
@@ -48,10 +51,10 @@ func gitReviewCommentDemo(c *ui.Context, log func(string)) {
|
||||
navigationDemo(c, func() {
|
||||
navigationCheck(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
r := mujica.ReviewComment(c, s.thread, &s.draft, mujica.ReviewCommentOptions{Disabled: s.disabled})
|
||||
r := git.ReviewComment(c, s.thread, &s.draft, git.ReviewCommentOptions{Disabled: s.disabled})
|
||||
if text, ok := r.Replied(); ok {
|
||||
log("Reply: " + text)
|
||||
s.thread.Messages = append(s.thread.Messages, mujica.ReviewMessage{Author: "You", When: c.Now(), Body: text})
|
||||
s.thread.Messages = append(s.thread.Messages, git.ReviewMessage{Author: "You", When: c.Now(), Body: text})
|
||||
}
|
||||
if r.Resolved() {
|
||||
log("Resolved")
|
||||
@@ -72,7 +75,7 @@ func gitDiffStatDemo(c *ui.Context, log func(string)) {
|
||||
navigationSlider(c, &s.deleted, "Deleted", 0, 500, log)
|
||||
navigationSlider(c, &s.blocks, "Blocks", 1, 10, log)
|
||||
}, func() {
|
||||
mujica.DiffStat(c, int(s.added), int(s.deleted), mujica.DiffStatOptions{Blocks: int(s.blocks)})
|
||||
git.DiffStat(c, int(s.added), int(s.deleted), git.DiffStatOptions{Blocks: int(s.blocks)})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -83,8 +86,8 @@ func gitStatusBadgeDemo(c *ui.Context, log func(string)) {
|
||||
navigationCheck(c, &s.labels, "Show labels", log)
|
||||
}, func() {
|
||||
ui.Row(c).Gap(16).Wrap().Children(func() {
|
||||
for st := mujica.GitModified; st <= mujica.GitCopied; st++ {
|
||||
mujica.GitStatusBadge(c, st, mujica.GitStatusBadgeOptions{ShowLabel: s.labels})
|
||||
for st := git.GitModified; st <= git.GitCopied; st++ {
|
||||
git.GitStatusBadge(c, st, git.GitStatusBadgeOptions{ShowLabel: s.labels})
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -99,14 +102,14 @@ func gitCommandBlockDemo(c *ui.Context, log func(string)) {
|
||||
navigationCheck(c, &s.failed, "Failed", log)
|
||||
navigationCheck(c, &s.running, "Running", log)
|
||||
}, func() {
|
||||
run := mujica.CommandRun{Command: "go test -run Git ./...", Duration: 3214 * time.Millisecond,
|
||||
run := agent.CommandRun{Command: "go test -run Git ./...", Duration: 3214 * time.Millisecond,
|
||||
Output: "\x1b[32mok\x1b[0m \tmujicaui\t3.214s\n\x1b[33mwarn\x1b[0m slow test: TestCommitGraphVirtualized"}
|
||||
if s.failed {
|
||||
run.ExitCode = 1
|
||||
run.Output = "\x1b[31m--- FAIL\x1b[0m: TestDiffViewer (0.02s)\n\x1b[1mFAIL\x1b[0m\tmujicaui\t0.401s"
|
||||
}
|
||||
run.Running = s.running
|
||||
r := mujica.CommandBlock(c, run, &s.open, mujica.CommandBlockOptions{})
|
||||
r := git.CommandBlock(c, run, &s.open, git.CommandBlockOptions{})
|
||||
if r.Toggled() {
|
||||
log(fmt.Sprintf("Toggled: open=%v", s.open))
|
||||
}
|
||||
@@ -122,13 +125,13 @@ func gitAnsiTextDemo(c *ui.Context, log func(string)) {
|
||||
navigationDemo(c, func() {
|
||||
navigationCheck(c, &s.wrap, "Wrap", log)
|
||||
}, func() {
|
||||
ui.Column(c).Padding(12).Background(mujica.Tokens(c).Surface).Radius(4).Children(func() {
|
||||
mujica.AnsiText(c, "\x1b[1mBuilding\x1b[0m mujica \x1b[2m(v1.1)\x1b[0m\n"+
|
||||
ui.Column(c).Padding(12).Background(core.Tokens(c).Surface).Radius(4).Children(func() {
|
||||
git.AnsiText(c, "\x1b[1mBuilding\x1b[0m mujica \x1b[2m(v1.1)\x1b[0m\n"+
|
||||
"\x1b[32m✓\x1b[0m 300 passed \x1b[31m✗\x1b[0m 2 failed \x1b[33m!\x1b[0m 1 skipped\n"+
|
||||
"\x1b[30m0\x1b[31m1\x1b[32m2\x1b[33m3\x1b[34m4\x1b[35m5\x1b[36m6\x1b[37m7\x1b[90m8\x1b[91m9\x1b[92mA\x1b[93mB\x1b[94mC\x1b[95mD\x1b[96mE\x1b[97mF\x1b[0m\n"+
|
||||
"\x1b[38;5;208m256-color\x1b[0m \x1b[38;2;125;32;52mtrue color\x1b[0m \x1b[4munderline\x1b[0m \x1b[3mitalic\x1b[0m \x1b[9mstruck\x1b[0m \x1b[7m inverse \x1b[0m\n"+
|
||||
"A long line that only breaks when wrapping is on, so the terminal layout of columns stays intact as it was printed.\n"+
|
||||
"Unknown escapes stay as text: \x1b[2K(cleared line)", mujica.AnsiTextOptions{Wrap: s.wrap})
|
||||
"Unknown escapes stay as text: \x1b[2K(cleared line)", git.AnsiTextOptions{Wrap: s.wrap})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -7,7 +7,10 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/git"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
var gitDemoTime = time.Date(2026, 10, 5, 9, 30, 0, 0, time.UTC)
|
||||
@@ -15,11 +18,11 @@ var gitDemoTime = time.Date(2026, 10, 5, 9, 30, 0, 0, time.UTC)
|
||||
const gitDemoOld = "package court\n\nimport \"fmt\"\n\n// Greet welcomes a guest.\nfunc Greet(name string) {\n\tfmt.Println(\"Welcome,\", name)\n}\n\nfunc Leave() {}\n\nfunc Dance() {}\n\nfunc Bow() {}\n"
|
||||
const gitDemoNew = "package court\n\nimport \"fmt\"\n\n// Greet welcomes a guest by title.\nfunc Greet(title, name string) {\n\tfmt.Println(\"Welcome,\", title, name)\n}\n\nfunc Leave() {}\n\nfunc Dance() {}\n\nfunc Bow() {}\n\nfunc Curtsy() {}\n"
|
||||
|
||||
func gitDemoCommits(n int) []mujica.Commit {
|
||||
out := make([]mujica.Commit, n)
|
||||
func gitDemoCommits(n int) []git.Commit {
|
||||
out := make([]git.Commit, n)
|
||||
authors := []string{"Sakiko", "Mutsumi", "Uika", "Umiri", "Nyamu"}
|
||||
for i := range out {
|
||||
out[i] = mujica.Commit{Hash: fmt.Sprintf("%016x%024x", uint64(n-i)*0x9E3779B97F4A7C15, n-i), Author: authors[i%5],
|
||||
out[i] = git.Commit{Hash: fmt.Sprintf("%016x%024x", uint64(n-i)*0x9E3779B97F4A7C15, n-i), Author: authors[i%5],
|
||||
When: gitDemoTime.Add(-time.Duration(i) * 37 * time.Minute), Subject: fmt.Sprintf("Chapter %d of the chronicle", n-i)}
|
||||
if i+1 < n {
|
||||
out[i].Parents = []string{fmt.Sprintf("%016x%024x", uint64(n-i-1)*0x9E3779B97F4A7C15, n-i-1)}
|
||||
@@ -31,8 +34,8 @@ func gitDemoCommits(n int) []mujica.Commit {
|
||||
return out
|
||||
}
|
||||
|
||||
func gitDemoGraph() []mujica.Commit {
|
||||
cs := []mujica.Commit{
|
||||
func gitDemoGraph() []git.Commit {
|
||||
cs := []git.Commit{
|
||||
{Hash: "e7", Parents: []string{"e5", "f3"}, Subject: "Merge branch 'feature/lanes'", Refs: []string{"main"}},
|
||||
{Hash: "f3", Parents: []string{"f2"}, Subject: "Color lanes by branch"},
|
||||
{Hash: "e5", Parents: []string{"e4", "h1"}, Subject: "Merge hotfix"},
|
||||
@@ -51,8 +54,8 @@ func gitDemoGraph() []mujica.Commit {
|
||||
return cs
|
||||
}
|
||||
|
||||
func gitDemoBranches() []mujica.Branch {
|
||||
return []mujica.Branch{
|
||||
func gitDemoBranches() []git.Branch {
|
||||
return []git.Branch{
|
||||
{Name: "main", Current: true, Upstream: "origin/main", Ahead: 2, Behind: 1},
|
||||
{Name: "feature/lanes", Upstream: "origin/feature/lanes", Ahead: 5},
|
||||
{Name: "fix/blame"},
|
||||
@@ -64,7 +67,7 @@ func gitDemoBranches() []mujica.Branch {
|
||||
|
||||
func gitDiffViewerDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
viewer mujica.DiffViewerState
|
||||
viewer git.DiffViewerState
|
||||
split, plain bool
|
||||
context float64
|
||||
}
|
||||
@@ -78,7 +81,7 @@ func gitDiffViewerDemo(c *ui.Context, log func(string)) {
|
||||
if s.plain {
|
||||
lang = ""
|
||||
}
|
||||
mujica.DiffViewer(c, &s.viewer, gitDemoOld, gitDemoNew, mujica.DiffViewerOptions{SideBySide: s.split, Context: int(s.context), Language: lang}).Height(380)
|
||||
git.DiffViewer(c, &s.viewer, gitDemoOld, gitDemoNew, git.DiffViewerOptions{SideBySide: s.split, Context: int(s.context), Language: lang}).Height(380)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -93,19 +96,19 @@ func gitInlineDiffDemo(c *ui.Context, log func(string)) {
|
||||
navigationDemo(c, func() {
|
||||
navigationCheck(c, &s.mono, "Monospace", log)
|
||||
}, func() {
|
||||
if mujica.TextInput(c, &s.from, mujica.TextInputOptions{Label: "Old line"}).Changed() {
|
||||
if input.TextInput(c, &s.from, input.TextInputOptions{Label: "Old line"}).Changed() {
|
||||
log("Old: " + s.from)
|
||||
}
|
||||
if mujica.TextInput(c, &s.to, mujica.TextInputOptions{Label: "New line"}).Changed() {
|
||||
if input.TextInput(c, &s.to, input.TextInputOptions{Label: "New line"}).Changed() {
|
||||
log("New: " + s.to)
|
||||
}
|
||||
mujica.InlineDiff(c, s.from, s.to, mujica.InlineDiffOptions{Mono: s.mono})
|
||||
git.InlineDiff(c, s.from, s.to, git.InlineDiffOptions{Mono: s.mono})
|
||||
})
|
||||
}
|
||||
|
||||
func gitThreeWayMergeDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
merge mujica.ThreeWayMergeState
|
||||
merge git.ThreeWayMergeState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "303", func() state { return state{} })
|
||||
@@ -115,19 +118,19 @@ func gitThreeWayMergeDemo(c *ui.Context, log func(string)) {
|
||||
navigationDemo(c, func() {
|
||||
navigationCheck(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
r := mujica.ThreeWayMerge(c, &s.merge, base, ours, theirs, mujica.ThreeWayMergeOptions{Disabled: s.disabled})
|
||||
r := git.ThreeWayMerge(c, &s.merge, base, ours, theirs, git.ThreeWayMergeOptions{Disabled: s.disabled})
|
||||
r.Element.Height(360)
|
||||
if r.Changed() {
|
||||
done, total := s.merge.Resolved()
|
||||
log(fmt.Sprintf("Changed: %d of %d resolved", done, total))
|
||||
}
|
||||
ui.Text(c, s.merge.Result()).Font("monospace").TextColor(mujica.Tokens(c).TextMuted)
|
||||
ui.Text(c, s.merge.Result()).Font("monospace").TextColor(core.Tokens(c).TextMuted)
|
||||
})
|
||||
}
|
||||
|
||||
func gitConflictResolverDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
res mujica.ConflictResolverState
|
||||
res git.ConflictResolverState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "304", func() state { return state{} })
|
||||
@@ -135,7 +138,7 @@ func gitConflictResolverDemo(c *ui.Context, log func(string)) {
|
||||
navigationDemo(c, func() {
|
||||
navigationCheck(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
if mujica.ConflictResolver(c, &s.res, text, mujica.ConflictResolverOptions{Disabled: s.disabled}).Changed() {
|
||||
if git.ConflictResolver(c, &s.res, text, git.ConflictResolverOptions{Disabled: s.disabled}).Changed() {
|
||||
done, total := s.res.Resolved()
|
||||
log(fmt.Sprintf("Changed: %d of %d resolved", done, total))
|
||||
}
|
||||
@@ -144,23 +147,23 @@ func gitConflictResolverDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func gitChangesListDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
list mujica.ChangesListState
|
||||
files []mujica.ChangedFile
|
||||
list git.ChangesListState
|
||||
files []git.ChangedFile
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "305", func() state {
|
||||
return state{files: []mujica.ChangedFile{
|
||||
{Path: "commit_graph.go", Status: mujica.GitAdded, Staged: true},
|
||||
{Path: "internal/git.go", Status: mujica.GitModified},
|
||||
{Path: "docs/old.md", Status: mujica.GitDeleted},
|
||||
{Path: "notes.txt", Status: mujica.GitUntracked},
|
||||
{Path: "icons/svg/tag.svg", Status: mujica.GitRenamed},
|
||||
return state{files: []git.ChangedFile{
|
||||
{Path: "commit_graph.go", Status: git.GitAdded, Staged: true},
|
||||
{Path: "internal/git.go", Status: git.GitModified},
|
||||
{Path: "docs/old.md", Status: git.GitDeleted},
|
||||
{Path: "notes.txt", Status: git.GitUntracked},
|
||||
{Path: "icons/svg/tag.svg", Status: git.GitRenamed},
|
||||
}}
|
||||
})
|
||||
navigationDemo(c, func() {
|
||||
navigationCheck(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
r := mujica.ChangesList(c, &s.list, s.files, mujica.ChangesListOptions{Disabled: s.disabled})
|
||||
r := git.ChangesList(c, &s.list, s.files, git.ChangesListOptions{Disabled: s.disabled})
|
||||
r.Element.Height(320)
|
||||
if a, path, ok := r.Action(); ok {
|
||||
log("Action: " + a + " " + path)
|
||||
@@ -173,8 +176,8 @@ func gitChangesListDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
// gitStage applies a stage action to a copy of files.
|
||||
func gitStage(files []mujica.ChangedFile, action, path string) []mujica.ChangedFile {
|
||||
out := append([]mujica.ChangedFile(nil), files...)
|
||||
func gitStage(files []git.ChangedFile, action, path string) []git.ChangedFile {
|
||||
out := append([]git.ChangedFile(nil), files...)
|
||||
for i := range out {
|
||||
switch {
|
||||
case action == "stage_all":
|
||||
@@ -190,14 +193,14 @@ func gitStage(files []mujica.ChangedFile, action, path string) []mujica.ChangedF
|
||||
|
||||
func gitCommitInputDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
msg mujica.CommitMessage
|
||||
msg git.CommitMessage
|
||||
busy, disabled bool
|
||||
until time.Time
|
||||
}
|
||||
s := ui.Local(c.Root(), "306", func() state { return state{} })
|
||||
if s.busy && c.Now().After(s.until) {
|
||||
s.busy = false
|
||||
s.msg = mujica.CommitMessage{}
|
||||
s.msg = git.CommitMessage{}
|
||||
log("Committed")
|
||||
}
|
||||
if s.busy {
|
||||
@@ -206,7 +209,7 @@ func gitCommitInputDemo(c *ui.Context, log func(string)) {
|
||||
navigationDemo(c, func() {
|
||||
navigationCheck(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
r := mujica.CommitInput(c, &s.msg, mujica.CommitInputOptions{Busy: s.busy, Disabled: s.disabled})
|
||||
r := git.CommitInput(c, &s.msg, git.CommitInputOptions{Busy: s.busy, Disabled: s.disabled})
|
||||
if r.Committed() || r.Amended() {
|
||||
log(fmt.Sprintf("Submit: amend=%v title=%q", r.Amended(), s.msg.Title))
|
||||
s.busy, s.until = true, c.Now().Add(1200*time.Millisecond)
|
||||
@@ -216,7 +219,7 @@ func gitCommitInputDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func gitCommitGraphDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
graph mujica.CommitGraphState
|
||||
graph git.CommitGraphState
|
||||
big bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "307", func() state { return state{} })
|
||||
@@ -228,7 +231,7 @@ func gitCommitGraphDemo(c *ui.Context, log func(string)) {
|
||||
if s.big {
|
||||
commits = big
|
||||
}
|
||||
r := mujica.CommitGraph(c, &s.graph, commits, mujica.CommitGraphOptions{})
|
||||
r := git.CommitGraph(c, &s.graph, commits, git.CommitGraphOptions{})
|
||||
r.Element.Height(380)
|
||||
if hash, ok := s.graph.List.Selected(); ok && (r.Changed() || r.Submitted()) {
|
||||
log(fmt.Sprintf("Selected %s, open=%v", hash[:min(7, len(hash))], r.Submitted()))
|
||||
@@ -238,7 +241,7 @@ func gitCommitGraphDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func gitCommitListDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
list mujica.ListState[string]
|
||||
list data.ListState[string]
|
||||
loading bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "308", func() state { return state{} })
|
||||
@@ -246,11 +249,11 @@ func gitCommitListDemo(c *ui.Context, log func(string)) {
|
||||
navigationDemo(c, func() {
|
||||
navigationCheck(c, &s.loading, "Loading more", log)
|
||||
}, func() {
|
||||
st := mujica.DataReady
|
||||
st := data.DataReady
|
||||
if s.loading {
|
||||
st = mujica.DataLoadingMore
|
||||
st = data.DataLoadingMore
|
||||
}
|
||||
r := mujica.CommitList(c, &s.list, commits, mujica.CommitListOptions{Status: st})
|
||||
r := git.CommitList(c, &s.list, commits, git.CommitListOptions{Status: st})
|
||||
r.Element.Height(360)
|
||||
if hash, ok := s.list.Selected(); ok && (r.Changed() || r.Submitted()) {
|
||||
log(fmt.Sprintf("Selected %s, open=%v", hash[:7], r.Submitted()))
|
||||
@@ -262,29 +265,29 @@ func gitBranchSelectorDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
branch string
|
||||
create, disabled bool
|
||||
branches []mujica.Branch
|
||||
branches []git.Branch
|
||||
}
|
||||
s := ui.Local(c.Root(), "309", func() state { return state{branch: "main", create: true, branches: gitDemoBranches()} })
|
||||
navigationDemo(c, func() {
|
||||
navigationCheck(c, &s.create, "Allow create", log)
|
||||
navigationCheck(c, &s.disabled, "Disabled", log)
|
||||
}, func() {
|
||||
r := mujica.BranchSelector(c, &s.branch, s.branches, mujica.BranchSelectorOptions{AllowCreate: s.create, Disabled: s.disabled})
|
||||
r := git.BranchSelector(c, &s.branch, s.branches, git.BranchSelectorOptions{AllowCreate: s.create, Disabled: s.disabled})
|
||||
if r.Changed() {
|
||||
log("Changed: " + s.branch)
|
||||
}
|
||||
if name, ok := r.Created(); ok {
|
||||
log("Create: " + name)
|
||||
s.branches = append(append([]mujica.Branch(nil), s.branches...), mujica.Branch{Name: name})
|
||||
s.branches = append(append([]git.Branch(nil), s.branches...), git.Branch{Name: name})
|
||||
s.branch = name
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func gitBranchListDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "310", func() mujica.ListState[string] { return mujica.ListState[string]{} })
|
||||
actions := []mujica.GitAction{{ID: "checkout", Label: "Check out", Icon: "git-branch"}, {ID: "delete", Label: "Delete", Icon: "trash-2"}}
|
||||
r := mujica.BranchList(c, s, gitDemoBranches(), mujica.BranchListOptions{Actions: actions})
|
||||
s := ui.Local(c.Root(), "310", func() data.ListState[string] { return data.ListState[string]{} })
|
||||
actions := []git.GitAction{{ID: "checkout", Label: "Check out", Icon: "git-branch"}, {ID: "delete", Label: "Delete", Icon: "trash-2"}}
|
||||
r := git.BranchList(c, s, gitDemoBranches(), git.BranchListOptions{Actions: actions})
|
||||
r.Element.Height(300)
|
||||
if a, name, ok := r.Action(); ok {
|
||||
log("Action: " + a + " " + name)
|
||||
@@ -295,13 +298,13 @@ func gitBranchListDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
func gitTagListDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "311", func() mujica.ListState[string] { return mujica.ListState[string]{} })
|
||||
tags := []mujica.GitTag{
|
||||
s := ui.Local(c.Root(), "311", func() data.ListState[string] { return data.ListState[string]{} })
|
||||
tags := []git.GitTag{
|
||||
{Name: "v1.1.0", Target: "9e3779b97f4a7c15", Annotation: "Five hundred components"},
|
||||
{Name: "v1.0.0", Target: "3c6ef372fe94f82a", Annotation: "The first hundred"},
|
||||
{Name: "nightly", Target: "daa66d2c7ddf743f"},
|
||||
}
|
||||
r := mujica.TagList(c, s, tags, mujica.TagListOptions{})
|
||||
r := git.TagList(c, s, tags, git.TagListOptions{})
|
||||
r.Element.Height(220)
|
||||
if _, hash, ok := r.Action(); ok {
|
||||
log("Jump to " + hash[:7])
|
||||
@@ -313,22 +316,22 @@ func gitTagListDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func gitStashListDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
list mujica.ListState[string]
|
||||
stashes []mujica.Stash
|
||||
list data.ListState[string]
|
||||
stashes []git.Stash
|
||||
}
|
||||
s := ui.Local(c.Root(), "312", func() state {
|
||||
return state{stashes: []mujica.Stash{
|
||||
return state{stashes: []git.Stash{
|
||||
{Ref: "stash@{0}", Message: "WIP on main: lane colors", Branch: "main"},
|
||||
{Ref: "stash@{1}", Message: "Try serif headings", Branch: "feature/type"},
|
||||
{Ref: "stash@{2}", Message: "Blame hover experiment", Branch: "fix/blame"},
|
||||
}}
|
||||
})
|
||||
r := mujica.StashList(c, &s.list, s.stashes, mujica.StashListOptions{})
|
||||
r := git.StashList(c, &s.list, s.stashes, git.StashListOptions{})
|
||||
r.Element.Height(240)
|
||||
if a, ref, ok := r.Action(); ok {
|
||||
log("Action: " + a + " " + ref)
|
||||
if a != "apply" {
|
||||
var keep []mujica.Stash
|
||||
var keep []git.Stash
|
||||
for _, st := range s.stashes {
|
||||
if st.Ref != ref {
|
||||
keep = append(keep, st)
|
||||
@@ -340,23 +343,23 @@ func gitStashListDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
func gitBlameViewDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "313", func() mujica.BlameViewState { return mujica.BlameViewState{} })
|
||||
var lines []mujica.BlameLine
|
||||
s := ui.Local(c.Root(), "313", func() git.BlameViewState { return git.BlameViewState{} })
|
||||
var lines []git.BlameLine
|
||||
for i, l := range strings.Split(strings.TrimSuffix(gitDemoNew, "\n"), "\n") {
|
||||
who := []mujica.BlameLine{
|
||||
who := []git.BlameLine{
|
||||
{Hash: "9e3779b97f4a7c15", Author: "Sakiko", When: gitDemoTime, Summary: "Create the court package"},
|
||||
{Hash: "3c6ef372fe94f82a", Author: "Uika", When: gitDemoTime.Add(-48 * time.Hour), Summary: "Greet guests by title"},
|
||||
}[min(i/4%2, 1)]
|
||||
who.Text = l
|
||||
lines = append(lines, who)
|
||||
}
|
||||
mujica.BlameView(c, s, lines, mujica.BlameViewOptions{Language: "go"}).Height(380)
|
||||
ui.Text(c, "Hover the left column for commit details.").TextColor(mujica.Tokens(c).TextMuted)
|
||||
git.BlameView(c, s, lines, git.BlameViewOptions{Language: "go"}).Height(380)
|
||||
ui.Text(c, "Hover the left column for commit details.").TextColor(core.Tokens(c).TextMuted)
|
||||
}
|
||||
|
||||
func gitFileHistoryDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "314", func() mujica.ListState[string] { return mujica.ListState[string]{} })
|
||||
r := mujica.FileHistory(c, s, "court/greet.go", gitDemoCommits(12), mujica.FileHistoryOptions{})
|
||||
s := ui.Local(c.Root(), "314", func() data.ListState[string] { return data.ListState[string]{} })
|
||||
r := git.FileHistory(c, s, "court/greet.go", gitDemoCommits(12), git.FileHistoryOptions{})
|
||||
r.Element.Height(360)
|
||||
if _, hash, ok := r.Action(); ok {
|
||||
log("Diff at " + hash[:7])
|
||||
|
||||
@@ -7,22 +7,23 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
const inputCategory = "Inputs"
|
||||
|
||||
// inputVariants lists the button variants for variant pickers.
|
||||
var inputVariants = []mujica.RadioItem[mujica.ButtonVariant]{
|
||||
{Value: mujica.Primary, Label: "Primary"}, {Value: mujica.Secondary, Label: "Secondary"},
|
||||
{Value: mujica.Outline, Label: "Outline"}, {Value: mujica.Ghost, Label: "Ghost"}, {Value: mujica.Danger, Label: "Danger"},
|
||||
var inputVariants = []input.RadioItem[input.ButtonVariant]{
|
||||
{Value: input.Primary, Label: "Primary"}, {Value: input.Secondary, Label: "Secondary"},
|
||||
{Value: input.Outline, Label: "Outline"}, {Value: input.Ghost, Label: "Ghost"}, {Value: input.Danger, Label: "Danger"},
|
||||
}
|
||||
|
||||
// inputSection titles one part of a demo.
|
||||
func inputSection(c *ui.Context, title string, fn func()) {
|
||||
ui.Column(c).Gap(8).Children(func() {
|
||||
ui.Text(c, title).FontWeight(600).TextColor(mujica.Tokens(c).TextMuted)
|
||||
ui.Text(c, title).FontWeight(600).TextColor(core.Tokens(c).TextMuted)
|
||||
fn()
|
||||
})
|
||||
}
|
||||
@@ -31,7 +32,7 @@ func inputSection(c *ui.Context, title string, fn func()) {
|
||||
func inputFlags(c *ui.Context, log func(string), flags map[string]*bool, order ...string) {
|
||||
ui.Row(c).Gap(16).Wrap().Children(func() {
|
||||
for _, name := range order {
|
||||
if mujica.Checkbox(c, flags[name], name, mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, flags[name], name, input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprintf("option %s = %v", name, *flags[name]))
|
||||
}
|
||||
}
|
||||
@@ -39,7 +40,7 @@ func inputFlags(c *ui.Context, log func(string), flags map[string]*bool, order .
|
||||
}
|
||||
|
||||
type inputButtonDemo struct {
|
||||
variant mujica.ButtonVariant
|
||||
variant input.ButtonVariant
|
||||
icon, loading, disabled bool
|
||||
}
|
||||
|
||||
@@ -81,22 +82,22 @@ type inputRadioDemo struct {
|
||||
|
||||
var inputPages = []page{
|
||||
{ID: "021", Name: "Button", ZhName: "按钮", Category: inputCategory, Keys: "Tab 聚焦;Enter 或 Space 触发。",
|
||||
Code: `if mujica.Button(c, "Save", mujica.ButtonOptions{Icon: icons.Must("check")}).Clicked() {
|
||||
Code: `if input.Button(c, "Save", input.ButtonOptions{Icon: icons.Must("check")}).Clicked() {
|
||||
fmt.Println("saved")
|
||||
}`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "021", func() inputButtonDemo { return inputButtonDemo{} })
|
||||
inputSection(c, "Parameters", func() {
|
||||
mujica.RadioGroup(c, &s.variant, inputVariants, mujica.RadioGroupOptions{Horizontal: true, Label: "Variant"})
|
||||
input.RadioGroup(c, &s.variant, inputVariants, input.RadioGroupOptions{Horizontal: true, Label: "Variant"})
|
||||
inputFlags(c, log, map[string]*bool{"Icon": &s.icon, "Loading": &s.loading, "Disabled": &s.disabled}, "Icon", "Loading", "Disabled")
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
opts := mujica.ButtonOptions{Variant: s.variant, Loading: s.loading, Disabled: s.disabled}
|
||||
opts := input.ButtonOptions{Variant: s.variant, Loading: s.loading, Disabled: s.disabled}
|
||||
if s.icon {
|
||||
opts.Icon = icons.Must("check")
|
||||
}
|
||||
ui.Row(c).Children(func() {
|
||||
if mujica.Button(c, "Save changes", opts).Clicked() {
|
||||
if input.Button(c, "Save changes", opts).Clicked() {
|
||||
log("Button clicked")
|
||||
}
|
||||
})
|
||||
@@ -104,30 +105,30 @@ var inputPages = []page{
|
||||
inputSection(c, "Variants", func() {
|
||||
ui.Row(c).Gap(12).Wrap().Children(func() {
|
||||
for _, v := range inputVariants {
|
||||
if mujica.Button(c, v.Label, mujica.ButtonOptions{Variant: v.Value}).Clicked() {
|
||||
if input.Button(c, v.Label, input.ButtonOptions{Variant: v.Value}).Clicked() {
|
||||
log(v.Label + " clicked")
|
||||
}
|
||||
}
|
||||
mujica.Button(c, "Loading", mujica.ButtonOptions{Loading: true})
|
||||
mujica.Button(c, "Disabled", mujica.ButtonOptions{Disabled: true})
|
||||
input.Button(c, "Loading", input.ButtonOptions{Loading: true})
|
||||
input.Button(c, "Disabled", input.ButtonOptions{Disabled: true})
|
||||
})
|
||||
})
|
||||
}},
|
||||
{ID: "022", Name: "IconButton", ZhName: "图标按钮", Category: inputCategory, Keys: "Tab 聚焦;Enter 或 Space 触发;悬停显示提示。",
|
||||
Code: `if mujica.IconButton(c, icons.Must("settings"), "Settings", mujica.IconButtonOptions{Variant: mujica.Ghost}).Clicked() {
|
||||
Code: `if input.IconButton(c, icons.Must("settings"), "Settings", input.IconButtonOptions{Variant: input.Ghost}).Clicked() {
|
||||
fmt.Println("open settings")
|
||||
}`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "022", func() inputButtonDemo { return inputButtonDemo{variant: mujica.Ghost} })
|
||||
s := ui.Local(c.Root(), "022", func() inputButtonDemo { return inputButtonDemo{variant: input.Ghost} })
|
||||
inputSection(c, "Parameters", func() {
|
||||
mujica.RadioGroup(c, &s.variant, inputVariants, mujica.RadioGroupOptions{Horizontal: true, Label: "Variant"})
|
||||
input.RadioGroup(c, &s.variant, inputVariants, input.RadioGroupOptions{Horizontal: true, Label: "Variant"})
|
||||
inputFlags(c, log, map[string]*bool{"Loading": &s.loading, "Disabled": &s.disabled}, "Loading", "Disabled")
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
ui.Row(c).Gap(8).Children(func() {
|
||||
for _, n := range []string{"bold", "italic", "underline", "copy", "trash-2"} {
|
||||
opts := mujica.IconButtonOptions{Variant: s.variant, Loading: s.loading, Disabled: s.disabled}
|
||||
if mujica.IconButton(c, icons.Must(n), n, opts).Clicked() {
|
||||
opts := input.IconButtonOptions{Variant: s.variant, Loading: s.loading, Disabled: s.disabled}
|
||||
if input.IconButton(c, icons.Must(n), n, opts).Clicked() {
|
||||
log("IconButton " + n + " clicked")
|
||||
}
|
||||
}
|
||||
@@ -135,8 +136,8 @@ var inputPages = []page{
|
||||
})
|
||||
}},
|
||||
{ID: "023", Name: "ButtonGroup", ZhName: "按钮组", Category: inputCategory, Keys: "Tab 进入组;方向键在组内移动并跳过禁用项;Enter 或 Space 触发。",
|
||||
Code: `items := []mujica.ButtonGroupItem{{Label: "Day"}, {Label: "Week"}, {Label: "Month"}}
|
||||
if i := mujica.ButtonGroup(c, items, mujica.ButtonGroupOptions{Label: "Range"}).ClickedItem(); i >= 0 {
|
||||
Code: `items := []input.ButtonGroupItem{{Label: "Day"}, {Label: "Week"}, {Label: "Month"}}
|
||||
if i := input.ButtonGroup(c, items, input.ButtonGroupOptions{Label: "Range"}).ClickedItem(); i >= 0 {
|
||||
fmt.Println("chose", items[i].Label)
|
||||
}`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
@@ -145,15 +146,15 @@ if i := mujica.ButtonGroup(c, items, mujica.ButtonGroupOptions{Label: "Range"}).
|
||||
inputFlags(c, log, map[string]*bool{"Vertical": &s.vertical, "Disable group": &s.disabled}, "Vertical", "Disable group")
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
items := []mujica.ButtonGroupItem{{Label: "Day"}, {Label: "Week", Disabled: true}, {Label: "Month"}, {Icon: icons.Must("calendar"), Name: "Pick a date"}}
|
||||
r := mujica.ButtonGroup(c, items, mujica.ButtonGroupOptions{Vertical: s.vertical, Disabled: s.disabled, Label: "Range"})
|
||||
items := []input.ButtonGroupItem{{Label: "Day"}, {Label: "Week", Disabled: true}, {Label: "Month"}, {Icon: icons.Must("calendar"), Name: "Pick a date"}}
|
||||
r := input.ButtonGroup(c, items, input.ButtonGroupOptions{Vertical: s.vertical, Disabled: s.disabled, Label: "Range"})
|
||||
if i := r.ClickedItem(); i >= 0 {
|
||||
log(fmt.Sprintf("ButtonGroup item %d clicked", i))
|
||||
}
|
||||
})
|
||||
}},
|
||||
{ID: "024", Name: "TextInput", ZhName: "文本输入框", Category: inputCategory, Keys: "Tab 聚焦;Enter 提交;Escape 清空(可清空时);Cmd/Ctrl+Z 撤销、+C/X/V 剪贴板;支持输入法组合。",
|
||||
Code: `r := mujica.TextInput(c, &name, mujica.TextInputOptions{Label: "Name", Placeholder: "Your name", Clearable: true})
|
||||
Code: `r := input.TextInput(c, &name, input.TextInputOptions{Label: "Name", Placeholder: "Your name", Clearable: true})
|
||||
if r.Submitted() {
|
||||
fmt.Println("hello", name)
|
||||
}`,
|
||||
@@ -161,15 +162,15 @@ if r.Submitted() {
|
||||
s := ui.Local(c.Root(), "024", func() inputTextDemo { return inputTextDemo{clearable: true, sl: true} })
|
||||
inputSection(c, "Parameters", func() {
|
||||
inputFlags(c, log, map[string]*bool{"Clearable": &s.clearable, "Slots": &s.sl, "ReadOnly": &s.readOnly, "Disabled": &s.disabled}, "Clearable", "Slots", "ReadOnly", "Disabled")
|
||||
mujica.TextInput(c, &s.err, mujica.TextInputOptions{Label: "Error message", Placeholder: "Error message (empty for none)"})
|
||||
input.TextInput(c, &s.err, input.TextInputOptions{Label: "Error message", Placeholder: "Error message (empty for none)"})
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
opts := mujica.TextInputOptions{Label: "Name", Placeholder: "Your name", Clearable: s.clearable, ReadOnly: s.readOnly, Disabled: s.disabled, Error: s.err}
|
||||
opts := input.TextInputOptions{Label: "Name", Placeholder: "Your name", Clearable: s.clearable, ReadOnly: s.readOnly, Disabled: s.disabled, Error: s.err}
|
||||
if s.sl {
|
||||
opts.Leading = func() { ui.Icon(c, icons.Must("user")).Size(16, 16).TextColor(mujica.Tokens(c).TextMuted) }
|
||||
opts.Trailing = func() { ui.Text(c, fmt.Sprintf("%d", len([]rune(s.value)))).TextColor(mujica.Tokens(c).TextMuted) }
|
||||
opts.Leading = func() { ui.Icon(c, icons.Must("user")).Size(16, 16).TextColor(core.Tokens(c).TextMuted) }
|
||||
opts.Trailing = func() { ui.Text(c, fmt.Sprintf("%d", len([]rune(s.value)))).TextColor(core.Tokens(c).TextMuted) }
|
||||
}
|
||||
r := mujica.TextInput(c, &s.value, opts)
|
||||
r := input.TextInput(c, &s.value, opts)
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("TextInput changed: %q", s.value))
|
||||
}
|
||||
@@ -180,14 +181,14 @@ if r.Submitted() {
|
||||
inputSection(c, "States", func() {
|
||||
ui.Row(c).Gap(12).Children(func() {
|
||||
e, ro, off := "1789-13-01", "Read only", "Disabled"
|
||||
mujica.TextInput(c, &e, mujica.TextInputOptions{Label: "Error state", Error: "No such date"}).Element.Grow(1)
|
||||
mujica.TextInput(c, &ro, mujica.TextInputOptions{Label: "Read-only state", ReadOnly: true}).Element.Grow(1)
|
||||
mujica.TextInput(c, &off, mujica.TextInputOptions{Label: "Disabled state", Disabled: true}).Element.Grow(1)
|
||||
input.TextInput(c, &e, input.TextInputOptions{Label: "Error state", Error: "No such date"}).Element.Grow(1)
|
||||
input.TextInput(c, &ro, input.TextInputOptions{Label: "Read-only state", ReadOnly: true}).Element.Grow(1)
|
||||
input.TextInput(c, &off, input.TextInputOptions{Label: "Disabled state", Disabled: true}).Element.Grow(1)
|
||||
})
|
||||
})
|
||||
}},
|
||||
{ID: "025", Name: "TextArea", ZhName: "多行文本框", Category: inputCategory, Keys: "Enter 换行;Cmd/Ctrl+Enter 提交(可配置);滚轮与方向键滚动;撤销、剪贴板与输入法同 TextInput。",
|
||||
Code: `r := mujica.TextArea(c, ¬es, mujica.TextAreaOptions{Label: "Notes", MaxHeight: 160, MaxLength: 200})
|
||||
Code: `r := input.TextArea(c, ¬es, input.TextAreaOptions{Label: "Notes", MaxHeight: 160, MaxLength: 200})
|
||||
if r.Submitted() {
|
||||
fmt.Println("send", notes)
|
||||
}`,
|
||||
@@ -195,12 +196,12 @@ if r.Submitted() {
|
||||
s := ui.Local(c.Root(), "025", func() inputAreaDemo { return inputAreaDemo{max: 120} })
|
||||
inputSection(c, "Parameters", func() {
|
||||
inputFlags(c, log, map[string]*bool{"ReadOnly": &s.readOnly, "Disabled": &s.disabled}, "ReadOnly", "Disabled")
|
||||
if mujica.NumberInput(c, &s.max, mujica.NumberInputOptions{Min: 0, Max: 1000, Step: 10, Label: "MaxLength"}).Changed() {
|
||||
if input.NumberInput(c, &s.max, input.NumberInputOptions{Min: 0, Max: 1000, Step: 10, Label: "MaxLength"}).Changed() {
|
||||
log(fmt.Sprintf("option MaxLength = %v", s.max))
|
||||
}
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
r := mujica.TextArea(c, &s.value, mujica.TextAreaOptions{Label: "Notes", Placeholder: "Write a dedication…", MinHeight: 80, MaxHeight: 160,
|
||||
r := input.TextArea(c, &s.value, input.TextAreaOptions{Label: "Notes", Placeholder: "Write a dedication…", MinHeight: 80, MaxHeight: 160,
|
||||
MaxLength: int(s.max), ReadOnly: s.readOnly, Disabled: s.disabled})
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("TextArea changed: %d characters", len([]rune(s.value))))
|
||||
@@ -211,15 +212,15 @@ if r.Submitted() {
|
||||
})
|
||||
}},
|
||||
{ID: "026", Name: "NumberInput", ZhName: "数字输入框", Category: inputCategory, Keys: "Up/Down 按步长增减;步进模式下 Up/Down/Left/Right、Home、End。",
|
||||
Code: `if mujica.NumberInput(c, &copies, mujica.NumberInputOptions{Min: 1, Max: 99, Label: "Copies"}).Changed() {
|
||||
Code: `if input.NumberInput(c, &copies, input.NumberInputOptions{Min: 1, Max: 99, Label: "Copies"}).Changed() {
|
||||
fmt.Println("copies", copies)
|
||||
}`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "026", func() inputNumberDemo { return inputNumberDemo{value: 3, step: 1} })
|
||||
inputSection(c, "Parameters", func() {
|
||||
inputFlags(c, log, map[string]*bool{"AllowEmpty": &s.empty, "StepperOnly": &s.stepper, "Disabled": &s.disabled, "Error": &s.fail}, "AllowEmpty", "StepperOnly", "Disabled", "Error")
|
||||
steps := []mujica.RadioItem[float64]{{Value: 1, Label: "Step 1"}, {Value: 0.5, Label: "Step 0.5"}, {Value: 0.01, Label: "Step 0.01"}}
|
||||
if mujica.RadioGroup(c, &s.step, steps, mujica.RadioGroupOptions{Horizontal: true, Label: "Step"}).Changed() {
|
||||
steps := []input.RadioItem[float64]{{Value: 1, Label: "Step 1"}, {Value: 0.5, Label: "Step 0.5"}, {Value: 0.01, Label: "Step 0.01"}}
|
||||
if input.RadioGroup(c, &s.step, steps, input.RadioGroupOptions{Horizontal: true, Label: "Step"}).Changed() {
|
||||
log(fmt.Sprintf("option Step = %v", s.step))
|
||||
}
|
||||
})
|
||||
@@ -230,12 +231,12 @@ if r.Submitted() {
|
||||
s.value = 0
|
||||
}
|
||||
inputSection(c, "Live", func() {
|
||||
opts := mujica.NumberInputOptions{Min: 0, Max: 10, Step: s.step, AllowEmpty: s.empty, StepperOnly: s.stepper, Disabled: s.disabled, Label: "Quantity", Placeholder: "Empty"}
|
||||
opts := input.NumberInputOptions{Min: 0, Max: 10, Step: s.step, AllowEmpty: s.empty, StepperOnly: s.stepper, Disabled: s.disabled, Label: "Quantity", Placeholder: "Empty"}
|
||||
if s.fail {
|
||||
opts.Error = "Not in stock"
|
||||
}
|
||||
ui.Box(c).Width(240).Children(func() {
|
||||
if mujica.NumberInput(c, &s.value, opts).Changed() {
|
||||
if input.NumberInput(c, &s.value, opts).Changed() {
|
||||
log(fmt.Sprintf("NumberInput changed: %v", s.value))
|
||||
}
|
||||
})
|
||||
@@ -243,14 +244,14 @@ if r.Submitted() {
|
||||
inputSection(c, "States", func() {
|
||||
ui.Row(c).Gap(12).AlignItems(ui.Start).Children(func() {
|
||||
a, b, e := 4.0, 2.0, 7.5
|
||||
mujica.NumberInput(c, &a, mujica.NumberInputOptions{Min: 1, Max: 8, StepperOnly: true, Label: "Stepper state"})
|
||||
mujica.NumberInput(c, &b, mujica.NumberInputOptions{Label: "Disabled state", Disabled: true}).Element.Width(160)
|
||||
mujica.NumberInput(c, &e, mujica.NumberInputOptions{Step: 0.5, Label: "Error state", Error: "Must be whole"}).Element.Width(160)
|
||||
input.NumberInput(c, &a, input.NumberInputOptions{Min: 1, Max: 8, StepperOnly: true, Label: "Stepper state"})
|
||||
input.NumberInput(c, &b, input.NumberInputOptions{Label: "Disabled state", Disabled: true}).Element.Width(160)
|
||||
input.NumberInput(c, &e, input.NumberInputOptions{Step: 0.5, Label: "Error state", Error: "Must be whole"}).Element.Width(160)
|
||||
})
|
||||
})
|
||||
}},
|
||||
{ID: "027", Name: "PasswordInput", ZhName: "密码输入框", Category: inputCategory, Keys: "Tab 依次聚焦输入框与显隐按钮;Space 或 Enter 切换显隐;Enter 在输入框中提交;粘贴可用,遮蔽时不可复制。",
|
||||
Code: `if mujica.PasswordInput(c, &password, mujica.PasswordInputOptions{Label: "Password"}).Submitted() {
|
||||
Code: `if input.PasswordInput(c, &password, input.PasswordInputOptions{Label: "Password"}).Submitted() {
|
||||
fmt.Println("signing in")
|
||||
}`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
@@ -259,11 +260,11 @@ if r.Submitted() {
|
||||
inputFlags(c, log, map[string]*bool{"Disabled": &s.disabled, "Error": &s.fail}, "Disabled", "Error")
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
opts := mujica.PasswordInputOptions{Label: "Password", Placeholder: "Password", Disabled: s.disabled}
|
||||
opts := input.PasswordInputOptions{Label: "Password", Placeholder: "Password", Disabled: s.disabled}
|
||||
if s.fail {
|
||||
opts.Error = "At least 8 characters"
|
||||
}
|
||||
r := mujica.PasswordInput(c, &s.value, opts)
|
||||
r := input.PasswordInput(c, &s.value, opts)
|
||||
if r.Changed() {
|
||||
// The contents never reach the log.
|
||||
log(fmt.Sprintf("PasswordInput changed: %d characters", len([]rune(s.value))))
|
||||
@@ -278,7 +279,7 @@ if r.Submitted() {
|
||||
if query != "" {
|
||||
matches = strings.Count(text, query)
|
||||
}
|
||||
r := mujica.SearchInput(c, &query, mujica.SearchInputOptions{Find: true, Matches: matches, Current: ¤t})
|
||||
r := input.SearchInput(c, &query, input.SearchInputOptions{Find: true, Matches: matches, Current: ¤t})
|
||||
if r.Moved() {
|
||||
fmt.Println("show match", current)
|
||||
}`,
|
||||
@@ -289,7 +290,7 @@ if r.Moved() {
|
||||
inputFlags(c, log, map[string]*bool{"Disabled": &s.disabled}, "Disabled")
|
||||
})
|
||||
inputSection(c, "Search mode", func() {
|
||||
r := mujica.SearchInput(c, &s.query, mujica.SearchInputOptions{Disabled: s.disabled})
|
||||
r := input.SearchInput(c, &s.query, input.SearchInputOptions{Disabled: s.disabled})
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("SearchInput changed: %q", s.query))
|
||||
}
|
||||
@@ -302,45 +303,45 @@ if r.Moved() {
|
||||
if s.find != "" {
|
||||
n = strings.Count(text, s.find)
|
||||
}
|
||||
r := mujica.SearchInput(c, &s.find, mujica.SearchInputOptions{Find: true, Matches: n, Current: &s.current, Disabled: s.disabled})
|
||||
r := input.SearchInput(c, &s.find, input.SearchInputOptions{Find: true, Matches: n, Current: &s.current, Disabled: s.disabled})
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("find query: %q, %d matches", s.find, n))
|
||||
}
|
||||
if r.Moved() {
|
||||
log(fmt.Sprintf("find moved to match %d", s.current+1))
|
||||
}
|
||||
ui.Text(c, text).TextColor(mujica.Tokens(c).TextMuted)
|
||||
ui.Text(c, text).TextColor(core.Tokens(c).TextMuted)
|
||||
})
|
||||
}},
|
||||
{ID: "029", Name: "Checkbox", ZhName: "复选框", Category: inputCategory, Keys: "Tab 聚焦;Space 切换;点击标签同样切换;全选框在组内勾选全部或清空。",
|
||||
Code: `mujica.CheckboxGroup(c, "Notifications", []mujica.CheckboxItem{
|
||||
Code: `input.CheckboxGroup(c, "Notifications", []input.CheckboxItem{
|
||||
{Label: "Mail", Value: &mail},
|
||||
{Label: "Calendar", Value: &calendar},
|
||||
}, mujica.CheckboxGroupOptions{})`,
|
||||
}, input.CheckboxGroupOptions{})`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "029", func() inputCheckDemo { return inputCheckDemo{items: [4]bool{true, false, false, true}} })
|
||||
inputSection(c, "Parameters", func() {
|
||||
inputFlags(c, log, map[string]*bool{"Indeterminate": &s.mixed, "Disabled": &s.disabled}, "Indeterminate", "Disabled")
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
if mujica.Checkbox(c, &s.single, "Accept the terms", mujica.CheckboxOptions{Indeterminate: s.mixed, Disabled: s.disabled}).Changed() {
|
||||
if input.Checkbox(c, &s.single, "Accept the terms", input.CheckboxOptions{Indeterminate: s.mixed, Disabled: s.disabled}).Changed() {
|
||||
s.mixed = false
|
||||
log(fmt.Sprintf("Checkbox changed: %v", s.single))
|
||||
}
|
||||
})
|
||||
inputSection(c, "Group with select all", func() {
|
||||
items := []mujica.CheckboxItem{
|
||||
items := []input.CheckboxItem{
|
||||
{Label: "Mail", Value: &s.items[0]}, {Label: "Calendar", Value: &s.items[1]},
|
||||
{Label: "Messages", Value: &s.items[2]}, {Label: "Security alerts (required)", Value: &s.items[3], Disabled: true},
|
||||
}
|
||||
if mujica.CheckboxGroup(c, "Notifications", items, mujica.CheckboxGroupOptions{Disabled: s.disabled}).Changed() {
|
||||
if input.CheckboxGroup(c, "Notifications", items, input.CheckboxGroupOptions{Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("CheckboxGroup changed: %v", s.items))
|
||||
}
|
||||
})
|
||||
}},
|
||||
{ID: "030", Name: "RadioGroup", ZhName: "单选组", Category: inputCategory, Keys: "Tab 进入组并落在已选项;方向键选择并跳过禁用项;Home、End 选择首末项。",
|
||||
Code: `items := []mujica.RadioItem[string]{{Value: "s", Label: "Small"}, {Value: "m", Label: "Medium"}, {Value: "l", Label: "Large"}}
|
||||
if mujica.RadioGroup(c, &size, items, mujica.RadioGroupOptions{Label: "Size"}).Changed() {
|
||||
Code: `items := []input.RadioItem[string]{{Value: "s", Label: "Small"}, {Value: "m", Label: "Medium"}, {Value: "l", Label: "Large"}}
|
||||
if input.RadioGroup(c, &size, items, input.RadioGroupOptions{Label: "Size"}).Changed() {
|
||||
fmt.Println("size", size)
|
||||
}`,
|
||||
Demo: func(c *ui.Context, log func(string)) {
|
||||
@@ -349,8 +350,8 @@ if mujica.RadioGroup(c, &size, items, mujica.RadioGroupOptions{Label: "Size"}).C
|
||||
inputFlags(c, log, map[string]*bool{"Horizontal": &s.horizontal, "Disabled": &s.disabledAll}, "Horizontal", "Disabled")
|
||||
})
|
||||
inputSection(c, "Live", func() {
|
||||
items := []mujica.RadioItem[string]{{Value: "s", Label: "Small"}, {Value: "m", Label: "Medium"}, {Value: "l", Label: "Large"}, {Value: "xl", Label: "Sold out", Disabled: true}}
|
||||
if mujica.RadioGroup(c, &s.value, items, mujica.RadioGroupOptions{Horizontal: s.horizontal, Disabled: s.disabledAll, Label: "Size"}).Changed() {
|
||||
items := []input.RadioItem[string]{{Value: "s", Label: "Small"}, {Value: "m", Label: "Medium"}, {Value: "l", Label: "Large"}, {Value: "xl", Label: "Sold out", Disabled: true}}
|
||||
if input.RadioGroup(c, &s.value, items, input.RadioGroupOptions{Horizontal: s.horizontal, Disabled: s.disabledAll, Label: "Size"}).Changed() {
|
||||
log("RadioGroup chose " + s.value)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -7,8 +7,10 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/layout"
|
||||
)
|
||||
|
||||
var layoutPages = []page{
|
||||
@@ -67,16 +69,16 @@ func layoutSlider(c *ui.Context, v *float64, label string, lo, hi float64, log f
|
||||
func layoutControls(c *ui.Context, fn func()) { ui.Row(c).Wrap().Gap(12).Children(fn) }
|
||||
|
||||
func layoutFrame(c *ui.Context, h float32, fn func()) *ui.Element {
|
||||
return ui.Column(c).Height(h).Border(1, mujica.Tokens(c).Border).Radius(4).Clip().Children(fn)
|
||||
return ui.Column(c).Height(h).Border(1, core.Tokens(c).Border).Radius(4).Clip().Children(fn)
|
||||
}
|
||||
|
||||
var layoutSpaces = []string{"Default", "None", "XS", "S", "M", "L", "XL", "XXL"}
|
||||
|
||||
func layoutSpace(s string) mujica.Space { return mujica.Space(slices.Index(layoutSpaces, s)) }
|
||||
func layoutSpace(s string) layout.Space { return layout.Space(slices.Index(layoutSpaces, s)) }
|
||||
|
||||
const layoutAppShellCode = `var collapsed bool
|
||||
mujica.AppShell(c, &collapsed, mujica.AppShellOptions{
|
||||
TitleBar: func() { mujica.TitleBar(c, "Court Records", mujica.TitleBarOptions{}) },
|
||||
layout.AppShell(c, &collapsed, layout.AppShellOptions{
|
||||
TitleBar: func() { layout.TitleBar(c, "Court Records", layout.TitleBarOptions{}) },
|
||||
Sidebar: func() { ui.Text(c, "Chronicles") },
|
||||
Content: func() { ui.Text(c, "The Grand Hall") },
|
||||
})`
|
||||
@@ -94,7 +96,7 @@ func layoutAppShellDemo(c *ui.Context, log func(string)) {
|
||||
layoutCheck(c, &s.collapsed, "Collapsed", log)
|
||||
layoutSlider(c, &s.width, "Sidebar width", 160, 320, log)
|
||||
})
|
||||
opts := mujica.AppShellOptions{
|
||||
opts := layout.AppShellOptions{
|
||||
SidebarWidth: float32(s.width),
|
||||
ContentScroll: &s.scroll,
|
||||
Sidebar: func() {
|
||||
@@ -103,7 +105,7 @@ func layoutAppShellDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
},
|
||||
Content: func() {
|
||||
mujica.Container(c, mujica.ContainerOptions{}, func() {
|
||||
layout.Container(c, layout.ContainerOptions{}, func() {
|
||||
for i := range 30 {
|
||||
ui.Text(c, fmt.Sprintf("Entry %d of the court record", i+1)).Padding(4, 0)
|
||||
}
|
||||
@@ -111,24 +113,24 @@ func layoutAppShellDemo(c *ui.Context, log func(string)) {
|
||||
},
|
||||
}
|
||||
if s.title {
|
||||
opts.TitleBar = func() { mujica.TitleBar(c, "Court Records", mujica.TitleBarOptions{}) }
|
||||
opts.TitleBar = func() { layout.TitleBar(c, "Court Records", layout.TitleBarOptions{}) }
|
||||
}
|
||||
if s.status {
|
||||
opts.StatusBar = func() {
|
||||
mujica.StatusBar(c, mujica.StatusBarOptions{Left: []mujica.StatusItem{{ID: "pos", Text: fmt.Sprintf("Scrolled %.0f", s.scroll.Y)}}})
|
||||
layout.StatusBar(c, layout.StatusBarOptions{Left: []layout.StatusItem{{ID: "pos", Text: fmt.Sprintf("Scrolled %.0f", s.scroll.Y)}}})
|
||||
}
|
||||
}
|
||||
ui.Column(c).Margin(16, 0, 0, 0).Children(func() {
|
||||
layoutFrame(c, 360, func() {
|
||||
if mujica.AppShell(c, &s.collapsed, opts).Toggled {
|
||||
if layout.AppShell(c, &s.collapsed, opts).Toggled {
|
||||
log(fmt.Sprintf("sidebar collapsed: %v", s.collapsed))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const layoutTitleBarCode = `mujica.TitleBar(c, "Court Records", mujica.TitleBarOptions{
|
||||
Trailing: func() { mujica.Button(c, "Share", mujica.ButtonOptions{Variant: mujica.Ghost}) },
|
||||
const layoutTitleBarCode = `layout.TitleBar(c, "Court Records", layout.TitleBarOptions{
|
||||
Trailing: func() { input.Button(c, "Share", input.ButtonOptions{Variant: input.Ghost}) },
|
||||
})`
|
||||
|
||||
func layoutTitleBarDemo(c *ui.Context, log func(string)) {
|
||||
@@ -142,32 +144,32 @@ func layoutTitleBarDemo(c *ui.Context, log func(string)) {
|
||||
layoutCheck(c, &s.trailing, "Trailing tool", log)
|
||||
layoutChoice(c, &s.title, "Title", []string{"Court Records", "宫廷档案", "A very long title of the royal chronicle of the court"}, log)
|
||||
})
|
||||
var opts mujica.TitleBarOptions
|
||||
var opts layout.TitleBarOptions
|
||||
if s.leading {
|
||||
opts.Leading = func() {
|
||||
if mujica.Button(c, "", mujica.ButtonOptions{Variant: mujica.Ghost, Icon: icons.Must("panel-left"), Label: "Sidebar"}).Clicked() {
|
||||
if input.Button(c, "", input.ButtonOptions{Variant: input.Ghost, Icon: icons.Must("panel-left"), Label: "Sidebar"}).Clicked() {
|
||||
log("leading tool clicked")
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.trailing {
|
||||
opts.Trailing = func() {
|
||||
if mujica.Button(c, "Share", mujica.ButtonOptions{Variant: mujica.Ghost}).Clicked() {
|
||||
if input.Button(c, "Share", input.ButtonOptions{Variant: input.Ghost}).Clicked() {
|
||||
log("Share clicked")
|
||||
}
|
||||
}
|
||||
}
|
||||
ui.Column(c).Margin(16, 0, 0, 0).Width(520).Children(func() {
|
||||
layoutFrame(c, 90, func() { mujica.TitleBar(c, s.title, opts) })
|
||||
layoutFrame(c, 90, func() { layout.TitleBar(c, s.title, opts) })
|
||||
})
|
||||
bar := c.TitleBar()
|
||||
ui.Text(c, fmt.Sprintf("Window controls reserve: left %.0f, right %.0f, height %.0f DIP", bar.Left, bar.Right, bar.Height)).
|
||||
TextColor(mujica.Tokens(c).TextMuted).Margin(8, 0, 0, 0)
|
||||
TextColor(core.Tokens(c).TextMuted).Margin(8, 0, 0, 0)
|
||||
}
|
||||
|
||||
const layoutStatusBarCode = `bar := mujica.StatusBar(c, mujica.StatusBarOptions{
|
||||
Left: []mujica.StatusItem{{ID: "sync", Text: "Syncing", ShowProgress: true, Progress: 0.4}},
|
||||
Right: []mujica.StatusItem{{ID: "bell", Label: "Notifications", Icon: icons.Must("bell"), Action: true}},
|
||||
const layoutStatusBarCode = `bar := layout.StatusBar(c, layout.StatusBarOptions{
|
||||
Left: []layout.StatusItem{{ID: "sync", Text: "Syncing", ShowProgress: true, Progress: 0.4}},
|
||||
Right: []layout.StatusItem{{ID: "bell", Label: "Notifications", Icon: icons.Must("bell"), Action: true}},
|
||||
})
|
||||
if bar.Clicked == "bell" {
|
||||
opened++
|
||||
@@ -186,30 +188,30 @@ func layoutStatusBarDemo(c *ui.Context, log func(string)) {
|
||||
layoutSlider(c, &s.progress, "Percent", 0, 100, log)
|
||||
layoutChoice(c, &s.tone, "Tone", []string{"Neutral", "Success", "Warning", "Danger", "Info"}, log)
|
||||
})
|
||||
tones := map[string]mujica.StatusItemTone{"Neutral": mujica.StatusItemNeutral, "Success": mujica.StatusItemSuccess,
|
||||
"Warning": mujica.StatusItemWarning, "Danger": mujica.StatusItemDanger, "Info": mujica.StatusItemInfo}
|
||||
opts := mujica.StatusBarOptions{
|
||||
Left: []mujica.StatusItem{
|
||||
tones := map[string]layout.StatusItemTone{"Neutral": layout.StatusItemNeutral, "Success": layout.StatusItemSuccess,
|
||||
"Warning": layout.StatusItemWarning, "Danger": layout.StatusItemDanger, "Info": layout.StatusItemInfo}
|
||||
opts := layout.StatusBarOptions{
|
||||
Left: []layout.StatusItem{
|
||||
{ID: "branch", Text: "main", Icon: icons.Must("folder")},
|
||||
{ID: "sync", Text: "Syncing", ShowProgress: s.showProgress, Progress: float32(s.progress / 100)},
|
||||
{ID: "lint", Text: "2 warnings", Tone: tones[s.tone], Secondary: true, Action: true},
|
||||
},
|
||||
Right: []mujica.StatusItem{
|
||||
Right: []layout.StatusItem{
|
||||
{ID: "enc", Text: "UTF-8", Secondary: true},
|
||||
{ID: "pos", Text: "Ln 12, Col 4", Secondary: true},
|
||||
{ID: "bell", Label: "Notifications", Icon: icons.Must("bell"), Action: true},
|
||||
},
|
||||
}
|
||||
ui.Column(c).Margin(16, 0, 0, 0).Width(float32(s.width)).Children(func() {
|
||||
if id := mujica.StatusBar(c, opts).Clicked; id != "" {
|
||||
if id := layout.StatusBar(c, opts).Clicked; id != "" {
|
||||
log("clicked " + id)
|
||||
}
|
||||
})
|
||||
ui.Text(c, "Narrow the width: secondary items fold into a count, last first.").
|
||||
TextColor(mujica.Tokens(c).TextMuted).Margin(8, 0, 0, 0)
|
||||
TextColor(core.Tokens(c).TextMuted).Margin(8, 0, 0, 0)
|
||||
}
|
||||
|
||||
const layoutContainerCode = `mujica.Container(c, mujica.ContainerOptions{MaxWidth: 320, Center: true}, func() {
|
||||
const layoutContainerCode = `layout.Container(c, layout.ContainerOptions{MaxWidth: 320, Center: true}, func() {
|
||||
ui.Text(c, "A centered column of prose.")
|
||||
})`
|
||||
|
||||
@@ -228,10 +230,10 @@ func layoutContainerDemo(c *ui.Context, log func(string)) {
|
||||
layoutCheck(c, &s.clip, "Clip", log)
|
||||
layoutCheck(c, &s.wide, "Overflowing child", log)
|
||||
})
|
||||
k := mujica.Tokens(c)
|
||||
k := core.Tokens(c)
|
||||
ui.Column(c).Margin(16, 0, 0, 0).Children(func() {
|
||||
layoutFrame(c, 200, func() {
|
||||
mujica.Container(c, mujica.ContainerOptions{MaxWidth: float32(s.maxWidth), Padding: layoutSpace(s.padding),
|
||||
layout.Container(c, layout.ContainerOptions{MaxWidth: float32(s.maxWidth), Padding: layoutSpace(s.padding),
|
||||
Center: s.center, Fill: s.fill, Clip: s.clip}, func() {
|
||||
ui.Text(c, "Of the Court").FontSize(18)
|
||||
ui.Text(c, "Prose keeps a readable measure inside a container.").TextColor(k.TextMuted)
|
||||
@@ -243,12 +245,12 @@ func layoutContainerDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
}
|
||||
|
||||
const layoutStackCode = `mujica.Stack(c, mujica.StackOptions{Horizontal: true, Gap: mujica.SpaceM}, func() {
|
||||
mujica.Button(c, "Save", mujica.ButtonOptions{})
|
||||
const layoutStackCode = `layout.Stack(c, layout.StackOptions{Horizontal: true, Gap: layout.SpaceM}, func() {
|
||||
input.Button(c, "Save", input.ButtonOptions{})
|
||||
if showDraft {
|
||||
mujica.Button(c, "Draft", mujica.ButtonOptions{Variant: mujica.Secondary})
|
||||
input.Button(c, "Draft", input.ButtonOptions{Variant: input.Secondary})
|
||||
}
|
||||
mujica.Button(c, "Cancel", mujica.ButtonOptions{Variant: mujica.Ghost})
|
||||
input.Button(c, "Cancel", input.ButtonOptions{Variant: input.Ghost})
|
||||
})`
|
||||
|
||||
func layoutStackDemo(c *ui.Context, log func(string)) {
|
||||
@@ -272,13 +274,13 @@ func layoutStackDemo(c *ui.Context, log func(string)) {
|
||||
layoutChoice(c, &s.align, "Align", aligns, log)
|
||||
layoutChoice(c, &s.justify, "Justify", []string{"Start", "Center", "End", "SpaceBetween"}, log)
|
||||
})
|
||||
align := mujica.StackAlign(slices.Index(aligns, s.align))
|
||||
align := layout.StackAlign(slices.Index(aligns, s.align))
|
||||
ui.Column(c).Margin(16, 0, 0, 0).Width(420).Children(func() {
|
||||
layoutFrame(c, 200, func() {
|
||||
mujica.Stack(c, mujica.StackOptions{Horizontal: s.horizontal, Wrap: s.wrap, Gap: layoutSpace(s.gap),
|
||||
layout.Stack(c, layout.StackOptions{Horizontal: s.horizontal, Wrap: s.wrap, Gap: layoutSpace(s.gap),
|
||||
Align: align, Justify: justifies[s.justify]}, func() {
|
||||
for _, n := range []string{"Save", "Draft", "Publish", "Archive", "Cancel"} {
|
||||
if (!s.hide || n != "Draft") && mujica.Button(c, n, mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if (!s.hide || n != "Draft") && input.Button(c, n, input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
log(n + " clicked")
|
||||
}
|
||||
}
|
||||
@@ -287,8 +289,8 @@ func layoutStackDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
}
|
||||
|
||||
const layoutGridCode = `mujica.Grid(c, mujica.GridOptions{Columns: []ui.Track{ui.Fixed(96), ui.Fr(1)}}, func() {
|
||||
mujica.GridCell(c, mujica.GridCellOptions{ColumnSpan: -1}, func() { ui.Text(c, "Header") })
|
||||
const layoutGridCode = `layout.Grid(c, layout.GridOptions{Columns: []ui.Track{ui.Fixed(96), ui.Fr(1)}}, func() {
|
||||
layout.GridCell(c, layout.GridCellOptions{ColumnSpan: -1}, func() { ui.Text(c, "Header") })
|
||||
ui.Text(c, "Name")
|
||||
ui.Text(c, "Ada")
|
||||
})`
|
||||
@@ -309,22 +311,22 @@ func layoutGridDemo(c *ui.Context, log func(string)) {
|
||||
layoutCheck(c, &s.header, "Header spans all", log)
|
||||
layoutCheck(c, &s.tall, "First cell spans 2 rows", log)
|
||||
})
|
||||
k := mujica.Tokens(c)
|
||||
k := core.Tokens(c)
|
||||
cell := func(text string) {
|
||||
ui.Box(c).Center().Padding(8).Background(k.Surface).Border(1, k.Border).Radius(4).
|
||||
Children(func() { ui.Text(c, text).TextColor(k.TextMuted) })
|
||||
}
|
||||
ui.Column(c).Margin(16, 0, 0, 0).Width(520).Children(func() {
|
||||
mujica.Grid(c, mujica.GridOptions{Columns: []ui.Track{tracks[s.first], ui.Fr(1), ui.Fr(1)},
|
||||
layout.Grid(c, layout.GridOptions{Columns: []ui.Track{tracks[s.first], ui.Fr(1), ui.Fr(1)},
|
||||
Gap: layoutSpace(s.gap), RowGap: layoutSpace(s.rowGap)}, func() {
|
||||
if s.header {
|
||||
mujica.GridCell(c, mujica.GridCellOptions{ColumnSpan: -1}, func() { cell("Header, ColumnSpan -1") })
|
||||
layout.GridCell(c, layout.GridCellOptions{ColumnSpan: -1}, func() { cell("Header, ColumnSpan -1") })
|
||||
}
|
||||
span := 1
|
||||
if s.tall {
|
||||
span = 2
|
||||
}
|
||||
mujica.GridCell(c, mujica.GridCellOptions{RowSpan: span}, func() { cell(s.first) })
|
||||
layout.GridCell(c, layout.GridCellOptions{RowSpan: span}, func() { cell(s.first) })
|
||||
for i := range 4 {
|
||||
cell(fmt.Sprintf("Cell %d", i+1))
|
||||
}
|
||||
@@ -333,7 +335,7 @@ func layoutGridDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
const layoutScrollAreaCode = `var offset ui.ScrollState
|
||||
mujica.ScrollArea(c, &offset, mujica.ScrollAreaOptions{Label: "Log"}, func() {
|
||||
layout.ScrollArea(c, &offset, layout.ScrollAreaOptions{Label: "Log"}, func() {
|
||||
for range 50 {
|
||||
ui.Text(c, "A line of the log")
|
||||
}
|
||||
@@ -346,25 +348,25 @@ func layoutScrollAreaDemo(c *ui.Context, log func(string)) {
|
||||
lastX, lastY float32
|
||||
}
|
||||
s := ui.Local(c.Root(), "007", func() state { return state{direction: "Vertical"} })
|
||||
dirs := map[string]mujica.ScrollDirection{"Vertical": mujica.ScrollVertical, "Horizontal": mujica.ScrollHorizontal, "Both": mujica.ScrollBoth}
|
||||
dirs := map[string]layout.ScrollDirection{"Vertical": layout.ScrollVertical, "Horizontal": layout.ScrollHorizontal, "Both": layout.ScrollBoth}
|
||||
layoutControls(c, func() {
|
||||
layoutChoice(c, &s.direction, "Direction", []string{"Vertical", "Horizontal", "Both"}, log)
|
||||
if mujica.Button(c, "Top", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Top", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.offset.X, s.offset.Y = 0, 0
|
||||
log("scroll to start")
|
||||
}
|
||||
if mujica.Button(c, "Middle", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Middle", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.offset.X, s.offset.Y = s.offset.MaxX/2, s.offset.MaxY/2
|
||||
log("scroll to middle")
|
||||
}
|
||||
if mujica.Button(c, "End", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "End", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.offset.X, s.offset.Y = math.MaxFloat32, math.MaxFloat32
|
||||
log("scroll to end")
|
||||
}
|
||||
})
|
||||
k := mujica.Tokens(c)
|
||||
k := core.Tokens(c)
|
||||
ui.Column(c).Margin(16, 0, 0, 0).Width(420).Children(func() {
|
||||
mujica.ScrollArea(c, &s.offset, mujica.ScrollAreaOptions{Direction: dirs[s.direction], Label: "Ledger"}, func() {
|
||||
layout.ScrollArea(c, &s.offset, layout.ScrollAreaOptions{Direction: dirs[s.direction], Label: "Ledger"}, func() {
|
||||
for i := range 40 {
|
||||
ui.Text(c, fmt.Sprintf("%02d Ledger line of the treasury, folio %d, carried over from the last quarter", i+1, i+7)).
|
||||
SingleLine().Padding(4, 12)
|
||||
@@ -379,30 +381,30 @@ func layoutScrollAreaDemo(c *ui.Context, log func(string)) {
|
||||
TextColor(k.TextMuted).Margin(8, 0, 0, 0)
|
||||
}
|
||||
|
||||
const layoutSplitPaneCode = `split := mujica.SplitPaneState{Size: 160}
|
||||
mujica.SplitPane(c, &split, mujica.SplitPaneOptions{MinFirst: 120, Collapsible: mujica.SplitPaneFirst},
|
||||
const layoutSplitPaneCode = `split := layout.SplitPaneState{Size: 160}
|
||||
layout.SplitPane(c, &split, layout.SplitPaneOptions{MinFirst: 120, Collapsible: layout.SplitPaneFirst},
|
||||
func() { ui.Text(c, "Files") },
|
||||
func() { ui.Text(c, "Editor") },
|
||||
).Fill()`
|
||||
|
||||
func layoutSplitPaneDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
split mujica.SplitPaneState
|
||||
split layout.SplitPaneState
|
||||
vertical bool
|
||||
collapsible string
|
||||
minFirst float64
|
||||
}
|
||||
s := ui.Local(c.Root(), "008", func() state {
|
||||
return state{split: mujica.SplitPaneState{Size: 180}, collapsible: "First", minFirst: 120}
|
||||
return state{split: layout.SplitPaneState{Size: 180}, collapsible: "First", minFirst: 120}
|
||||
})
|
||||
sides := map[string]mujica.SplitPaneSide{"Neither": mujica.SplitPaneNeither, "First": mujica.SplitPaneFirst, "Second": mujica.SplitPaneSecond}
|
||||
sides := map[string]layout.SplitPaneSide{"Neither": layout.SplitPaneNeither, "First": layout.SplitPaneFirst, "Second": layout.SplitPaneSecond}
|
||||
layoutControls(c, func() {
|
||||
layoutCheck(c, &s.vertical, "Vertical", log)
|
||||
layoutChoice(c, &s.collapsible, "Collapsible", []string{"Neither", "First", "Second"}, log)
|
||||
layoutSlider(c, &s.minFirst, "Min first", 40, 200, log)
|
||||
})
|
||||
s.split.Collapsed = s.split.Collapsed && s.collapsible != "Neither"
|
||||
k := mujica.Tokens(c)
|
||||
k := core.Tokens(c)
|
||||
pane := func(title, body string, bg ui.Color) func() {
|
||||
return func() {
|
||||
ui.Column(c).Fill().Padding(12).Gap(6).Background(bg).Children(func() {
|
||||
@@ -413,7 +415,7 @@ func layoutSplitPaneDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
ui.Column(c).Margin(16, 0, 0, 0).Width(560).Children(func() {
|
||||
layoutFrame(c, 280, func() {
|
||||
r := mujica.SplitPane(c, &s.split, mujica.SplitPaneOptions{Vertical: s.vertical, MinFirst: float32(s.minFirst),
|
||||
r := layout.SplitPane(c, &s.split, layout.SplitPaneOptions{Vertical: s.vertical, MinFirst: float32(s.minFirst),
|
||||
Collapsible: sides[s.collapsible]},
|
||||
pane("Files", "Drag the divider, or focus it and use the arrows.", k.Surface),
|
||||
pane("Editor", "Enter on the divider folds the collapsible pane.", k.Background))
|
||||
@@ -429,7 +431,7 @@ func layoutSplitPaneDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
const layoutAspectRatioCode = `ui.Box(c).Size(320, 240).Children(func() {
|
||||
mujica.AspectRatio(c, mujica.AspectRatioOptions{Ratio: 16.0 / 9}, func() {
|
||||
layout.AspectRatio(c, layout.AspectRatioOptions{Ratio: 16.0 / 9}, func() {
|
||||
ui.Text(c, "16 : 9")
|
||||
})
|
||||
})`
|
||||
@@ -446,9 +448,9 @@ func layoutAspectRatioDemo(c *ui.Context, log func(string)) {
|
||||
layoutSlider(c, &s.width, "Parent width", 120, 560, log)
|
||||
layoutSlider(c, &s.height, "Parent height", 80, 320, log)
|
||||
})
|
||||
k := mujica.Tokens(c)
|
||||
k := core.Tokens(c)
|
||||
ui.Box(c).Margin(16, 0, 0, 0).Size(float32(s.width), float32(s.height)).Border(1, k.Border).Children(func() {
|
||||
mujica.AspectRatio(c, mujica.AspectRatioOptions{Ratio: ratios[s.ratio]}, func() {
|
||||
layout.AspectRatio(c, layout.AspectRatioOptions{Ratio: ratios[s.ratio]}, func() {
|
||||
ui.Box(c).Fill().Center().Background(k.Surface).Border(1, k.Ornament).Children(func() {
|
||||
ui.Text(c, s.ratio).TextColor(k.TextMuted)
|
||||
})
|
||||
@@ -457,8 +459,8 @@ func layoutAspectRatioDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
|
||||
const layoutDividerCode = `ui.Column(c).Padding(16).Gap(12).Children(func() {
|
||||
mujica.Divider(c, mujica.DividerOptions{Ornament: true})
|
||||
mujica.Divider(c, mujica.DividerOptions{Label: "Chapter II"})
|
||||
layout.Divider(c, layout.DividerOptions{Ornament: true})
|
||||
layout.Divider(c, layout.DividerOptions{Label: "Chapter II"})
|
||||
})`
|
||||
|
||||
func layoutDividerDemo(c *ui.Context, log func(string)) {
|
||||
@@ -478,19 +480,19 @@ func layoutDividerDemo(c *ui.Context, log func(string)) {
|
||||
if label == "None" || s.vertical {
|
||||
label = ""
|
||||
}
|
||||
opts := mujica.DividerOptions{Vertical: s.vertical, Ornament: s.ornament, Label: label}
|
||||
opts := layout.DividerOptions{Vertical: s.vertical, Ornament: s.ornament, Label: label}
|
||||
ui.Column(c).Margin(16, 0, 0, 0).Width(480).Children(func() {
|
||||
if s.vertical {
|
||||
ui.Row(c).Height(80).Gap(16).Children(func() {
|
||||
ui.Text(c, "Overture")
|
||||
mujica.Divider(c, opts)
|
||||
layout.Divider(c, opts)
|
||||
ui.Text(c, "Aria")
|
||||
})
|
||||
return
|
||||
}
|
||||
ui.Column(c).Gap(16).Children(func() {
|
||||
ui.Text(c, "Overture")
|
||||
mujica.Divider(c, opts)
|
||||
layout.Divider(c, opts)
|
||||
ui.Text(c, "Aria")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
)
|
||||
|
||||
func layoutPageTester(t *testing.T, id string, events *[]string) *ui.Tester {
|
||||
@@ -13,7 +13,7 @@ func layoutPageTester(t *testing.T, id string, events *[]string) *ui.Tester {
|
||||
for _, p := range layoutPages {
|
||||
if p.ID == id {
|
||||
return ui.NewTester(func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
core.Use(c, core.Settings{})
|
||||
ui.Scroll(c).Fill().Children(func() {
|
||||
p.Demo(c, func(s string) { *events = append(*events, s) })
|
||||
})
|
||||
|
||||
@@ -2,65 +2,68 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
var marketsPages = []page{
|
||||
{ID: "181", Name: "OptionChain", ZhName: "期权链", Category: "Markets", Demo: marketsOptionChainDemo, Code: `var state mujica.OptionChainState
|
||||
rows := []mujica.OptionStrike{{Strike: 100, Call: mujica.OptionQuote{Bid: 3, Ask: 3.2, Last: 3.1}, Put: mujica.OptionQuote{Bid: 2, Ask: 2.2, Last: 2.1}}}
|
||||
{ID: "181", Name: "OptionChain", ZhName: "期权链", Category: "Markets", Demo: marketsOptionChainDemo, Code: `var state finance.OptionChainState
|
||||
rows := []finance.OptionStrike{{Strike: 100, Call: finance.OptionQuote{Bid: 3, Ask: 3.2, Last: 3.1}, Put: finance.OptionQuote{Bid: 2, Ask: 2.2, Last: 2.1}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.OptionChain(c, &state, []string{"Oct 17", "Nov 21"}, rows, mujica.OptionChainOptions{Spot: 100.4})
|
||||
core.Use(c, core.Settings{})
|
||||
r := finance.OptionChain(c, &state, []string{"Oct 17", "Nov 21"}, rows, finance.OptionChainOptions{Spot: 100.4})
|
||||
if r.Changed || r.Submitted || r.ExpiryChanged {
|
||||
fmt.Println(state.Expiry, state.Strike, state.Side)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "Tab 切换到期日或列表;上下、Home/End 选择行;左右切换认购认沽;Enter 确认。"},
|
||||
{ID: "182", Name: "GreeksTable", ZhName: "希腊值表", Category: "Markets", Demo: marketsGreeksTableDemo, Code: `rows := []mujica.GreeksRow{{Label: "AAPL 100 C", Greeks: mujica.OptionGreeks{Delta: 0.5, Gamma: 0.03, Theta: -0.1, Vega: 0.2, Rho: 0.05}}}
|
||||
{ID: "182", Name: "GreeksTable", ZhName: "希腊值表", Category: "Markets", Demo: marketsGreeksTableDemo, Code: `rows := []finance.GreeksRow{{Label: "AAPL 100 C", Greeks: finance.OptionGreeks{Delta: 0.5, Gamma: 0.03, Theta: -0.1, Vega: 0.2, Rho: 0.05}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.GreeksTable(c, rows, mujica.GreeksTableOptions{Notes: true})
|
||||
core.Use(c, core.Settings{})
|
||||
finance.GreeksTable(c, rows, finance.GreeksTableOptions{Notes: true})
|
||||
}
|
||||
ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "展示组件;表头提供说明和悬停提示。"},
|
||||
{ID: "183", Name: "PayoffDiagram", ZhName: "到期损益图", Category: "Markets", Demo: marketsPayoffDiagramDemo, Code: `legs := []mujica.OptionLeg{{Kind: mujica.LegCall, Strike: 100, Premium: 3, Quantity: 1}}
|
||||
{ID: "183", Name: "PayoffDiagram", ZhName: "到期损益图", Category: "Markets", Demo: marketsPayoffDiagramDemo, Code: `legs := []finance.OptionLeg{{Kind: finance.LegCall, Strike: 100, Premium: 3, Quantity: 1}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.PayoffDiagram(c, legs, mujica.PayoffDiagramOptions{Frame: mujica.ChartFrame{Label: "Call payoff"}, Spot: 103})
|
||||
core.Use(c, core.Settings{})
|
||||
r := finance.PayoffDiagram(c, legs, finance.PayoffDiagramOptions{Frame: chart.ChartFrame{Label: "Call payoff"}, Spot: 103})
|
||||
if r.Changed() || r.Retried() {
|
||||
fmt.Println(r.Point)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "Tab 聚焦;方向键、Home/End 逐点读数;Escape 清除。"},
|
||||
{ID: "184", Name: "Watchlist", ZhName: "自选列表", Category: "Markets", Demo: marketsWatchlistDemo, Code: `var state mujica.WatchlistState
|
||||
groups := []mujica.WatchGroup{{Name: "Technology", Items: []mujica.WatchItem{{Symbol: "AAPL", Name: "Apple", Price: 227.5, Change: 2.7, ChangePercent: 1.2}}}, {Name: "Energy", Items: []mujica.WatchItem{{Symbol: "XOM", Price: 117.4}}}}
|
||||
{ID: "184", Name: "Watchlist", ZhName: "自选列表", Category: "Markets", Demo: marketsWatchlistDemo, Code: `var state finance.WatchlistState
|
||||
groups := []finance.WatchGroup{{Name: "Technology", Items: []finance.WatchItem{{Symbol: "AAPL", Name: "Apple", Price: 227.5, Change: 2.7, ChangePercent: 1.2}}}, {Name: "Energy", Items: []finance.WatchItem{{Symbol: "XOM", Price: 117.4}}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.Watchlist(c, &state, groups, mujica.WatchlistOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := finance.Watchlist(c, &state, groups, finance.WatchlistOptions{})
|
||||
if r.Changed || r.Submitted || r.Moved {
|
||||
fmt.Println(state.Selected, r.Rows, r.To)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "Tab 切换分组或列菜单;上下、Home/End 选择;Enter 确认;菜单 Space 开关列。"},
|
||||
{ID: "185", Name: "SymbolSearch", ZhName: "代码搜索", Category: "Markets", Demo: marketsSymbolSearchDemo, Code: `var state mujica.SymbolSearchState
|
||||
rows := []mujica.MarketSymbol{{Code: "AAPL", Name: "Apple", Market: "NASDAQ", Type: "Stock"}}
|
||||
{ID: "185", Name: "SymbolSearch", ZhName: "代码搜索", Category: "Markets", Demo: marketsSymbolSearchDemo, Code: `var state finance.SymbolSearchState
|
||||
rows := []finance.MarketSymbol{{Code: "AAPL", Name: "Apple", Market: "NASDAQ", Type: "Stock"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.SymbolSearch(c, &state, rows, mujica.SymbolSearchOptions{Markets: []string{"NASDAQ", "NYSE"}, Recent: rows})
|
||||
core.Use(c, core.Settings{})
|
||||
r := finance.SymbolSearch(c, &state, rows, finance.SymbolSearchOptions{Markets: []string{"NASDAQ", "NYSE"}, Recent: rows})
|
||||
if r.Changed || r.Picked || r.Retry {
|
||||
fmt.Println(state.Query, state.Market, r.Chosen.Code)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "Tab 聚焦输入与市场;Down 进入结果;Tab 到达结果按钮,Enter 选择。"},
|
||||
{ID: "186", Name: "SymbolBadge", ZhName: "代码徽标", Category: "Markets", Demo: marketsSymbolBadgeDemo, Code: `symbol := mujica.MarketSymbol{Code: "AAPL", Name: "Apple", Market: "NASDAQ", Type: "Stock"}
|
||||
{ID: "186", Name: "SymbolBadge", ZhName: "代码徽标", Category: "Markets", Demo: marketsSymbolBadgeDemo, Code: `symbol := finance.MarketSymbol{Code: "AAPL", Name: "Apple", Market: "NASDAQ", Type: "Stock"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.SymbolBadge(c, symbol, mujica.SymbolBadgeOptions{Clickable: true})
|
||||
core.Use(c, core.Settings{})
|
||||
r := finance.SymbolBadge(c, symbol, finance.SymbolBadgeOptions{Clickable: true})
|
||||
if r.Changed() {
|
||||
fmt.Println(symbol.Code)
|
||||
}
|
||||
@@ -68,42 +71,42 @@ view := func(c *ui.Context) {
|
||||
ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "Clickable 开启时 Tab 聚焦,Enter/Space 激活。"},
|
||||
{ID: "187", Name: "MarketHeatmap", ZhName: "市场热力图", Category: "Markets", Demo: marketsMarketHeatmapDemo, Code: `var path []string
|
||||
root := mujica.HeatNode{Name: "Market", Children: []mujica.HeatNode{{Name: "Technology", Children: []mujica.HeatNode{{Name: "AAPL", Cap: 3000, Change: 1.2}, {Name: "MSFT", Cap: 2800, Change: -0.6}}}, {Name: "Energy", Cap: 450, Change: -2.1}}}
|
||||
root := chart.HeatNode{Name: "Market", Children: []chart.HeatNode{{Name: "Technology", Children: []chart.HeatNode{{Name: "AAPL", Cap: 3000, Change: 1.2}, {Name: "MSFT", Cap: 2800, Change: -0.6}}}, {Name: "Energy", Cap: 450, Change: -2.1}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.MarketHeatmap(c, &path, root, mujica.MarketHeatmapOptions{Frame: mujica.ChartFrame{Label: "Market"}})
|
||||
core.Use(c, core.Settings{})
|
||||
r := chart.MarketHeatmap(c, &path, root, chart.MarketHeatmapOptions{Frame: chart.ChartFrame{Label: "Market"}})
|
||||
if r.Changed() || r.ValueChanged() || r.Retried() {
|
||||
fmt.Println(path, r.Point)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "Tab 聚焦图;方向键逐块读数;Enter 下钻;Backspace 返回;路径按钮可用 Tab。"},
|
||||
{ID: "188", Name: "Screener", ZhName: "选股器", Category: "Markets", Demo: marketsScreenerDemo, Code: `var state mujica.ScreenerState
|
||||
fields := []mujica.ScreenerField{{ID: "pe", Label: "P/E", Decimals: 1}}
|
||||
rows := []mujica.ScreenerRow{{Symbol: "AAPL", Values: map[string]float64{"pe": 34}}, {Symbol: "XOM", Values: map[string]float64{"pe": 13}}}
|
||||
{ID: "188", Name: "Screener", ZhName: "选股器", Category: "Markets", Demo: marketsScreenerDemo, Code: `var state finance.ScreenerState
|
||||
fields := []finance.ScreenerField{{ID: "pe", Label: "P/E", Decimals: 1}}
|
||||
rows := []finance.ScreenerRow{{Symbol: "AAPL", Values: map[string]float64{"pe": 34}}, {Symbol: "XOM", Values: map[string]float64{"pe": 13}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.Screener(c, &state, fields, rows, mujica.ScreenerOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := finance.Screener(c, &state, fields, rows, finance.ScreenerOptions{})
|
||||
if r.Changed || r.Save || r.Table.Changed() || r.Table.Submitted() || r.Table.SortChanged() || r.Table.Retried() {
|
||||
fmt.Println(state.Conditions)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "Tab 到达字段、比较、数值和保存;Select 方向键选择;NumberInput 方向键步进;表格方向键选择。"},
|
||||
{ID: "189", Name: "MarketOverview", ZhName: "市场概览", Category: "Markets", Demo: marketsMarketOverviewDemo, Code: `indices := []mujica.IndexQuote{{Name: "S&P 500", Price: 5751.07, Change: 51.13, ChangePercent: 0.9, Series: []float64{5700, 5730, 5720, 5751}}}
|
||||
{ID: "189", Name: "MarketOverview", ZhName: "市场概览", Category: "Markets", Demo: marketsMarketOverviewDemo, Code: `indices := []finance.IndexQuote{{Name: "S&P 500", Price: 5751.07, Change: 51.13, ChangePercent: 0.9, Series: []float64{5700, 5730, 5720, 5751}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.MarketOverview(c, indices, mujica.MarketOverviewOptions{Breadth: mujica.MarketBreadth{Advancing: 2100, Declining: 1400, Unchanged: 300}})
|
||||
core.Use(c, core.Settings{})
|
||||
r := finance.MarketOverview(c, indices, finance.MarketOverviewOptions{Breadth: finance.MarketBreadth{Advancing: 2100, Declining: 1400, Unchanged: 300}})
|
||||
if r.Chosen >= 0 {
|
||||
fmt.Println(indices[r.Chosen].Name)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "Tab 到达指数卡片;Enter/Space 选择。"},
|
||||
{ID: "190", Name: "IndexCard", ZhName: "指数卡片", Category: "Markets", Demo: marketsIndexCardDemo, Code: `quote := mujica.IndexQuote{Name: "S&P 500", Price: 5751.07, Change: 51.13, ChangePercent: 0.9, Series: []float64{5700, 5730, 5720, 5751}}
|
||||
{ID: "190", Name: "IndexCard", ZhName: "指数卡片", Category: "Markets", Demo: marketsIndexCardDemo, Code: `quote := finance.IndexQuote{Name: "S&P 500", Price: 5751.07, Change: 51.13, ChangePercent: 0.9, Series: []float64{5700, 5730, 5720, 5751}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.IndexCard(c, quote, mujica.IndexCardOptions{Clickable: true})
|
||||
core.Use(c, core.Settings{})
|
||||
r := finance.IndexCard(c, quote, finance.IndexCardOptions{Clickable: true})
|
||||
if r.Changed() {
|
||||
fmt.Println(quote.Name)
|
||||
}
|
||||
@@ -112,46 +115,46 @@ ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "Tab 聚焦卡片或迷你图;Enter/Space 激活卡片,图表方向键读数。"},
|
||||
{ID: "191", Name: "MarketStatus", ZhName: "市场状态", Category: "Markets", Demo: marketsMarketStatusDemo, Code: `now := time.Date(2026, 10, 5, 14, 30, 0, 0, time.UTC)
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.MarketStatus(c, mujica.MarketOpen, mujica.MarketStatusOptions{Market: "NYSE", Now: now, Next: now.Add(5 * time.Hour), NextPhase: mujica.MarketAfterHours})
|
||||
core.Use(c, core.Settings{})
|
||||
finance.MarketStatus(c, finance.MarketOpen, finance.MarketStatusOptions{Market: "NYSE", Now: now, Next: now.Add(5 * time.Hour), NextPhase: finance.MarketAfterHours})
|
||||
}
|
||||
ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "展示组件。"},
|
||||
{ID: "192", Name: "TradingSessionClock", ZhName: "交易时段时钟", Category: "Markets", Demo: marketsTradingSessionClockDemo, Code: `now := time.Date(2026, 10, 5, 14, 30, 0, 0, time.UTC)
|
||||
markets := []mujica.SessionMarket{{Name: "NYSE", Location: time.FixedZone("EDT", -4*3600), Sessions: []mujica.TradingSession{{Phase: mujica.MarketOpen, Start: 570, End: 960}}}}
|
||||
markets := []finance.SessionMarket{{Name: "NYSE", Location: time.FixedZone("EDT", -4*3600), Sessions: []finance.TradingSession{{Phase: finance.MarketOpen, Start: 570, End: 960}}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.TradingSessionClock(c, markets, mujica.TradingSessionClockOptions{Now: now})
|
||||
core.Use(c, core.Settings{})
|
||||
finance.TradingSessionClock(c, markets, finance.TradingSessionClockOptions{Now: now})
|
||||
}
|
||||
ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "展示组件。"},
|
||||
{ID: "193", Name: "EconomicCalendar", ZhName: "经济日历", Category: "Markets", Demo: marketsEconomicCalendarDemo, Code: `var state mujica.EconomicCalendarState
|
||||
rows := []mujica.EconomicEvent{{ID: "jobs", Country: "US", Title: "Payrolls", Time: time.Date(2026, 10, 5, 14, 30, 0, 0, time.UTC), Importance: 3, Forecast: "150K", Previous: "142K", Actual: "254K"}}
|
||||
{ID: "193", Name: "EconomicCalendar", ZhName: "经济日历", Category: "Markets", Demo: marketsEconomicCalendarDemo, Code: `var state finance.EconomicCalendarState
|
||||
rows := []finance.EconomicEvent{{ID: "jobs", Country: "US", Title: "Payrolls", Time: time.Date(2026, 10, 5, 14, 30, 0, 0, time.UTC), Importance: 3, Forecast: "150K", Previous: "142K", Actual: "254K"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.EconomicCalendar(c, &state, rows, mujica.EconomicCalendarOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := finance.EconomicCalendar(c, &state, rows, finance.EconomicCalendarOptions{})
|
||||
if r.Changed() || r.Submitted() || r.SortChanged() || r.Retried() {
|
||||
fmt.Println(state.Table.Selected())
|
||||
}
|
||||
}
|
||||
ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "Tab 到达国家过滤、重要性和表格;方向键选择;Enter 提交行。"},
|
||||
{ID: "194", Name: "EarningsCalendar", ZhName: "财报日历", Category: "Markets", Demo: marketsEarningsCalendarDemo, Code: `var state mujica.DataTableState[string]
|
||||
rows := []mujica.EarningsEvent{{ID: "a", Symbol: "AAPL", Name: "Apple", Date: mujica.Date{Year: 2026, Month: 10, Day: 30}, Timing: mujica.EarningsAfterClose, EPSEstimate: 1.6, EPSActual: 1.64}}
|
||||
{ID: "194", Name: "EarningsCalendar", ZhName: "财报日历", Category: "Markets", Demo: marketsEarningsCalendarDemo, Code: `var state data.DataTableState[string]
|
||||
rows := []finance.EarningsEvent{{ID: "a", Symbol: "AAPL", Name: "Apple", Date: core.Date{Year: 2026, Month: 10, Day: 30}, Timing: finance.EarningsAfterClose, EPSEstimate: 1.6, EPSActual: 1.64}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.EarningsCalendar(c, &state, rows, mujica.EarningsCalendarOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := finance.EarningsCalendar(c, &state, rows, finance.EarningsCalendarOptions{})
|
||||
if r.Changed() || r.Submitted() || r.SortChanged() || r.Retried() {
|
||||
fmt.Println(state.Selected())
|
||||
}
|
||||
}
|
||||
ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "Tab 聚焦表格;方向键及 Home/End 选择;Enter 提交;表头支持键盘排序。"},
|
||||
{ID: "195", Name: "NewsFeed", ZhName: "新闻流", Category: "Markets", Demo: marketsNewsFeedDemo, Code: `var state mujica.NewsFeedState
|
||||
rows := []mujica.NewsItem{{ID: "a", Title: "Apple unveils new chips", Source: "Wire", Time: time.Date(2026, 10, 5, 14, 30, 0, 0, time.UTC), Symbols: []string{"AAPL"}}}
|
||||
{ID: "195", Name: "NewsFeed", ZhName: "新闻流", Category: "Markets", Demo: marketsNewsFeedDemo, Code: `var state finance.NewsFeedState
|
||||
rows := []finance.NewsItem{{ID: "a", Title: "Apple unveils new chips", Source: "Wire", Time: time.Date(2026, 10, 5, 14, 30, 0, 0, time.UTC), Symbols: []string{"AAPL"}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.NewsFeed(c, &state, rows, mujica.NewsFeedOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := finance.NewsFeed(c, &state, rows, finance.NewsFeedOptions{})
|
||||
if r.Opened != "" || r.Symbol != "" || r.Retry {
|
||||
fmt.Println(r.Opened, r.Symbol)
|
||||
}
|
||||
@@ -160,26 +163,26 @@ ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "Tab 聚焦列表或相关代码;上下选择;Enter 打开并标记已读。"},
|
||||
{ID: "196", Name: "SentimentGauge", ZhName: "情绪仪表", Category: "Markets", Demo: marketsSentimentGaugeDemo, Code: `value := 72.0
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.SentimentGauge(c, value, mujica.SentimentGaugeOptions{Label: "Fear & Greed"})
|
||||
core.Use(c, core.Settings{})
|
||||
finance.SentimentGauge(c, value, finance.SentimentGaugeOptions{Label: "Fear & Greed"})
|
||||
}
|
||||
ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "展示组件,数值与文字同时呈现。"},
|
||||
{ID: "197", Name: "PortfolioSummary", ZhName: "资产总览", Category: "Markets", Demo: marketsPortfolioSummaryDemo, Code: `account := ""
|
||||
accounts := []mujica.PortfolioAccount{{ID: "a", Name: "Brokerage", Total: 120000, DayPnL: 1250, TotalReturn: 20000, Cost: 100000}, {ID: "b", Name: "Retirement", Total: 80000, DayPnL: -300, TotalReturn: 5000, Cost: 75000}}
|
||||
accounts := []finance.PortfolioAccount{{ID: "a", Name: "Brokerage", Total: 120000, DayPnL: 1250, TotalReturn: 20000, Cost: 100000}, {ID: "b", Name: "Retirement", Total: 80000, DayPnL: -300, TotalReturn: 5000, Cost: 75000}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.PortfolioSummary(c, &account, accounts, mujica.PortfolioSummaryOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := finance.PortfolioSummary(c, &account, accounts, finance.PortfolioSummaryOptions{})
|
||||
if r.Changed() {
|
||||
fmt.Println(account)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "Tab 聚焦账户组;方向键切换账户。"},
|
||||
{ID: "198", Name: "AssetAllocationChart", ZhName: "资产配置图", Category: "Markets", Demo: marketsAssetAllocationChartDemo, Code: `items := []mujica.AllocationItem{{Name: "Stocks", Value: 60, Target: 50}, {Name: "Bonds", Value: 30, Target: 40}, {Name: "Cash", Value: 10, Target: 10}}
|
||||
{ID: "198", Name: "AssetAllocationChart", ZhName: "资产配置图", Category: "Markets", Demo: marketsAssetAllocationChartDemo, Code: `items := []finance.AllocationItem{{Name: "Stocks", Value: 60, Target: 50}, {Name: "Bonds", Value: 30, Target: 40}, {Name: "Cash", Value: 10, Target: 10}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.AssetAllocationChart(c, items, mujica.AssetAllocationChartOptions{Frame: mujica.ChartFrame{Label: "Allocation"}})
|
||||
core.Use(c, core.Settings{})
|
||||
r := finance.AssetAllocationChart(c, items, finance.AssetAllocationChartOptions{Frame: chart.ChartFrame{Label: "Allocation"}})
|
||||
if r.Changed() || r.Retried() {
|
||||
fmt.Println(r.Point)
|
||||
}
|
||||
@@ -190,8 +193,8 @@ ui.Render(view, 960, 440, 1)
|
||||
benchmark := []float64{100, 105, 108, 112}
|
||||
times := []time.Time{time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC), time.Date(2026, 3, 1, 0, 0, 0, 0, time.UTC), time.Date(2026, 4, 1, 0, 0, 0, 0, time.UTC)}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.PerformanceChart(c, values, mujica.PerformanceChartOptions{Frame: mujica.ChartFrame{Label: "Performance"}, Times: times, Benchmark: benchmark})
|
||||
core.Use(c, core.Settings{})
|
||||
r := finance.PerformanceChart(c, values, finance.PerformanceChartOptions{Frame: chart.ChartFrame{Label: "Performance"}, Times: times, Benchmark: benchmark})
|
||||
if r.Changed() || r.Retried() {
|
||||
fmt.Println(r.Point)
|
||||
}
|
||||
@@ -201,12 +204,12 @@ view := func(c *ui.Context) {
|
||||
}
|
||||
ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "Tab 聚焦图和图例;方向键读数;Space 切换序列。"},
|
||||
{ID: "200", Name: "CurrencyConverter", ZhName: "货币换算", Category: "Markets", Demo: marketsCurrencyConverterDemo, Code: `state := mujica.CurrencyConverterState{Amount: 100, From: "USD", To: "JPY"}
|
||||
currencies := []mujica.Currency{{Code: "USD", Name: "Dollar", Decimals: 2}, {Code: "JPY", Name: "Yen", Decimals: 0}}
|
||||
{ID: "200", Name: "CurrencyConverter", ZhName: "货币换算", Category: "Markets", Demo: marketsCurrencyConverterDemo, Code: `state := finance.CurrencyConverterState{Amount: 100, From: "USD", To: "JPY"}
|
||||
currencies := []finance.Currency{{Code: "USD", Name: "Dollar", Decimals: 2}, {Code: "JPY", Name: "Yen", Decimals: 0}}
|
||||
rate := 149.37
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.CurrencyConverter(c, &state, currencies, mujica.CurrencyConverterOptions{Rate: rate})
|
||||
core.Use(c, core.Settings{})
|
||||
r := finance.CurrencyConverter(c, &state, currencies, finance.CurrencyConverterOptions{Rate: rate})
|
||||
if r.PairChanged {
|
||||
rate = 0
|
||||
fmt.Println(state.From, state.To)
|
||||
@@ -216,18 +219,18 @@ ui.Render(view, 960, 440, 1)
|
||||
`, Keys: "Tab 到达金额、币种和互换;方向键步进、选择;Enter/Space 互换。"},
|
||||
}
|
||||
|
||||
func marketsDataState(c *ui.Context, id string, log func(string)) (mujica.DataStatus, bool, string) {
|
||||
func marketsDataState(c *ui.Context, id string, log func(string)) (data.DataStatus, bool, string) {
|
||||
state := ui.Local(c.Root(), id+".status", func() int { return 0 })
|
||||
if mujica.RadioGroup(c, state, []mujica.RadioItem[int]{{Value: 0, Label: "Ready"}, {Value: 1, Label: "Loading"}, {Value: 2, Label: "Empty"}, {Value: 3, Label: "Error"}}, mujica.RadioGroupOptions{Horizontal: true}).Changed() {
|
||||
if input.RadioGroup(c, state, []input.RadioItem[int]{{Value: 0, Label: "Ready"}, {Value: 1, Label: "Loading"}, {Value: 2, Label: "Empty"}, {Value: 3, Label: "Error"}}, input.RadioGroupOptions{Horizontal: true}).Changed() {
|
||||
log(fmt.Sprint("status ", *state))
|
||||
}
|
||||
switch *state {
|
||||
case 1:
|
||||
return mujica.DataLoading, false, ""
|
||||
return data.DataLoading, false, ""
|
||||
case 2:
|
||||
return mujica.DataReady, true, ""
|
||||
return data.DataReady, true, ""
|
||||
case 3:
|
||||
return mujica.DataFailed, false, "Sample failed"
|
||||
return data.DataFailed, false, "Sample failed"
|
||||
}
|
||||
return mujica.DataReady, false, ""
|
||||
return data.DataReady, false, ""
|
||||
}
|
||||
@@ -2,32 +2,37 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chart"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/finance"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func marketsOptionChainDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "181.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
state := ui.Local(c.Root(), "181.state", func() mujica.OptionChainState { return mujica.OptionChainState{} })
|
||||
rows := *ui.Local(c.Root(), "markets.OptionChain.rows.pages_markets_1", func() []mujica.OptionStrike {
|
||||
return []mujica.OptionStrike{{Strike: 100, Call: mujica.OptionQuote{Bid: 3, Ask: 3.2, Last: 3.1}, Put: mujica.OptionQuote{Bid: 2, Ask: 2.2, Last: 2.1}}}
|
||||
state := ui.Local(c.Root(), "181.state", func() finance.OptionChainState { return finance.OptionChainState{} })
|
||||
rows := *ui.Local(c.Root(), "markets.OptionChain.rows.pages_markets_1", func() []finance.OptionStrike {
|
||||
return []finance.OptionStrike{{Strike: 100, Call: finance.OptionQuote{Bid: 3, Ask: 3.2, Last: 3.1}, Put: finance.OptionQuote{Bid: 2, Ask: 2.2, Last: 2.1}}}
|
||||
})
|
||||
|
||||
precision := ui.Local(c.Root(), "markets.OptionChain.precision", func() float64 { return 2 })
|
||||
if mujica.Slider(c, precision, mujica.SliderOptions{Min: 1, Max: 8, Step: 1, Label: "Decimals", ShowValue: true}).Changed() {
|
||||
if input.Slider(c, precision, input.SliderOptions{Min: 1, Max: 8, Step: 1, Label: "Decimals", ShowValue: true}).Changed() {
|
||||
log(fmt.Sprint("decimals ", *precision))
|
||||
}
|
||||
spot := ui.Local(c.Root(), "181.spot", func() float64 { return 100.4 })
|
||||
if mujica.Slider(c, spot, mujica.SliderOptions{Min: 95, Max: 105, Step: 0.1, Label: "Spot", ShowValue: true}).Changed() {
|
||||
if input.Slider(c, spot, input.SliderOptions{Min: 95, Max: 105, Step: 0.1, Label: "Spot", ShowValue: true}).Changed() {
|
||||
log(fmt.Sprint("spot ", *spot))
|
||||
}
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
r := mujica.OptionChain(c, state, []string{"Oct 17", "Nov 21"}, rows, mujica.OptionChainOptions{Decimals: int(*precision), Spot: *spot})
|
||||
r := finance.OptionChain(c, state, []string{"Oct 17", "Nov 21"}, rows, finance.OptionChainOptions{Decimals: int(*precision), Spot: *spot})
|
||||
if r.Changed || r.Submitted || r.ExpiryChanged {
|
||||
log(fmt.Sprint(state.Expiry, state.Strike, state.Side))
|
||||
}
|
||||
@@ -36,52 +41,52 @@ func marketsOptionChainDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func marketsGreeksTableDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "182.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
rows := *ui.Local(c.Root(), "markets.GreeksTable.rows.pages_markets_1", func() []mujica.GreeksRow {
|
||||
return []mujica.GreeksRow{{Label: "AAPL 100 C", Greeks: mujica.OptionGreeks{Delta: 0.5, Gamma: 0.03, Theta: -0.1, Vega: 0.2, Rho: 0.05}}}
|
||||
rows := *ui.Local(c.Root(), "markets.GreeksTable.rows.pages_markets_1", func() []finance.GreeksRow {
|
||||
return []finance.GreeksRow{{Label: "AAPL 100 C", Greeks: finance.OptionGreeks{Delta: 0.5, Gamma: 0.03, Theta: -0.1, Vega: 0.2, Rho: 0.05}}}
|
||||
})
|
||||
|
||||
precision := ui.Local(c.Root(), "markets.GreeksTable.precision", func() float64 { return 2 })
|
||||
if mujica.Slider(c, precision, mujica.SliderOptions{Min: 1, Max: 8, Step: 1, Label: "Decimals", ShowValue: true}).Changed() {
|
||||
if input.Slider(c, precision, input.SliderOptions{Min: 1, Max: 8, Step: 1, Label: "Decimals", ShowValue: true}).Changed() {
|
||||
log(fmt.Sprint("decimals ", *precision))
|
||||
}
|
||||
notes := ui.Local(c.Root(), "182.notes", func() bool { return true })
|
||||
if mujica.Checkbox(c, notes, "Notes", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, notes, "Notes", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("notes ", *notes))
|
||||
}
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
mujica.GreeksTable(c, rows, mujica.GreeksTableOptions{Decimals: int(*precision), Notes: *notes})
|
||||
finance.GreeksTable(c, rows, finance.GreeksTableOptions{Decimals: int(*precision), Notes: *notes})
|
||||
})
|
||||
}
|
||||
|
||||
func marketsPayoffDiagramDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "183.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
legs := *ui.Local(c.Root(), "markets.PayoffDiagram.legs.pages_markets_1", func() []mujica.OptionLeg {
|
||||
return []mujica.OptionLeg{{Kind: mujica.LegCall, Strike: 100, Premium: 3, Quantity: 1}}
|
||||
legs := *ui.Local(c.Root(), "markets.PayoffDiagram.legs.pages_markets_1", func() []finance.OptionLeg {
|
||||
return []finance.OptionLeg{{Kind: finance.LegCall, Strike: 100, Premium: 3, Quantity: 1}}
|
||||
})
|
||||
|
||||
status := ui.Local(c.Root(), "183.status", func() int { return 0 })
|
||||
if mujica.RadioGroup(c, status, []mujica.RadioItem[int]{{Value: 0, Label: "Ready"}, {Value: 1, Label: "Loading"}, {Value: 2, Label: "Empty"}, {Value: 3, Label: "Error"}}, mujica.RadioGroupOptions{Horizontal: true}).Changed() {
|
||||
if input.RadioGroup(c, status, []input.RadioItem[int]{{Value: 0, Label: "Ready"}, {Value: 1, Label: "Loading"}, {Value: 2, Label: "Empty"}, {Value: 3, Label: "Error"}}, input.RadioGroupOptions{Horizontal: true}).Changed() {
|
||||
log(fmt.Sprint("status ", *status))
|
||||
}
|
||||
frame := mujica.ChartFrame{Label: "PayoffDiagram"}
|
||||
frame := chart.ChartFrame{Label: "PayoffDiagram"}
|
||||
if *status == 1 {
|
||||
frame.Status = mujica.DataLoading
|
||||
frame.Status = data.DataLoading
|
||||
}
|
||||
if *status == 3 {
|
||||
frame.Status = mujica.DataFailed
|
||||
frame.Status = data.DataFailed
|
||||
frame.Error = "Sample failed"
|
||||
}
|
||||
if *status == 2 {
|
||||
legs = nil
|
||||
}
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
r := mujica.PayoffDiagram(c, legs, mujica.PayoffDiagramOptions{Frame: frame, Spot: 103})
|
||||
r := finance.PayoffDiagram(c, legs, finance.PayoffDiagramOptions{Frame: frame, Spot: 103})
|
||||
if r.Changed() || r.Retried() {
|
||||
log(fmt.Sprint(r.Point))
|
||||
}
|
||||
@@ -90,33 +95,33 @@ func marketsPayoffDiagramDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func marketsWatchlistDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "184.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
state := ui.Local(c.Root(), "184.state", func() mujica.WatchlistState { return mujica.WatchlistState{} })
|
||||
groups := *ui.Local(c.Root(), "markets.Watchlist.groups.pages_markets_1", func() []mujica.WatchGroup {
|
||||
return []mujica.WatchGroup{{Name: "Technology", Items: []mujica.WatchItem{{Symbol: "AAPL", Name: "Apple", Price: 227.5, Change: 2.7, ChangePercent: 1.2}, {Symbol: "MSFT", Name: "Microsoft", Price: 418.2, Change: -2.5, ChangePercent: -0.6}}}, {Name: "Energy", Items: []mujica.WatchItem{{Symbol: "XOM", Price: 117.4}}}}
|
||||
state := ui.Local(c.Root(), "184.state", func() finance.WatchlistState { return finance.WatchlistState{} })
|
||||
groups := *ui.Local(c.Root(), "markets.Watchlist.groups.pages_markets_1", func() []finance.WatchGroup {
|
||||
return []finance.WatchGroup{{Name: "Technology", Items: []finance.WatchItem{{Symbol: "AAPL", Name: "Apple", Price: 227.5, Change: 2.7, ChangePercent: 1.2}, {Symbol: "MSFT", Name: "Microsoft", Price: 418.2, Change: -2.5, ChangePercent: -0.6}}}, {Name: "Energy", Items: []finance.WatchItem{{Symbol: "XOM", Price: 117.4}}}}
|
||||
})
|
||||
|
||||
precision := ui.Local(c.Root(), "markets.Watchlist.precision", func() float64 { return 2 })
|
||||
if mujica.Slider(c, precision, mujica.SliderOptions{Min: 1, Max: 8, Step: 1, Label: "Decimals", ShowValue: true}).Changed() {
|
||||
if input.Slider(c, precision, input.SliderOptions{Min: 1, Max: 8, Step: 1, Label: "Decimals", ShowValue: true}).Changed() {
|
||||
log(fmt.Sprint("decimals ", *precision))
|
||||
}
|
||||
if mujica.Button(c, "Refresh quotes", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Refresh quotes", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
groups[0].Items = slices.Clone(groups[0].Items)
|
||||
groups[0].Items[0].Price += 0.1
|
||||
log("quotes refreshed")
|
||||
}
|
||||
beforeGroup, beforeColumns := state.Group, fmt.Sprint(state.Hidden)
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
r := mujica.Watchlist(c, state, groups, mujica.WatchlistOptions{Decimals: int(*precision)})
|
||||
r := finance.Watchlist(c, state, groups, finance.WatchlistOptions{Decimals: int(*precision)})
|
||||
if r.Changed || r.Submitted || r.Moved {
|
||||
log(fmt.Sprint(state.Selected, r.Rows, r.To))
|
||||
}
|
||||
if r.Moved {
|
||||
items := groups[state.Group].Items
|
||||
var moving []mujica.WatchItem
|
||||
var rest []mujica.WatchItem
|
||||
var moving []finance.WatchItem
|
||||
var rest []finance.WatchItem
|
||||
at := r.To
|
||||
for j, item := range items {
|
||||
if slices.Contains(r.Rows, j) {
|
||||
@@ -138,26 +143,26 @@ func marketsWatchlistDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func marketsSymbolSearchDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "185.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
state := ui.Local(c.Root(), "185.state", func() mujica.SymbolSearchState { return mujica.SymbolSearchState{} })
|
||||
rows := *ui.Local(c.Root(), "markets.SymbolSearch.rows.pages_markets_1", func() []mujica.MarketSymbol {
|
||||
return []mujica.MarketSymbol{{Code: "AAPL", Name: "Apple", Market: "NASDAQ", Type: "Stock"}}
|
||||
state := ui.Local(c.Root(), "185.state", func() finance.SymbolSearchState { return finance.SymbolSearchState{} })
|
||||
rows := *ui.Local(c.Root(), "markets.SymbolSearch.rows.pages_markets_1", func() []finance.MarketSymbol {
|
||||
return []finance.MarketSymbol{{Code: "AAPL", Name: "Apple", Market: "NASDAQ", Type: "Stock"}}
|
||||
})
|
||||
|
||||
status, empty, err := marketsDataState(c, "185", log)
|
||||
if empty || status != mujica.DataReady {
|
||||
if empty || status != data.DataReady {
|
||||
rows = nil
|
||||
}
|
||||
results := []mujica.MarketSymbol{}
|
||||
results := []finance.MarketSymbol{}
|
||||
for _, row := range rows {
|
||||
if (state.Market == "" || state.Market == row.Market) && strings.Contains(strings.ToUpper(row.Code+" "+row.Name), strings.ToUpper(state.Query)) {
|
||||
results = append(results, row)
|
||||
}
|
||||
}
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
r := mujica.SymbolSearch(c, state, results, mujica.SymbolSearchOptions{Loading: status == mujica.DataLoading, Error: err, Markets: []string{"NASDAQ", "NYSE"}, Recent: rows})
|
||||
r := finance.SymbolSearch(c, state, results, finance.SymbolSearchOptions{Loading: status == data.DataLoading, Error: err, Markets: []string{"NASDAQ", "NYSE"}, Recent: rows})
|
||||
if r.Changed || r.Picked || r.Retry {
|
||||
log(fmt.Sprint(state.Query, state.Market, r.Chosen.Code))
|
||||
}
|
||||
|
||||
@@ -2,26 +2,31 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chart"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/finance"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func marketsSymbolBadgeDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "186.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
symbol := *ui.Local(c.Root(), "markets.SymbolBadge.symbol.pages_markets_2", func() mujica.MarketSymbol {
|
||||
return mujica.MarketSymbol{Code: "AAPL", Name: "Apple", Market: "NASDAQ", Type: "Stock"}
|
||||
symbol := *ui.Local(c.Root(), "markets.SymbolBadge.symbol.pages_markets_2", func() finance.MarketSymbol {
|
||||
return finance.MarketSymbol{Code: "AAPL", Name: "Apple", Market: "NASDAQ", Type: "Stock"}
|
||||
})
|
||||
|
||||
kind := ui.Local(c.Root(), "186.kind", func() string { return "Stock" })
|
||||
if mujica.RadioGroup(c, kind, []mujica.RadioItem[string]{{Value: "Stock", Label: "Stock"}, {Value: "ETF", Label: "ETF"}}, mujica.RadioGroupOptions{Horizontal: true}).Changed() {
|
||||
if input.RadioGroup(c, kind, []input.RadioItem[string]{{Value: "Stock", Label: "Stock"}, {Value: "ETF", Label: "ETF"}}, input.RadioGroupOptions{Horizontal: true}).Changed() {
|
||||
log(*kind)
|
||||
}
|
||||
symbol.Type = *kind
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
r := mujica.SymbolBadge(c, symbol, mujica.SymbolBadgeOptions{Clickable: true})
|
||||
r := finance.SymbolBadge(c, symbol, finance.SymbolBadgeOptions{Clickable: true})
|
||||
if r.Changed() {
|
||||
log(fmt.Sprint(symbol.Code))
|
||||
}
|
||||
@@ -30,24 +35,24 @@ func marketsSymbolBadgeDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func marketsMarketHeatmapDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "187.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
path := ui.Local(c.Root(), "187.path", func() []string { return []string{} })
|
||||
root := *ui.Local(c.Root(), "markets.MarketHeatmap.root.pages_markets_2", func() mujica.HeatNode {
|
||||
return mujica.HeatNode{Name: "Market", Children: []mujica.HeatNode{{Name: "Technology", Children: []mujica.HeatNode{{Name: "AAPL", Cap: 3000, Change: 1.2}, {Name: "MSFT", Cap: 2800, Change: -0.6}}}, {Name: "Energy", Cap: 450, Change: -2.1}}}
|
||||
root := *ui.Local(c.Root(), "markets.MarketHeatmap.root.pages_markets_2", func() chart.HeatNode {
|
||||
return chart.HeatNode{Name: "Market", Children: []chart.HeatNode{{Name: "Technology", Children: []chart.HeatNode{{Name: "AAPL", Cap: 3000, Change: 1.2}, {Name: "MSFT", Cap: 2800, Change: -0.6}}}, {Name: "Energy", Cap: 450, Change: -2.1}}}
|
||||
})
|
||||
|
||||
status := ui.Local(c.Root(), "187.status", func() int { return 0 })
|
||||
if mujica.RadioGroup(c, status, []mujica.RadioItem[int]{{Value: 0, Label: "Ready"}, {Value: 1, Label: "Loading"}, {Value: 2, Label: "Empty"}, {Value: 3, Label: "Error"}}, mujica.RadioGroupOptions{Horizontal: true}).Changed() {
|
||||
if input.RadioGroup(c, status, []input.RadioItem[int]{{Value: 0, Label: "Ready"}, {Value: 1, Label: "Loading"}, {Value: 2, Label: "Empty"}, {Value: 3, Label: "Error"}}, input.RadioGroupOptions{Horizontal: true}).Changed() {
|
||||
log(fmt.Sprint("status ", *status))
|
||||
}
|
||||
frame := mujica.ChartFrame{Label: "MarketHeatmap"}
|
||||
frame := chart.ChartFrame{Label: "MarketHeatmap"}
|
||||
if *status == 1 {
|
||||
frame.Status = mujica.DataLoading
|
||||
frame.Status = data.DataLoading
|
||||
}
|
||||
if *status == 3 {
|
||||
frame.Status = mujica.DataFailed
|
||||
frame.Status = data.DataFailed
|
||||
frame.Error = "Sample failed"
|
||||
}
|
||||
if *status == 2 {
|
||||
@@ -58,11 +63,11 @@ func marketsMarketHeatmapDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
}
|
||||
scale := ui.Local(c.Root(), "187.scale", func() float64 { return 3 })
|
||||
if mujica.Slider(c, scale, mujica.SliderOptions{Min: 1, Max: 10, Step: 1, Label: "Color scale", ShowValue: true}).Changed() {
|
||||
if input.Slider(c, scale, input.SliderOptions{Min: 1, Max: 10, Step: 1, Label: "Color scale", ShowValue: true}).Changed() {
|
||||
log(fmt.Sprint(*scale))
|
||||
}
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
r := mujica.MarketHeatmap(c, path, root, mujica.MarketHeatmapOptions{Frame: frame, Scale: *scale})
|
||||
r := chart.MarketHeatmap(c, path, root, chart.MarketHeatmapOptions{Frame: frame, Scale: *scale})
|
||||
if r.Changed() || r.ValueChanged() || r.Retried() {
|
||||
log(fmt.Sprint(path, r.Point))
|
||||
}
|
||||
@@ -71,21 +76,21 @@ func marketsMarketHeatmapDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func marketsScreenerDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "188.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
state := ui.Local(c.Root(), "188.state", func() mujica.ScreenerState { return mujica.ScreenerState{} })
|
||||
fields := *ui.Local(c.Root(), "markets.Screener.fields.pages_markets_2", func() []mujica.ScreenerField { return []mujica.ScreenerField{{ID: "pe", Label: "P/E", Decimals: 1}} })
|
||||
rows := *ui.Local(c.Root(), "markets.Screener.rows.pages_markets_2", func() []mujica.ScreenerRow {
|
||||
return []mujica.ScreenerRow{{Symbol: "AAPL", Values: map[string]float64{"pe": 34}}, {Symbol: "XOM", Values: map[string]float64{"pe": 13}}}
|
||||
state := ui.Local(c.Root(), "188.state", func() finance.ScreenerState { return finance.ScreenerState{} })
|
||||
fields := *ui.Local(c.Root(), "markets.Screener.fields.pages_markets_2", func() []finance.ScreenerField { return []finance.ScreenerField{{ID: "pe", Label: "P/E", Decimals: 1}} })
|
||||
rows := *ui.Local(c.Root(), "markets.Screener.rows.pages_markets_2", func() []finance.ScreenerRow {
|
||||
return []finance.ScreenerRow{{Symbol: "AAPL", Values: map[string]float64{"pe": 34}}, {Symbol: "XOM", Values: map[string]float64{"pe": 13}}}
|
||||
})
|
||||
|
||||
status, empty, err := marketsDataState(c, "188", log)
|
||||
if empty || status != mujica.DataReady {
|
||||
if empty || status != data.DataReady {
|
||||
rows = nil
|
||||
}
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
r := mujica.Screener(c, state, fields, rows, mujica.ScreenerOptions{Status: status, Error: err})
|
||||
r := finance.Screener(c, state, fields, rows, finance.ScreenerOptions{Status: status, Error: err})
|
||||
if r.Changed || r.Save || r.Table.Changed() || r.Table.Submitted() || r.Table.SortChanged() || r.Table.Retried() {
|
||||
log(fmt.Sprint(state.Conditions))
|
||||
}
|
||||
@@ -94,19 +99,19 @@ func marketsScreenerDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func marketsMarketOverviewDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "189.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
indices := *ui.Local(c.Root(), "markets.MarketOverview.indices.pages_markets_2", func() []mujica.IndexQuote {
|
||||
return []mujica.IndexQuote{{Name: "S&P 500", Price: 5751.07, Change: 51.13, ChangePercent: 0.9, Series: []float64{5700, 5730, 5720, 5751}}}
|
||||
indices := *ui.Local(c.Root(), "markets.MarketOverview.indices.pages_markets_2", func() []finance.IndexQuote {
|
||||
return []finance.IndexQuote{{Name: "S&P 500", Price: 5751.07, Change: 51.13, ChangePercent: 0.9, Series: []float64{5700, 5730, 5720, 5751}}}
|
||||
})
|
||||
|
||||
advancing := ui.Local(c.Root(), "189.advancing", func() float64 { return 2100 })
|
||||
if mujica.Slider(c, advancing, mujica.SliderOptions{Min: 0, Max: 4000, Step: 100, Label: "Advancing", ShowValue: true}).Changed() {
|
||||
if input.Slider(c, advancing, input.SliderOptions{Min: 0, Max: 4000, Step: 100, Label: "Advancing", ShowValue: true}).Changed() {
|
||||
log(fmt.Sprint(*advancing))
|
||||
}
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
r := mujica.MarketOverview(c, indices, mujica.MarketOverviewOptions{Breadth: mujica.MarketBreadth{Advancing: int(*advancing), Declining: 1400, Unchanged: 300}})
|
||||
r := finance.MarketOverview(c, indices, finance.MarketOverviewOptions{Breadth: finance.MarketBreadth{Advancing: int(*advancing), Declining: 1400, Unchanged: 300}})
|
||||
if r.Chosen >= 0 {
|
||||
log(fmt.Sprint(indices[r.Chosen].Name))
|
||||
}
|
||||
@@ -115,24 +120,24 @@ func marketsMarketOverviewDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func marketsIndexCardDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "190.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
quote := *ui.Local(c.Root(), "markets.IndexCard.quote.pages_markets_2", func() mujica.IndexQuote {
|
||||
return mujica.IndexQuote{Name: "S&P 500", Price: 5751.07, Change: 51.13, ChangePercent: 0.9, Series: []float64{5700, 5730, 5720, 5751}}
|
||||
quote := *ui.Local(c.Root(), "markets.IndexCard.quote.pages_markets_2", func() finance.IndexQuote {
|
||||
return finance.IndexQuote{Name: "S&P 500", Price: 5751.07, Change: 51.13, ChangePercent: 0.9, Series: []float64{5700, 5730, 5720, 5751}}
|
||||
})
|
||||
|
||||
precision := ui.Local(c.Root(), "markets.IndexCard.precision", func() float64 { return 2 })
|
||||
if mujica.Slider(c, precision, mujica.SliderOptions{Min: 1, Max: 8, Step: 1, Label: "Decimals", ShowValue: true}).Changed() {
|
||||
if input.Slider(c, precision, input.SliderOptions{Min: 1, Max: 8, Step: 1, Label: "Decimals", ShowValue: true}).Changed() {
|
||||
log(fmt.Sprint("decimals ", *precision))
|
||||
}
|
||||
price := ui.Local(c.Root(), "190.price", func() float64 { return 5751.07 })
|
||||
if mujica.NumberInput(c, price, mujica.NumberInputOptions{Min: 1, Max: 10000, Step: 0.01, Precision: 2, Label: "Price"}).Changed() {
|
||||
if input.NumberInput(c, price, input.NumberInputOptions{Min: 1, Max: 10000, Step: 0.01, Precision: 2, Label: "Price"}).Changed() {
|
||||
log(fmt.Sprint(*price))
|
||||
}
|
||||
quote.Price = *price
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
r := mujica.IndexCard(c, quote, mujica.IndexCardOptions{Decimals: int(*precision), Clickable: true})
|
||||
r := finance.IndexCard(c, quote, finance.IndexCardOptions{Decimals: int(*precision), Clickable: true})
|
||||
if r.Changed() {
|
||||
log(fmt.Sprint(quote.Name))
|
||||
}
|
||||
|
||||
@@ -2,69 +2,74 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
"time"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/finance"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func marketsMarketStatusDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "191.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
now := time.Date(2026, 10, 5, 14, 30, 0, 0, time.UTC)
|
||||
|
||||
phase := ui.Local(c.Root(), "191.phase", func() mujica.MarketPhase { return mujica.MarketOpen })
|
||||
if mujica.RadioGroup(c, phase, []mujica.RadioItem[mujica.MarketPhase]{{Value: mujica.MarketClosed, Label: "Closed"}, {Value: mujica.MarketPreMarket, Label: "Pre-market"}, {Value: mujica.MarketOpen, Label: "Open"}, {Value: mujica.MarketAfterHours, Label: "After hours"}, {Value: mujica.MarketHoliday, Label: "Holiday"}}, mujica.RadioGroupOptions{Horizontal: true}).Changed() {
|
||||
phase := ui.Local(c.Root(), "191.phase", func() finance.MarketPhase { return finance.MarketOpen })
|
||||
if input.RadioGroup(c, phase, []input.RadioItem[finance.MarketPhase]{{Value: finance.MarketClosed, Label: "Closed"}, {Value: finance.MarketPreMarket, Label: "Pre-market"}, {Value: finance.MarketOpen, Label: "Open"}, {Value: finance.MarketAfterHours, Label: "After hours"}, {Value: finance.MarketHoliday, Label: "Holiday"}}, input.RadioGroupOptions{Horizontal: true}).Changed() {
|
||||
log(fmt.Sprint(*phase))
|
||||
}
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
mujica.MarketStatus(c, *phase, mujica.MarketStatusOptions{Market: "NYSE", Now: now, Next: now.Add(5 * time.Hour), NextPhase: mujica.MarketAfterHours})
|
||||
finance.MarketStatus(c, *phase, finance.MarketStatusOptions{Market: "NYSE", Now: now, Next: now.Add(5 * time.Hour), NextPhase: finance.MarketAfterHours})
|
||||
})
|
||||
}
|
||||
|
||||
func marketsTradingSessionClockDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "192.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
now := time.Date(2026, 10, 5, 14, 30, 0, 0, time.UTC)
|
||||
markets := *ui.Local(c.Root(), "markets.TradingSessionClock.markets.pages_markets_3", func() []mujica.SessionMarket {
|
||||
return []mujica.SessionMarket{{Name: "NYSE", Location: time.FixedZone("EDT", -4*3600), Sessions: []mujica.TradingSession{{Phase: mujica.MarketOpen, Start: 570, End: 960}}}}
|
||||
markets := *ui.Local(c.Root(), "markets.TradingSessionClock.markets.pages_markets_3", func() []finance.SessionMarket {
|
||||
return []finance.SessionMarket{{Name: "NYSE", Location: time.FixedZone("EDT", -4*3600), Sessions: []finance.TradingSession{{Phase: finance.MarketOpen, Start: 570, End: 960}}}}
|
||||
})
|
||||
|
||||
holiday := ui.Local(c.Root(), "192.holiday", func() bool { return false })
|
||||
if mujica.Checkbox(c, holiday, "Holiday", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, holiday, "Holiday", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint(*holiday))
|
||||
}
|
||||
markets[0].Holiday = *holiday
|
||||
minute := ui.Local(c.Root(), "192.minute", func() float64 { return 870 })
|
||||
if mujica.Slider(c, minute, mujica.SliderOptions{Min: 0, Max: 1439, Step: 1, Label: "UTC minute", ShowValue: true}).Changed() {
|
||||
if input.Slider(c, minute, input.SliderOptions{Min: 0, Max: 1439, Step: 1, Label: "UTC minute", ShowValue: true}).Changed() {
|
||||
log(fmt.Sprint(*minute))
|
||||
}
|
||||
now = now.Truncate(24 * time.Hour).Add(time.Duration(*minute) * time.Minute)
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
mujica.TradingSessionClock(c, markets, mujica.TradingSessionClockOptions{Now: now})
|
||||
finance.TradingSessionClock(c, markets, finance.TradingSessionClockOptions{Now: now})
|
||||
})
|
||||
}
|
||||
|
||||
func marketsEconomicCalendarDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "193.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
state := ui.Local(c.Root(), "193.state", func() mujica.EconomicCalendarState { return mujica.EconomicCalendarState{} })
|
||||
rows := *ui.Local(c.Root(), "markets.EconomicCalendar.rows.pages_markets_3", func() []mujica.EconomicEvent {
|
||||
return []mujica.EconomicEvent{{ID: "jobs", Country: "US", Title: "Payrolls", Time: time.Date(2026, 10, 5, 14, 30, 0, 0, time.UTC), Importance: 3, Forecast: "150K", Previous: "142K", Actual: "254K"}}
|
||||
state := ui.Local(c.Root(), "193.state", func() finance.EconomicCalendarState { return finance.EconomicCalendarState{} })
|
||||
rows := *ui.Local(c.Root(), "markets.EconomicCalendar.rows.pages_markets_3", func() []finance.EconomicEvent {
|
||||
return []finance.EconomicEvent{{ID: "jobs", Country: "US", Title: "Payrolls", Time: time.Date(2026, 10, 5, 14, 30, 0, 0, time.UTC), Importance: 3, Forecast: "150K", Previous: "142K", Actual: "254K"}}
|
||||
})
|
||||
|
||||
status, empty, err := marketsDataState(c, "193", log)
|
||||
if empty || status != mujica.DataReady {
|
||||
if empty || status != data.DataReady {
|
||||
rows = nil
|
||||
}
|
||||
beforeFilter := fmt.Sprint(state.Countries, state.MinImportance)
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
r := mujica.EconomicCalendar(c, state, rows, mujica.EconomicCalendarOptions{Status: status, Error: err})
|
||||
r := finance.EconomicCalendar(c, state, rows, finance.EconomicCalendarOptions{Status: status, Error: err})
|
||||
if r.Changed() || r.Submitted() || r.SortChanged() || r.Retried() {
|
||||
log(fmt.Sprint(state.Table.Selected()))
|
||||
}
|
||||
@@ -76,20 +81,20 @@ func marketsEconomicCalendarDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func marketsEarningsCalendarDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "194.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
state := ui.Local(c.Root(), "194.state", func() mujica.DataTableState[string] { return mujica.DataTableState[string]{} })
|
||||
rows := *ui.Local(c.Root(), "markets.EarningsCalendar.rows.pages_markets_3", func() []mujica.EarningsEvent {
|
||||
return []mujica.EarningsEvent{{ID: "a", Symbol: "AAPL", Name: "Apple", Date: mujica.Date{Year: 2026, Month: 10, Day: 30}, Timing: mujica.EarningsAfterClose, EPSEstimate: 1.6, EPSActual: 1.64}}
|
||||
state := ui.Local(c.Root(), "194.state", func() data.DataTableState[string] { return data.DataTableState[string]{} })
|
||||
rows := *ui.Local(c.Root(), "markets.EarningsCalendar.rows.pages_markets_3", func() []finance.EarningsEvent {
|
||||
return []finance.EarningsEvent{{ID: "a", Symbol: "AAPL", Name: "Apple", Date: core.Date{Year: 2026, Month: 10, Day: 30}, Timing: finance.EarningsAfterClose, EPSEstimate: 1.6, EPSActual: 1.64}}
|
||||
})
|
||||
|
||||
status, empty, err := marketsDataState(c, "194", log)
|
||||
if empty || status != mujica.DataReady {
|
||||
if empty || status != data.DataReady {
|
||||
rows = nil
|
||||
}
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
r := mujica.EarningsCalendar(c, state, rows, mujica.EarningsCalendarOptions{Status: status, Error: err})
|
||||
r := finance.EarningsCalendar(c, state, rows, finance.EarningsCalendarOptions{Status: status, Error: err})
|
||||
if r.Changed() || r.Submitted() || r.SortChanged() || r.Retried() {
|
||||
log(fmt.Sprint(state.Selected()))
|
||||
}
|
||||
@@ -98,20 +103,20 @@ func marketsEarningsCalendarDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func marketsNewsFeedDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "195.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
state := ui.Local(c.Root(), "195.state", func() mujica.NewsFeedState { return mujica.NewsFeedState{} })
|
||||
rows := *ui.Local(c.Root(), "markets.NewsFeed.rows.pages_markets_3", func() []mujica.NewsItem {
|
||||
return []mujica.NewsItem{{ID: "a", Title: "Apple unveils new chips", Source: "Wire", Time: time.Date(2026, 10, 5, 14, 30, 0, 0, time.UTC), Symbols: []string{"AAPL"}}}
|
||||
state := ui.Local(c.Root(), "195.state", func() finance.NewsFeedState { return finance.NewsFeedState{} })
|
||||
rows := *ui.Local(c.Root(), "markets.NewsFeed.rows.pages_markets_3", func() []finance.NewsItem {
|
||||
return []finance.NewsItem{{ID: "a", Title: "Apple unveils new chips", Source: "Wire", Time: time.Date(2026, 10, 5, 14, 30, 0, 0, time.UTC), Symbols: []string{"AAPL"}}}
|
||||
})
|
||||
|
||||
status, empty, err := marketsDataState(c, "195", log)
|
||||
if empty || status != mujica.DataReady {
|
||||
if empty || status != data.DataReady {
|
||||
rows = nil
|
||||
}
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
r := mujica.NewsFeed(c, state, rows, mujica.NewsFeedOptions{Status: status, Error: err})
|
||||
r := finance.NewsFeed(c, state, rows, finance.NewsFeedOptions{Status: status, Error: err})
|
||||
if r.Opened != "" || r.Symbol != "" || r.Retry || r.ReadChanged {
|
||||
log(fmt.Sprint(r.Opened, r.Symbol))
|
||||
}
|
||||
|
||||
@@ -2,46 +2,51 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
"time"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chart"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/finance"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func marketsSentimentGaugeDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "196.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
value := 72.0
|
||||
|
||||
level := ui.Local(c.Root(), "196.value", func() float64 { return 72 })
|
||||
if mujica.Slider(c, level, mujica.SliderOptions{Min: 0, Max: 100, Step: 1, Label: "Sentiment", ShowValue: true}).Changed() {
|
||||
if input.Slider(c, level, input.SliderOptions{Min: 0, Max: 100, Step: 1, Label: "Sentiment", ShowValue: true}).Changed() {
|
||||
log(fmt.Sprint(*level))
|
||||
}
|
||||
value = *level
|
||||
status, empty, err := marketsDataState(c, "196", log)
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
mujica.SentimentGauge(c, value, mujica.SentimentGaugeOptions{Label: "Fear & Greed", Status: status, Empty: empty, Error: err, Retry: func() { log("retry requested") }})
|
||||
finance.SentimentGauge(c, value, finance.SentimentGaugeOptions{Label: "Fear & Greed", Status: status, Empty: empty, Error: err, Retry: func() { log("retry requested") }})
|
||||
})
|
||||
}
|
||||
|
||||
func marketsPortfolioSummaryDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "197.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
account := ui.Local(c.Root(), "197.account", func() string { return "" })
|
||||
accounts := *ui.Local(c.Root(), "markets.PortfolioSummary.accounts.pages_markets_4", func() []mujica.PortfolioAccount {
|
||||
return []mujica.PortfolioAccount{{ID: "a", Name: "Brokerage", Total: 120000, DayPnL: 1250, TotalReturn: 20000, Cost: 100000}, {ID: "b", Name: "Retirement", Total: 80000, DayPnL: -300, TotalReturn: 5000, Cost: 75000}}
|
||||
accounts := *ui.Local(c.Root(), "markets.PortfolioSummary.accounts.pages_markets_4", func() []finance.PortfolioAccount {
|
||||
return []finance.PortfolioAccount{{ID: "a", Name: "Brokerage", Total: 120000, DayPnL: 1250, TotalReturn: 20000, Cost: 100000}, {ID: "b", Name: "Retirement", Total: 80000, DayPnL: -300, TotalReturn: 5000, Cost: 75000}}
|
||||
})
|
||||
|
||||
assets := ui.Local(c.Root(), "197.assets", func() float64 { return 120000 })
|
||||
if mujica.NumberInput(c, assets, mujica.NumberInputOptions{Min: 0, Max: 1000000, Step: 100, Precision: 2, Label: "Brokerage assets"}).Changed() {
|
||||
if input.NumberInput(c, assets, input.NumberInputOptions{Min: 0, Max: 1000000, Step: 100, Precision: 2, Label: "Brokerage assets"}).Changed() {
|
||||
log(fmt.Sprint(*assets))
|
||||
}
|
||||
accounts[0].Total = *assets
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
r := mujica.PortfolioSummary(c, account, accounts, mujica.PortfolioSummaryOptions{})
|
||||
r := finance.PortfolioSummary(c, account, accounts, finance.PortfolioSummaryOptions{})
|
||||
if r.Changed() {
|
||||
log(fmt.Sprint(account))
|
||||
}
|
||||
@@ -50,34 +55,34 @@ func marketsPortfolioSummaryDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func marketsAssetAllocationChartDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "198.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
items := *ui.Local(c.Root(), "markets.AssetAllocationChart.items.pages_markets_4", func() []mujica.AllocationItem {
|
||||
return []mujica.AllocationItem{{Name: "Stocks", Value: 60, Target: 50}, {Name: "Bonds", Value: 30, Target: 40}, {Name: "Cash", Value: 10, Target: 10}}
|
||||
items := *ui.Local(c.Root(), "markets.AssetAllocationChart.items.pages_markets_4", func() []finance.AllocationItem {
|
||||
return []finance.AllocationItem{{Name: "Stocks", Value: 60, Target: 50}, {Name: "Bonds", Value: 30, Target: 40}, {Name: "Cash", Value: 10, Target: 10}}
|
||||
})
|
||||
|
||||
status := ui.Local(c.Root(), "198.status", func() int { return 0 })
|
||||
if mujica.RadioGroup(c, status, []mujica.RadioItem[int]{{Value: 0, Label: "Ready"}, {Value: 1, Label: "Loading"}, {Value: 2, Label: "Empty"}, {Value: 3, Label: "Error"}}, mujica.RadioGroupOptions{Horizontal: true}).Changed() {
|
||||
if input.RadioGroup(c, status, []input.RadioItem[int]{{Value: 0, Label: "Ready"}, {Value: 1, Label: "Loading"}, {Value: 2, Label: "Empty"}, {Value: 3, Label: "Error"}}, input.RadioGroupOptions{Horizontal: true}).Changed() {
|
||||
log(fmt.Sprint("status ", *status))
|
||||
}
|
||||
frame := mujica.ChartFrame{Label: "AssetAllocationChart"}
|
||||
frame := chart.ChartFrame{Label: "AssetAllocationChart"}
|
||||
if *status == 1 {
|
||||
frame.Status = mujica.DataLoading
|
||||
frame.Status = data.DataLoading
|
||||
}
|
||||
if *status == 3 {
|
||||
frame.Status = mujica.DataFailed
|
||||
frame.Status = data.DataFailed
|
||||
frame.Error = "Sample failed"
|
||||
}
|
||||
if *status == 2 {
|
||||
items = nil
|
||||
}
|
||||
tolerance := ui.Local(c.Root(), "198.tolerance", func() float64 { return 5 })
|
||||
if mujica.Slider(c, tolerance, mujica.SliderOptions{Min: 1, Max: 15, Step: 1, Label: "Drift tolerance", ShowValue: true}).Changed() {
|
||||
if input.Slider(c, tolerance, input.SliderOptions{Min: 1, Max: 15, Step: 1, Label: "Drift tolerance", ShowValue: true}).Changed() {
|
||||
log(fmt.Sprint(*tolerance))
|
||||
}
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
r := mujica.AssetAllocationChart(c, items, mujica.AssetAllocationChartOptions{Frame: frame, Tolerance: *tolerance})
|
||||
r := finance.AssetAllocationChart(c, items, finance.AssetAllocationChartOptions{Frame: frame, Tolerance: *tolerance})
|
||||
if r.Changed() || r.Retried() {
|
||||
log(fmt.Sprint(r.Point))
|
||||
}
|
||||
@@ -86,7 +91,7 @@ func marketsAssetAllocationChartDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func marketsPerformanceChartDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "199.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
values := *ui.Local(c.Root(), "markets.PerformanceChart.values.pages_markets_4", func() []float64 { return []float64{100, 110, 95, 120} })
|
||||
@@ -96,15 +101,15 @@ func marketsPerformanceChartDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
|
||||
status := ui.Local(c.Root(), "199.status", func() int { return 0 })
|
||||
if mujica.RadioGroup(c, status, []mujica.RadioItem[int]{{Value: 0, Label: "Ready"}, {Value: 1, Label: "Loading"}, {Value: 2, Label: "Empty"}, {Value: 3, Label: "Error"}}, mujica.RadioGroupOptions{Horizontal: true}).Changed() {
|
||||
if input.RadioGroup(c, status, []input.RadioItem[int]{{Value: 0, Label: "Ready"}, {Value: 1, Label: "Loading"}, {Value: 2, Label: "Empty"}, {Value: 3, Label: "Error"}}, input.RadioGroupOptions{Horizontal: true}).Changed() {
|
||||
log(fmt.Sprint("status ", *status))
|
||||
}
|
||||
frame := mujica.ChartFrame{Label: "PerformanceChart"}
|
||||
frame := chart.ChartFrame{Label: "PerformanceChart"}
|
||||
if *status == 1 {
|
||||
frame.Status = mujica.DataLoading
|
||||
frame.Status = data.DataLoading
|
||||
}
|
||||
if *status == 3 {
|
||||
frame.Status = mujica.DataFailed
|
||||
frame.Status = data.DataFailed
|
||||
frame.Error = "Sample failed"
|
||||
}
|
||||
if *status == 2 {
|
||||
@@ -113,7 +118,7 @@ func marketsPerformanceChartDemo(c *ui.Context, log func(string)) {
|
||||
times = nil
|
||||
}
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
r := mujica.PerformanceChart(c, values, mujica.PerformanceChartOptions{Frame: frame, Times: times, Benchmark: benchmark})
|
||||
r := finance.PerformanceChart(c, values, finance.PerformanceChartOptions{Frame: frame, Times: times, Benchmark: benchmark})
|
||||
if r.Changed() || r.Retried() {
|
||||
log(fmt.Sprint(r.Point))
|
||||
}
|
||||
@@ -125,21 +130,21 @@ func marketsPerformanceChartDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func marketsCurrencyConverterDemo(c *ui.Context, log func(string)) {
|
||||
disabled := ui.Local(c.Root(), "200.disabled", func() bool { return false })
|
||||
if mujica.Checkbox(c, disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprint("disabled ", *disabled))
|
||||
}
|
||||
state := ui.Local(c.Root(), "200.state", func() mujica.CurrencyConverterState {
|
||||
return mujica.CurrencyConverterState{Amount: 100, From: "USD", To: "JPY"}
|
||||
state := ui.Local(c.Root(), "200.state", func() finance.CurrencyConverterState {
|
||||
return finance.CurrencyConverterState{Amount: 100, From: "USD", To: "JPY"}
|
||||
})
|
||||
currencies := *ui.Local(c.Root(), "markets.CurrencyConverter.currencies.pages_markets_4", func() []mujica.Currency {
|
||||
return []mujica.Currency{{Code: "USD", Name: "Dollar", Decimals: 2}, {Code: "JPY", Name: "Yen", Decimals: 0}}
|
||||
currencies := *ui.Local(c.Root(), "markets.CurrencyConverter.currencies.pages_markets_4", func() []finance.Currency {
|
||||
return []finance.Currency{{Code: "USD", Name: "Dollar", Decimals: 2}, {Code: "JPY", Name: "Yen", Decimals: 0}}
|
||||
})
|
||||
rate := ui.Local(c.Root(), "200.rate", func() float64 { return 149.37 })
|
||||
|
||||
if mujica.NumberInput(c, rate, mujica.NumberInputOptions{Min: 0, Max: 1000, Step: 0.0001, Precision: 4, Label: "Caller rate"}).Changed() {
|
||||
if input.NumberInput(c, rate, input.NumberInputOptions{Min: 0, Max: 1000, Step: 0.0001, Precision: 4, Label: "Caller rate"}).Changed() {
|
||||
log(fmt.Sprint("rate ", *rate))
|
||||
}
|
||||
if mujica.Button(c, "Load pair rate", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Load pair rate", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
*rate = 149.37
|
||||
if state.From == "JPY" {
|
||||
*rate = 1 / 149.37
|
||||
@@ -148,7 +153,7 @@ func marketsCurrencyConverterDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
beforeAmount := state.Amount
|
||||
ui.Column(c).Disabled(*disabled).MinHeight(0).Height(400).Children(func() {
|
||||
r := mujica.CurrencyConverter(c, state, currencies, mujica.CurrencyConverterOptions{Rate: *rate, RateFrom: state.From, RateTo: state.To})
|
||||
r := finance.CurrencyConverter(c, state, currencies, finance.CurrencyConverterOptions{Rate: *rate, RateFrom: state.From, RateTo: state.To})
|
||||
if r.PairChanged {
|
||||
*rate = 0
|
||||
log(fmt.Sprint(state.From, state.To))
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
)
|
||||
|
||||
func marketsDemo(t *testing.T, id string) (*ui.Tester, *[]string) {
|
||||
@@ -16,7 +18,7 @@ func marketsDemo(t *testing.T, id string) (*ui.Tester, *[]string) {
|
||||
}
|
||||
events := &[]string{}
|
||||
tt := ui.NewTester(func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
core.Use(c, core.Settings{})
|
||||
ui.Scroll(c).Fill().Padding(24).Children(func() { marketsPages[i].Demo(c, func(s string) { *events = append(*events, s) }) })
|
||||
}, 1000, 800)
|
||||
tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1})
|
||||
|
||||
@@ -9,8 +9,9 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"mujicaui/theme"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||
)
|
||||
|
||||
const mediaCategory = "Media"
|
||||
@@ -61,7 +62,7 @@ var mediaPages = []page{
|
||||
// mediaSection titles a part of a demo.
|
||||
func mediaSection(c *ui.Context, title string, fn func()) {
|
||||
ui.Column(c).Gap(8).FillWidth().Children(func() {
|
||||
ui.Text(c, title).FontSize(mujica.FontSize(c, theme.CaptionSize)).TextColor(mujica.Tokens(c).TextMuted)
|
||||
ui.Text(c, title).FontSize(core.FontSize(c, theme.CaptionSize)).TextColor(core.Tokens(c).TextMuted)
|
||||
fn()
|
||||
})
|
||||
}
|
||||
@@ -69,7 +70,7 @@ func mediaSection(c *ui.Context, title string, fn func()) {
|
||||
// mediaCheck is a parameter checkbox that logs its changes.
|
||||
func mediaCheck(c *ui.Context, log func(string), v *bool, label string) {
|
||||
before := *v
|
||||
mujica.Checkbox(c, v, label, mujica.CheckboxOptions{})
|
||||
input.Checkbox(c, v, label, input.CheckboxOptions{})
|
||||
if *v != before {
|
||||
log(fmt.Sprintf("%s = %v", label, *v))
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/media"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/navigation"
|
||||
)
|
||||
|
||||
// mediaClockState simulates the caller's playback: position advances while playing.
|
||||
@@ -55,12 +57,12 @@ func videoPlayerDemo(c *ui.Context, log func(string)) {
|
||||
if s.noFrame {
|
||||
frame = nil
|
||||
}
|
||||
st := mujica.VideoState{Frame: frame, Position: s.clock.pos, Duration: s.clock.dur, Playing: s.clock.playing,
|
||||
Rate: s.clock.rate, Buffered: []mujica.MediaRange{{Start: 0, End: s.clock.pos + 40*time.Second}},
|
||||
st := media.VideoState{Frame: frame, Position: s.clock.pos, Duration: s.clock.dur, Playing: s.clock.playing,
|
||||
Rate: s.clock.rate, Buffered: []media.MediaRange{{Start: 0, End: s.clock.pos + 40*time.Second}},
|
||||
Subtitle: cues[int(s.clock.pos/(10*time.Second))%3], SubtitlesOn: s.subs, Fullscreen: s.full, PiP: s.pip}
|
||||
mediaSection(c, "Live", func() {
|
||||
ui.Column(c).MaxWidth(640).Children(func() {
|
||||
mujica.VideoPlayer(c, st, mujica.VideoPlayerOptions{
|
||||
media.VideoPlayer(c, st, media.VideoPlayerOptions{
|
||||
OnPlay: func() { s.clock.playing = true; log("play") },
|
||||
OnPause: func() { s.clock.playing = false; log("pause") },
|
||||
OnSeek: func(d time.Duration) { s.clock.pos = d; log("seek " + mediaClockLabel(d)) },
|
||||
@@ -86,8 +88,8 @@ func videoScrubberDemo(c *ui.Context, log func(string)) {
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
})
|
||||
})
|
||||
opts := mujica.VideoScrubberOptions{Duration: 3 * time.Minute, Disabled: s.disabled,
|
||||
Buffered: []mujica.MediaRange{{Start: 0, End: 90 * time.Second}, {Start: 120 * time.Second, End: 140 * time.Second}}}
|
||||
opts := media.VideoScrubberOptions{Duration: 3 * time.Minute, Disabled: s.disabled,
|
||||
Buffered: []media.MediaRange{{Start: 0, End: 90 * time.Second}, {Start: 120 * time.Second, End: 140 * time.Second}}}
|
||||
if s.preview {
|
||||
opts.Preview = func(at time.Duration) {
|
||||
ui.Image(c, mediaBitmaps[int(at/(time.Minute))%3]).Fit(ui.Cover).Fill()
|
||||
@@ -95,7 +97,7 @@ func videoScrubberDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
mediaSection(c, "Live", func() {
|
||||
ui.Column(c).MaxWidth(560).Padding(110, 0, 0, 0).Children(func() {
|
||||
if mujica.VideoScrubber(c, &s.pos, opts).Changed() {
|
||||
if media.VideoScrubber(c, &s.pos, opts).Changed() {
|
||||
log("seek " + mediaClockLabel(s.pos))
|
||||
}
|
||||
})
|
||||
@@ -112,8 +114,8 @@ func audioPlayerDemo(c *ui.Context, log func(string)) {
|
||||
mediaSection(c, "Parameters", func() { mediaCheck(c, log, &s.disabled, "Disabled") })
|
||||
mediaSection(c, "Live", func() {
|
||||
ui.Column(c).MaxWidth(560).Children(func() {
|
||||
mujica.AudioPlayer(c, mujica.AudioState{Title: "Ave Mujica — Overture", Position: s.clock.pos, Duration: s.clock.dur, Playing: s.clock.playing},
|
||||
mujica.AudioPlayerOptions{
|
||||
media.AudioPlayer(c, media.AudioState{Title: "Ave Mujica — Overture", Position: s.clock.pos, Duration: s.clock.dur, Playing: s.clock.playing},
|
||||
media.AudioPlayerOptions{
|
||||
OnPlay: func() { s.clock.playing = true; log("play") },
|
||||
OnPause: func() { s.clock.playing = false; log("pause") },
|
||||
OnSeek: func(d time.Duration) { s.clock.pos = d; log("seek " + mediaClockLabel(d)) },
|
||||
@@ -136,7 +138,7 @@ var mediaSamples = func() []float32 {
|
||||
func audioWaveformDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
pos time.Duration
|
||||
sel mujica.MediaRange
|
||||
sel media.MediaRange
|
||||
status int
|
||||
selecting bool
|
||||
}
|
||||
@@ -144,28 +146,28 @@ func audioWaveformDemo(c *ui.Context, log func(string)) {
|
||||
mediaSection(c, "Parameters", func() {
|
||||
ui.Row(c).Gap(16).AlignItems(ui.Center).Children(func() {
|
||||
before := s.status
|
||||
mujica.SegmentedControl(c, &s.status, []mujica.Segment{{Label: "Ready"}, {Label: "Loading"}, {Label: "Failed"}, {Label: "Empty"}}, mujica.SegmentedControlOptions{Label: "Status"})
|
||||
navigation.SegmentedControl(c, &s.status, []navigation.Segment{{Label: "Ready"}, {Label: "Loading"}, {Label: "Failed"}, {Label: "Empty"}}, navigation.SegmentedControlOptions{Label: "Status"})
|
||||
if s.status != before {
|
||||
log(fmt.Sprintf("status %d", s.status))
|
||||
}
|
||||
mediaCheck(c, log, &s.selecting, "Selectable")
|
||||
})
|
||||
})
|
||||
opts := mujica.AudioWaveformOptions{Duration: time.Minute, Position: s.pos, OnSeek: func(d time.Duration) { s.pos = d; log("seek " + mediaClockLabel(d)) }}
|
||||
opts := media.AudioWaveformOptions{Duration: time.Minute, Position: s.pos, OnSeek: func(d time.Duration) { s.pos = d; log("seek " + mediaClockLabel(d)) }}
|
||||
if s.selecting {
|
||||
opts.Selection = &s.sel
|
||||
}
|
||||
data := mediaSamples
|
||||
samples := mediaSamples
|
||||
switch s.status {
|
||||
case 1:
|
||||
opts.Status, data = mujica.DataLoading, nil
|
||||
opts.Status, samples = data.DataLoading, nil
|
||||
case 2:
|
||||
opts.Status, opts.Error, data = mujica.DataFailed, "The recording could not be decoded.", nil
|
||||
opts.Status, opts.Error, samples = data.DataFailed, "The recording could not be decoded.", nil
|
||||
case 3:
|
||||
data = nil
|
||||
samples = nil
|
||||
}
|
||||
mediaSection(c, "Live", func() {
|
||||
if mujica.AudioWaveform(c, data, opts).Changed() {
|
||||
if media.AudioWaveform(c, samples, opts).Changed() {
|
||||
log(fmt.Sprintf("selection %s – %s", mediaClockLabel(s.sel.Start), mediaClockLabel(s.sel.End)))
|
||||
}
|
||||
})
|
||||
@@ -186,6 +188,6 @@ func audioSpectrumDemo(c *ui.Context, log func(string)) {
|
||||
c.AnimationFrame()
|
||||
}
|
||||
mediaSection(c, "Live", func() {
|
||||
mujica.AudioSpectrum(c, bands, mujica.AudioSpectrumOptions{BandLabel: func(i int) string { return freqs[i] }})
|
||||
media.AudioSpectrum(c, bands, media.AudioSpectrumOptions{BandLabel: func(i int) string { return freqs[i] }})
|
||||
})
|
||||
}
|
||||
@@ -2,20 +2,20 @@ package main
|
||||
|
||||
// Media page snippets, each its compiled Example's body.
|
||||
const (
|
||||
imageCropperCode = `crop := mujica.ImageCrop{} // empty: starts fitted to the image
|
||||
imageCropperCode = `crop := media.ImageCrop{} // empty: starts fitted to the image
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.ImageCropper(c, examplePhoto, &crop, mujica.ImageCropperOptions{Aspect: 1}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if media.ImageCropper(c, examplePhoto, &crop, media.ImageCropperOptions{Aspect: 1}).Changed() {
|
||||
_ = crop.Rect // in pixels of the photo turned by crop.Rotation
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
imageAnnotatorCode = `photo := ui.NewBitmap(examplePhoto)
|
||||
var notes []mujica.Annotation
|
||||
var notes []media.Annotation
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.ImageAnnotator(c, photo, ¬es, mujica.ImageAnnotatorOptions{}).Changed() {
|
||||
data, _ := mujica.ExportAnnotations(notes)
|
||||
core.Use(c, core.Settings{})
|
||||
if media.ImageAnnotator(c, photo, ¬es, media.ImageAnnotatorOptions{}).Changed() {
|
||||
data, _ := media.ExportAnnotations(notes)
|
||||
_ = data // save the JSON
|
||||
}
|
||||
}
|
||||
@@ -23,26 +23,26 @@ ui.Render(view, 400, 300, 1)`
|
||||
imageThumbnailCode = `photo := ui.NewBitmap(examplePhoto)
|
||||
selected := false
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.ImageThumbnail(c, photo, mujica.ImageThumbnailOptions{Alt: "Grand hall", Aspect: 4.0 / 3, Selected: selected}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if media.ImageThumbnail(c, photo, media.ImageThumbnailOptions{Alt: "Grand hall", Aspect: 4.0 / 3, Selected: selected}).Changed() {
|
||||
selected = !selected
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
lightboxCode = `images := []mujica.ViewerImage{{Source: ui.NewBitmap(examplePhoto), Alt: "The grand hall"}}
|
||||
lightboxCode = `images := []data.ViewerImage{{Source: ui.NewBitmap(examplePhoto), Alt: "The grand hall"}}
|
||||
open, index := false, 0
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.Button(c, "View photos", mujica.ButtonOptions{}).Clicked() {
|
||||
core.Use(c, core.Settings{})
|
||||
if input.Button(c, "View photos", input.ButtonOptions{}).Clicked() {
|
||||
open = true
|
||||
}
|
||||
mujica.Lightbox(c, &open, &index, images)
|
||||
media.Lightbox(c, &open, &index, images)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
videoPlayerCode = `st := mujica.VideoState{Duration: 4 * time.Minute, Rate: 1}
|
||||
videoPlayerCode = `st := media.VideoState{Duration: 4 * time.Minute, Rate: 1}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.VideoPlayer(c, st, mujica.VideoPlayerOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
media.VideoPlayer(c, st, media.VideoPlayerOptions{
|
||||
OnPlay: func() { st.Playing = true }, // start your decoder
|
||||
OnPause: func() { st.Playing = false }, // pause it
|
||||
OnSeek: func(d time.Duration) { st.Position = d },
|
||||
@@ -52,26 +52,26 @@ view := func(c *ui.Context) {
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
videoScrubberCode = `pos := 30 * time.Second
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.VideoScrubber(c, &pos, mujica.VideoScrubberOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
media.VideoScrubber(c, &pos, media.VideoScrubberOptions{
|
||||
Duration: 2 * time.Minute,
|
||||
Buffered: []mujica.MediaRange{{Start: 0, End: time.Minute}},
|
||||
Buffered: []media.MediaRange{{Start: 0, End: time.Minute}},
|
||||
})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
videoStripCode = `frames := []mujica.StripFrame{{At: 0}, {At: 10 * time.Second}, {At: 20 * time.Second}}
|
||||
videoStripCode = `frames := []media.StripFrame{{At: 0}, {At: 10 * time.Second}, {At: 20 * time.Second}}
|
||||
current := 0
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.VideoThumbnailStrip(c, ¤t, frames, mujica.VideoThumbnailStripOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if media.VideoThumbnailStrip(c, ¤t, frames, media.VideoThumbnailStripOptions{}).Changed() {
|
||||
_ = frames[current].At // seek the player there
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
audioPlayerCode = `st := mujica.AudioState{Title: "Overture", Duration: 3 * time.Minute}
|
||||
audioPlayerCode = `st := media.AudioState{Title: "Overture", Duration: 3 * time.Minute}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.AudioPlayer(c, st, mujica.AudioPlayerOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
media.AudioPlayer(c, st, media.AudioPlayerOptions{
|
||||
OnPlay: func() { st.Playing = true },
|
||||
OnPause: func() { st.Playing = false },
|
||||
OnSeek: func(d time.Duration) { st.Position = d },
|
||||
@@ -79,10 +79,10 @@ view := func(c *ui.Context) {
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
audioWaveformCode = `samples := make([]float32, 48000) // from your decoder, -1 to 1
|
||||
pos, sel := time.Duration(0), mujica.MediaRange{}
|
||||
pos, sel := time.Duration(0), media.MediaRange{}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.AudioWaveform(c, samples, mujica.AudioWaveformOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
media.AudioWaveform(c, samples, media.AudioWaveformOptions{
|
||||
Duration: time.Second, Position: pos, Selection: &sel,
|
||||
OnSeek: func(d time.Duration) { pos = d },
|
||||
})
|
||||
@@ -90,89 +90,89 @@ view := func(c *ui.Context) {
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
audioSpectrumCode = `bands := []float32{0.2, 0.6, 0.9, 0.4, 0.1} // set by your analyser, then c.Invalidate()
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.AudioSpectrum(c, bands, mujica.AudioSpectrumOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
media.AudioSpectrum(c, bands, media.AudioSpectrumOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
volumeCode = `volume, muted := 0.8, false
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.VolumeControl(c, &volume, &muted, mujica.VolumeControlOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
media.VolumeControl(c, &volume, &muted, media.VolumeControlOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
speedCode = `rate := 1.0
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.PlaybackSpeedControl(c, &rate, mujica.PlaybackSpeedControlOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
media.PlaybackSpeedControl(c, &rate, media.PlaybackSpeedControlOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
mediaControlsCode = `st := mujica.MediaControlsState{}
|
||||
mediaControlsCode = `st := media.MediaControlsState{}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.MediaControls(c, st, mujica.MediaControlsOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
media.MediaControls(c, st, media.MediaControlsOptions{
|
||||
OnPrevious: func() {}, OnNext: func() {},
|
||||
OnPlay: func() { st.Playing = true }, OnPause: func() { st.Playing = false },
|
||||
OnLoop: func(m mujica.LoopMode) { st.Loop = m }, OnShuffle: func(on bool) { st.Shuffle = on },
|
||||
OnLoop: func(m media.LoopMode) { st.Loop = m }, OnShuffle: func(on bool) { st.Shuffle = on },
|
||||
})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
playlistCode = `items := []mujica.PlaylistItem{{ID: "a", Title: "Overture"}, {ID: "b", Title: "Masquerade"}}
|
||||
playlistCode = `items := []media.PlaylistItem{{ID: "a", Title: "Overture"}, {ID: "b", Title: "Masquerade"}}
|
||||
current := "a"
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if r := mujica.Playlist(c, &items, mujica.PlaylistOptions{Current: current}); r.Played != "" {
|
||||
core.Use(c, core.Settings{})
|
||||
if r := media.Playlist(c, &items, media.PlaylistOptions{Current: current}); r.Played != "" {
|
||||
current = r.Played
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
subtitleCode = `cues := []mujica.SubtitleCue{{Start: 0, End: 2 * time.Second, Text: "The curtain rises."}}
|
||||
subtitleCode = `cues := []media.SubtitleCue{{Start: 0, End: 2 * time.Second, Text: "The curtain rises."}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.SubtitleEditor(c, &cues, mujica.SubtitleEditorOptions{})
|
||||
if len(mujica.CheckSubtitles(cues)) > 0 {
|
||||
core.Use(c, core.Settings{})
|
||||
media.SubtitleEditor(c, &cues, media.SubtitleEditorOptions{})
|
||||
if len(media.CheckSubtitles(cues)) > 0 {
|
||||
_ = cues // block saving until the issues are fixed
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
recorderCode = `st := mujica.RecorderState{}
|
||||
recorderCode = `st := media.RecorderState{}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ScreenRecorderControls(c, st, mujica.ScreenRecorderControlsOptions{
|
||||
OnStart: func() { st.Status = mujica.RecorderRecording },
|
||||
OnPause: func() { st.Status = mujica.RecorderPaused },
|
||||
OnResume: func() { st.Status = mujica.RecorderRecording },
|
||||
OnStop: func() { st = mujica.RecorderState{} },
|
||||
core.Use(c, core.Settings{})
|
||||
media.ScreenRecorderControls(c, st, media.ScreenRecorderControlsOptions{
|
||||
OnStart: func() { st.Status = media.RecorderRecording },
|
||||
OnPause: func() { st.Status = media.RecorderPaused },
|
||||
OnResume: func() { st.Status = media.RecorderRecording },
|
||||
OnStop: func() { st = media.RecorderState{} },
|
||||
})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
cameraCode = `frame := mujica.CameraFrame{Image: examplePhoto, Seq: 1} // a new Seq for each captured frame
|
||||
cameraCode = `frame := media.CameraFrame{Image: examplePhoto, Seq: 1} // a new Seq for each captured frame
|
||||
mirror := true
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.CameraPreview(c, frame, &mirror, mujica.CameraPreviewOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
media.CameraPreview(c, frame, &mirror, media.CameraPreviewOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
micCode = `level := float32(0.4) // the input's peak, 0 to 1 of full scale
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.MicLevelMeter(c, level, mujica.MicLevelMeterOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
media.MicLevelMeter(c, level, media.MicLevelMeterOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
deviceCode = `devices := mujica.MediaDevices{
|
||||
Microphones: []mujica.MediaDevice{{ID: "mic-1", Name: "Built-in Microphone"}},
|
||||
Speakers: []mujica.MediaDevice{{ID: "out-1", Name: "Headphones"}},
|
||||
deviceCode = `devices := media.MediaDevices{
|
||||
Microphones: []media.MediaDevice{{ID: "mic-1", Name: "Built-in Microphone"}},
|
||||
Speakers: []media.MediaDevice{{ID: "out-1", Name: "Headphones"}},
|
||||
}
|
||||
sel := mujica.DeviceSelection{Microphone: "mic-1"}
|
||||
sel := media.DeviceSelection{Microphone: "mic-1"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.DeviceSelector(c, &sel, devices, mujica.DeviceSelectorOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
media.DeviceSelector(c, &sel, devices, media.DeviceSelectorOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
compareCode = `before, after := ui.NewBitmap(examplePhoto), ui.NewBitmap(examplePhoto)
|
||||
split := 0.5
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ImageCompare(c, before, after, &split, mujica.ImageCompareOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
media.ImageCompare(c, before, after, &split, media.ImageCompareOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
)
|
||||
@@ -7,7 +7,8 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/media"
|
||||
)
|
||||
|
||||
func volumeDemo(c *ui.Context, log func(string)) {
|
||||
@@ -18,7 +19,7 @@ func volumeDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "351", func() state { return state{volume: 0.7} })
|
||||
mediaSection(c, "Parameters", func() { mediaCheck(c, log, &s.disabled, "Disabled") })
|
||||
mediaSection(c, "Live", func() {
|
||||
if mujica.VolumeControl(c, &s.volume, &s.muted, mujica.VolumeControlOptions{Disabled: s.disabled}).Changed() {
|
||||
if media.VolumeControl(c, &s.volume, &s.muted, media.VolumeControlOptions{Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("volume %.0f%%, muted %v", s.volume*100, s.muted))
|
||||
}
|
||||
})
|
||||
@@ -32,7 +33,7 @@ func speedDemo(c *ui.Context, log func(string)) {
|
||||
s := ui.Local(c.Root(), "352", func() state { return state{rate: 1} })
|
||||
mediaSection(c, "Parameters", func() { mediaCheck(c, log, &s.disabled, "Disabled") })
|
||||
mediaSection(c, "Live", func() {
|
||||
if mujica.PlaybackSpeedControl(c, &s.rate, mujica.PlaybackSpeedControlOptions{Disabled: s.disabled}).Changed() {
|
||||
if media.PlaybackSpeedControl(c, &s.rate, media.PlaybackSpeedControlOptions{Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("rate %v×", s.rate))
|
||||
}
|
||||
})
|
||||
@@ -40,7 +41,7 @@ func speedDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func mediaControlsDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
st mujica.MediaControlsState
|
||||
st media.MediaControlsState
|
||||
track int
|
||||
disabled bool
|
||||
}
|
||||
@@ -50,12 +51,12 @@ func mediaControlsDemo(c *ui.Context, log func(string)) {
|
||||
mediaSection(c, "Parameters", func() { mediaCheck(c, log, &s.disabled, "Disabled") })
|
||||
mediaSection(c, "Live", func() {
|
||||
ui.Text(c, fmt.Sprintf("Now: %s (%d of %d)", tracks[s.track], s.track+1, len(tracks)))
|
||||
mujica.MediaControls(c, s.st, mujica.MediaControlsOptions{
|
||||
media.MediaControls(c, s.st, media.MediaControlsOptions{
|
||||
OnPrevious: func() { s.track--; log("previous") },
|
||||
OnNext: func() { s.track++; log("next") },
|
||||
OnPlay: func() { s.st.Playing = true; log("play") },
|
||||
OnPause: func() { s.st.Playing = false; log("pause") },
|
||||
OnLoop: func(m mujica.LoopMode) { s.st.Loop = m; log(fmt.Sprintf("loop mode %d", m)) },
|
||||
OnLoop: func(m media.LoopMode) { s.st.Loop = m; log(fmt.Sprintf("loop mode %d", m)) },
|
||||
OnShuffle: func(on bool) { s.st.Shuffle = on; log(fmt.Sprintf("shuffle %v", on)) },
|
||||
Disabled: s.disabled,
|
||||
})
|
||||
@@ -64,31 +65,31 @@ func mediaControlsDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func playlistDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
items []mujica.PlaylistItem
|
||||
items []media.PlaylistItem
|
||||
current string
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "354", func() state {
|
||||
titles := []string{"Ave Mujica", "Imprisoned XII", "KiLLKiSS", "Symbol I: △", "Choir 'S' Choir", "Georgette Me, Georgette You"}
|
||||
items := make([]mujica.PlaylistItem, len(titles))
|
||||
items := make([]media.PlaylistItem, len(titles))
|
||||
for i, t := range titles {
|
||||
items[i] = mujica.PlaylistItem{ID: fmt.Sprint("t", i), Title: t, Subtitle: fmt.Sprintf("Act %d", i+1), Duration: time.Duration(200+i*17) * time.Second}
|
||||
items[i] = media.PlaylistItem{ID: fmt.Sprint("t", i), Title: t, Subtitle: fmt.Sprintf("Act %d", i+1), Duration: time.Duration(200+i*17) * time.Second}
|
||||
}
|
||||
return state{items: items, current: "t1"}
|
||||
})
|
||||
mediaSection(c, "Parameters", func() {
|
||||
ui.Row(c).Gap(16).AlignItems(ui.Center).Children(func() {
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.Button(c, "Add track", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Add track", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
id := fmt.Sprint("t", len(s.items)+int(time.Now().UnixNano()%1000000))
|
||||
s.items = append(s.items, mujica.PlaylistItem{ID: id, Title: "Encore " + id, Duration: 3 * time.Minute})
|
||||
s.items = append(s.items, media.PlaylistItem{ID: id, Title: "Encore " + id, Duration: 3 * time.Minute})
|
||||
log("added " + id)
|
||||
}
|
||||
})
|
||||
})
|
||||
mediaSection(c, "Live", func() {
|
||||
ui.Column(c).MaxWidth(520).Children(func() {
|
||||
r := mujica.Playlist(c, &s.items, mujica.PlaylistOptions{Current: s.current, Disabled: s.disabled})
|
||||
r := media.Playlist(c, &s.items, media.PlaylistOptions{Current: s.current, Disabled: s.disabled})
|
||||
switch {
|
||||
case r.Played != "":
|
||||
s.current = r.Played
|
||||
@@ -104,11 +105,11 @@ func playlistDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func subtitleDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
cues []mujica.SubtitleCue
|
||||
cues []media.SubtitleCue
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "355", func() state {
|
||||
return state{cues: []mujica.SubtitleCue{
|
||||
return state{cues: []media.SubtitleCue{
|
||||
{Start: 0, End: 2500 * time.Millisecond, Text: "The curtain rises."},
|
||||
{Start: 2 * time.Second, End: 5 * time.Second, Text: "Welcome to the masquerade."},
|
||||
{Start: 6 * time.Second, End: 9 * time.Second, Text: "Ave Mujica."},
|
||||
@@ -117,8 +118,8 @@ func subtitleDemo(c *ui.Context, log func(string)) {
|
||||
mediaSection(c, "Parameters", func() { mediaCheck(c, log, &s.disabled, "Disabled") })
|
||||
mediaSection(c, "Live", func() {
|
||||
ui.Column(c).MaxWidth(720).Children(func() {
|
||||
if mujica.SubtitleEditor(c, &s.cues, mujica.SubtitleEditorOptions{Height: 260, Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("%d cues, %d issues", len(s.cues), len(mujica.CheckSubtitles(s.cues))))
|
||||
if media.SubtitleEditor(c, &s.cues, media.SubtitleEditorOptions{Height: 260, Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("%d cues, %d issues", len(s.cues), len(media.CheckSubtitles(s.cues))))
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -126,27 +127,27 @@ func subtitleDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func recorderDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
st mujica.RecorderState
|
||||
st media.RecorderState
|
||||
since time.Time
|
||||
disabled bool
|
||||
accumulated time.Duration
|
||||
}
|
||||
s := ui.Local(c.Root(), "356", func() state { return state{} })
|
||||
if s.st.Status == mujica.RecorderRecording {
|
||||
if s.st.Status == media.RecorderRecording {
|
||||
s.st.Elapsed = s.accumulated + c.Now().Sub(s.since)
|
||||
// The clock changes each second; ask for that frame only.
|
||||
c.After(time.Second - s.st.Elapsed%time.Second)
|
||||
}
|
||||
mediaSection(c, "Parameters", func() { mediaCheck(c, log, &s.disabled, "Disabled") })
|
||||
mediaSection(c, "Live", func() {
|
||||
mujica.ScreenRecorderControls(c, s.st, mujica.ScreenRecorderControlsOptions{
|
||||
media.ScreenRecorderControls(c, s.st, media.ScreenRecorderControlsOptions{
|
||||
OnStart: func() {
|
||||
s.st, s.since, s.accumulated = mujica.RecorderState{Status: mujica.RecorderRecording}, c.Now(), 0
|
||||
s.st, s.since, s.accumulated = media.RecorderState{Status: media.RecorderRecording}, c.Now(), 0
|
||||
log("start")
|
||||
},
|
||||
OnPause: func() { s.st.Status, s.accumulated = mujica.RecorderPaused, s.st.Elapsed; log("pause") },
|
||||
OnResume: func() { s.st.Status, s.since = mujica.RecorderRecording, c.Now(); log("resume") },
|
||||
OnStop: func() { log("stop at " + mediaClockLabel(s.st.Elapsed)); s.st = mujica.RecorderState{} },
|
||||
OnPause: func() { s.st.Status, s.accumulated = media.RecorderPaused, s.st.Elapsed; log("pause") },
|
||||
OnResume: func() { s.st.Status, s.since = media.RecorderRecording, c.Now(); log("resume") },
|
||||
OnStop: func() { log("stop at " + mediaClockLabel(s.st.Elapsed)); s.st = media.RecorderState{} },
|
||||
Disabled: s.disabled,
|
||||
})
|
||||
})
|
||||
@@ -163,13 +164,13 @@ func cameraDemo(c *ui.Context, log func(string)) {
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
})
|
||||
})
|
||||
frame := mujica.CameraFrame{}
|
||||
frame := media.CameraFrame{}
|
||||
if s.signal {
|
||||
frame = mujica.CameraFrame{Image: mediaPhotos[2], Seq: 1}
|
||||
frame = media.CameraFrame{Image: mediaPhotos[2], Seq: 1}
|
||||
}
|
||||
mediaSection(c, "Live", func() {
|
||||
ui.Column(c).MaxWidth(480).Children(func() {
|
||||
if mujica.CameraPreview(c, frame, &s.mirror, mujica.CameraPreviewOptions{Disabled: s.disabled}).Changed() {
|
||||
if media.CameraPreview(c, frame, &s.mirror, media.CameraPreviewOptions{Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("mirror %v", s.mirror))
|
||||
}
|
||||
})
|
||||
@@ -187,20 +188,20 @@ func micDemo(c *ui.Context, log func(string)) {
|
||||
c.AnimationFrame()
|
||||
}
|
||||
mediaSection(c, "Live", func() {
|
||||
ui.Column(c).MaxWidth(420).Children(func() { mujica.MicLevelMeter(c, level, mujica.MicLevelMeterOptions{}) })
|
||||
ui.Column(c).MaxWidth(420).Children(func() { media.MicLevelMeter(c, level, media.MicLevelMeterOptions{}) })
|
||||
})
|
||||
}
|
||||
|
||||
func deviceDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
sel mujica.DeviceSelection
|
||||
sel media.DeviceSelection
|
||||
noCamera, disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "359", func() state { return state{sel: mujica.DeviceSelection{Microphone: "m1", Speaker: "s2"}} })
|
||||
devices := mujica.MediaDevices{
|
||||
Microphones: []mujica.MediaDevice{{ID: "m1", Name: "Built-in Microphone"}, {ID: "m2", Name: "Studio Condenser"}},
|
||||
Cameras: []mujica.MediaDevice{{ID: "c1", Name: "FaceTime HD Camera"}},
|
||||
Speakers: []mujica.MediaDevice{{ID: "s1", Name: "Built-in Speakers"}, {ID: "s2", Name: "Headphones"}},
|
||||
s := ui.Local(c.Root(), "359", func() state { return state{sel: media.DeviceSelection{Microphone: "m1", Speaker: "s2"}} })
|
||||
devices := media.MediaDevices{
|
||||
Microphones: []media.MediaDevice{{ID: "m1", Name: "Built-in Microphone"}, {ID: "m2", Name: "Studio Condenser"}},
|
||||
Cameras: []media.MediaDevice{{ID: "c1", Name: "FaceTime HD Camera"}},
|
||||
Speakers: []media.MediaDevice{{ID: "s1", Name: "Built-in Speakers"}, {ID: "s2", Name: "Headphones"}},
|
||||
}
|
||||
mediaSection(c, "Parameters", func() {
|
||||
ui.Row(c).Gap(16).Children(func() {
|
||||
@@ -213,11 +214,11 @@ func deviceDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
})
|
||||
if s.noCamera {
|
||||
devices.Cameras = []mujica.MediaDevice{}
|
||||
devices.Cameras = []media.MediaDevice{}
|
||||
}
|
||||
mediaSection(c, "Live", func() {
|
||||
ui.Column(c).MaxWidth(480).Children(func() {
|
||||
if mujica.DeviceSelector(c, &s.sel, devices, mujica.DeviceSelectorOptions{Disabled: s.disabled}).Changed() {
|
||||
if media.DeviceSelector(c, &s.sel, devices, media.DeviceSelectorOptions{Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("microphone %q, camera %q, speaker %q", s.sel.Microphone, s.sel.Camera, s.sel.Speaker))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -8,12 +8,15 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/media"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/navigation"
|
||||
)
|
||||
|
||||
func imageCropperDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
crop mujica.ImageCrop
|
||||
crop media.ImageCrop
|
||||
aspect int
|
||||
disabled bool
|
||||
}
|
||||
@@ -22,7 +25,7 @@ func imageCropperDemo(c *ui.Context, log func(string)) {
|
||||
mediaSection(c, "Parameters", func() {
|
||||
ui.Row(c).Gap(16).AlignItems(ui.Center).Children(func() {
|
||||
before := s.aspect
|
||||
mujica.SegmentedControl(c, &s.aspect, []mujica.Segment{{Label: "Free"}, {Label: "1:1"}, {Label: "16:9"}, {Label: "4:3"}}, mujica.SegmentedControlOptions{Label: "Aspect"})
|
||||
navigation.SegmentedControl(c, &s.aspect, []navigation.Segment{{Label: "Free"}, {Label: "1:1"}, {Label: "16:9"}, {Label: "4:3"}}, navigation.SegmentedControlOptions{Label: "Aspect"})
|
||||
if s.aspect != before {
|
||||
s.crop.Rect = image.Rectangle{} // refit to the new ratio
|
||||
log(fmt.Sprintf("aspect = %v", aspects[s.aspect]))
|
||||
@@ -31,7 +34,7 @@ func imageCropperDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
})
|
||||
mediaSection(c, "Live", func() {
|
||||
if mujica.ImageCropper(c, mediaPhotos[0], &s.crop, mujica.ImageCropperOptions{Aspect: aspects[s.aspect], Disabled: s.disabled}).Changed() {
|
||||
if media.ImageCropper(c, mediaPhotos[0], &s.crop, media.ImageCropperOptions{Aspect: aspects[s.aspect], Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("crop %v rotated %d°", s.crop.Rect, s.crop.Rotation))
|
||||
}
|
||||
})
|
||||
@@ -39,17 +42,17 @@ func imageCropperDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func imageAnnotatorDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
notes []mujica.Annotation
|
||||
notes []media.Annotation
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "342", func() state {
|
||||
return state{notes: []mujica.Annotation{{Kind: mujica.AnnotateRect, From: image.Pt(150, 60), To: image.Pt(330, 230)}}}
|
||||
return state{notes: []media.Annotation{{Kind: media.AnnotateRect, From: image.Pt(150, 60), To: image.Pt(330, 230)}}}
|
||||
})
|
||||
mediaSection(c, "Parameters", func() {
|
||||
ui.Row(c).Gap(16).AlignItems(ui.Center).Children(func() {
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.Button(c, "Export JSON", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
data, err := mujica.ExportAnnotations(s.notes)
|
||||
if input.Button(c, "Export JSON", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
data, err := media.ExportAnnotations(s.notes)
|
||||
if err != nil {
|
||||
log("export failed: " + err.Error())
|
||||
} else {
|
||||
@@ -60,7 +63,7 @@ func imageAnnotatorDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
})
|
||||
mediaSection(c, "Live", func() {
|
||||
if mujica.ImageAnnotator(c, mediaBitmaps[1], &s.notes, mujica.ImageAnnotatorOptions{Disabled: s.disabled}).Changed() {
|
||||
if media.ImageAnnotator(c, mediaBitmaps[1], &s.notes, media.ImageAnnotatorOptions{Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("annotations: %d", len(s.notes)))
|
||||
}
|
||||
})
|
||||
@@ -86,9 +89,9 @@ func imageThumbnailDemo(c *ui.Context, log func(string)) {
|
||||
if s.failed && i == 2 {
|
||||
err = errors.New("the file could not be read")
|
||||
}
|
||||
o := mujica.ImageThumbnailOptions{Alt: fmt.Sprintf("Scene %d", i+1), Aspect: 4.0 / 3, Width: 120,
|
||||
o := media.ImageThumbnailOptions{Alt: fmt.Sprintf("Scene %d", i+1), Aspect: 4.0 / 3, Width: 120,
|
||||
Selected: s.selected == i, Loading: s.loading && i == 1, Err: err, Disabled: s.disabled}
|
||||
if mujica.ImageThumbnail(c, b, o).Changed() {
|
||||
if media.ImageThumbnail(c, b, o).Changed() {
|
||||
s.selected = i
|
||||
log(fmt.Sprintf("selected scene %d", i+1))
|
||||
}
|
||||
@@ -103,7 +106,7 @@ func lightboxDemo(c *ui.Context, log func(string)) {
|
||||
index int
|
||||
}
|
||||
s := ui.Local(c.Root(), "344", func() state { return state{} })
|
||||
images := []mujica.ViewerImage{
|
||||
images := []data.ViewerImage{
|
||||
{Source: mediaBitmaps[0], Alt: "The grand hall at dusk"},
|
||||
{Source: mediaBitmaps[1], Alt: "The ivory gallery"},
|
||||
{Source: mediaBitmaps[2], Alt: "The blue chapel"},
|
||||
@@ -111,14 +114,14 @@ func lightboxDemo(c *ui.Context, log func(string)) {
|
||||
mediaSection(c, "Live", func() {
|
||||
ui.Row(c).Gap(12).Children(func() {
|
||||
for i, im := range images {
|
||||
if mujica.ImageThumbnail(c, im.Source, mujica.ImageThumbnailOptions{Alt: im.Alt, Aspect: 4.0 / 3}).Changed() {
|
||||
if media.ImageThumbnail(c, im.Source, media.ImageThumbnailOptions{Alt: im.Alt, Aspect: 4.0 / 3}).Changed() {
|
||||
s.open, s.index = true, i
|
||||
log(fmt.Sprintf("open at %d", i+1))
|
||||
}
|
||||
}
|
||||
})
|
||||
was := s.open
|
||||
if mujica.Lightbox(c, &s.open, &s.index, images).Changed() {
|
||||
if media.Lightbox(c, &s.open, &s.index, images).Changed() {
|
||||
log(fmt.Sprintf("image %d", s.index+1))
|
||||
}
|
||||
if was && !s.open {
|
||||
@@ -133,13 +136,13 @@ func videoStripDemo(c *ui.Context, log func(string)) {
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "347", func() state { return state{} })
|
||||
frames := make([]mujica.StripFrame, 240)
|
||||
frames := make([]media.StripFrame, 240)
|
||||
for i := range frames {
|
||||
frames[i] = mujica.StripFrame{Image: mediaBitmaps[i%3], At: time.Duration(i) * 5 * time.Second}
|
||||
frames[i] = media.StripFrame{Image: mediaBitmaps[i%3], At: time.Duration(i) * 5 * time.Second}
|
||||
}
|
||||
mediaSection(c, "Parameters", func() { mediaCheck(c, log, &s.disabled, "Disabled") })
|
||||
mediaSection(c, "Live", func() {
|
||||
if mujica.VideoThumbnailStrip(c, &s.current, frames, mujica.VideoThumbnailStripOptions{Disabled: s.disabled}).Changed() {
|
||||
if media.VideoThumbnailStrip(c, &s.current, frames, media.VideoThumbnailStripOptions{Disabled: s.disabled}).Changed() {
|
||||
log("jump to " + mediaClockLabel(frames[s.current].At))
|
||||
}
|
||||
})
|
||||
@@ -154,7 +157,7 @@ func compareDemo(c *ui.Context, log func(string)) {
|
||||
mediaSection(c, "Parameters", func() { mediaCheck(c, log, &s.disabled, "Disabled") })
|
||||
mediaSection(c, "Live", func() {
|
||||
ui.Column(c).MaxWidth(560).Children(func() {
|
||||
if mujica.ImageCompare(c, mediaBitmaps[0], mediaBitmaps[1], &s.split, mujica.ImageCompareOptions{Disabled: s.disabled}).Changed() {
|
||||
if media.ImageCompare(c, mediaBitmaps[0], mediaBitmaps[1], &s.split, media.ImageCompareOptions{Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("split %.0f%%", s.split*100))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -4,85 +4,85 @@ const messagingCategory = "Messaging & Mail"
|
||||
|
||||
var messagingPages = []page{
|
||||
{ID: "401", Name: "ChannelList", ZhName: "频道列表", Category: messagingCategory, Demo: channelListDemo,
|
||||
Keys: "Tab 进入;上下方向键切换行;Enter / Space 选择或折叠;分组标题上的左、右方向键折叠、展开。", Code: `groups := []mujica.ChannelGroup{{ID: "ch", Title: "Channels", Channels: []mujica.Channel{
|
||||
Keys: "Tab 进入;上下方向键切换行;Enter / Space 选择或折叠;分组标题上的左、右方向键折叠、展开。", Code: `groups := []messaging.ChannelGroup{{ID: "ch", Title: "Channels", Channels: []messaging.Channel{
|
||||
{ID: "general", Name: "general", Unread: 3},
|
||||
{ID: "rehearsal", Name: "rehearsal", Pinned: true},
|
||||
}}}
|
||||
selected := "general"
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.ChannelList(c, &selected, groups, mujica.ChannelListOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if messaging.ChannelList(c, &selected, groups, messaging.ChannelListOptions{}).Changed() {
|
||||
_ = selected // show that channel
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "402", Name: "ChatMessage", ZhName: "聊天消息", Category: messagingCategory, Demo: chatMessageDemo,
|
||||
Keys: "可选文字支持选择复制;插槽控件按 Tab 访问。", Code: `prev := mujica.ChatEntry{ID: "1", Author: "Uika", Time: time.Now(), Text: "Rehearsal at nine."}
|
||||
m := mujica.ChatEntry{ID: "2", Author: "Uika", Time: time.Now(), Text: "Bring the masks.", Edited: true}
|
||||
Keys: "可选文字支持选择复制;插槽控件按 Tab 访问。", Code: `prev := messaging.ChatEntry{ID: "1", Author: "Uika", Time: time.Now(), Text: "Rehearsal at nine."}
|
||||
m := messaging.ChatEntry{ID: "2", Author: "Uika", Time: time.Now(), Text: "Bring the masks.", Edited: true}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ChatMessage(c, prev, mujica.ChatMessageOptions{})
|
||||
mujica.ChatMessage(c, m, mujica.ChatMessageOptions{
|
||||
Continued: mujica.ChatContinues(prev, m),
|
||||
Reactions: func() { mujica.Tag(c, "👍 2", mujica.TagOptions{}) },
|
||||
core.Use(c, core.Settings{})
|
||||
messaging.ChatMessage(c, prev, messaging.ChatMessageOptions{})
|
||||
messaging.ChatMessage(c, m, messaging.ChatMessageOptions{
|
||||
Continued: messaging.ChatContinues(prev, m),
|
||||
Reactions: func() { display.Tag(c, "👍 2", display.TagOptions{}) },
|
||||
})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "403", Name: "UnreadDivider", ZhName: "未读分隔线", Category: messagingCategory, Demo: unreadDividerDemo,
|
||||
Keys: "展示组件保持自然焦点序列。", Code: `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.UnreadDivider(c, 3)
|
||||
core.Use(c, core.Settings{})
|
||||
messaging.UnreadDivider(c, 3)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "404", Name: "ReadReceipt", ZhName: "已读回执", Category: messagingCategory, Demo: readReceiptDemo,
|
||||
Keys: "展示组件保持自然焦点序列。", Code: `readers := []mujica.AvatarItem{{Name: "Uika"}, {Name: "Mutsumi"}}
|
||||
Keys: "展示组件保持自然焦点序列。", Code: `readers := []display.AvatarItem{{Name: "Uika"}, {Name: "Mutsumi"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ReadReceipt(c, mujica.ReceiptRead, mujica.ReadReceiptOptions{Readers: readers})
|
||||
core.Use(c, core.Settings{})
|
||||
messaging.ReadReceipt(c, messaging.ReceiptRead, messaging.ReadReceiptOptions{Readers: readers})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "405", Name: "MemberList", ZhName: "成员列表", Category: messagingCategory, Demo: memberListDemo,
|
||||
Keys: "Tab 聚焦列表;上下方向键选择;Home / End 首尾;Enter 或双击打开成员。", Code: `var s mujica.MemberListState
|
||||
members := []mujica.Member{
|
||||
{ID: "1", Name: "Sakiko", Presence: mujica.PresenceOnline, Role: "Leader"},
|
||||
{ID: "2", Name: "Umiri", Presence: mujica.PresenceOffline},
|
||||
Keys: "Tab 聚焦列表;上下方向键选择;Home / End 首尾;Enter 或双击打开成员。", Code: `var s messaging.MemberListState
|
||||
members := []messaging.Member{
|
||||
{ID: "1", Name: "Sakiko", Presence: messaging.PresenceOnline, Role: "Leader"},
|
||||
{ID: "2", Name: "Umiri", Presence: messaging.PresenceOffline},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if r := mujica.MemberList(c, &s, members, mujica.MemberListOptions{}); r.Opened != "" {
|
||||
core.Use(c, core.Settings{})
|
||||
if r := messaging.MemberList(c, &s, members, messaging.MemberListOptions{}); r.Opened != "" {
|
||||
_ = r.Opened // show that member's profile
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "406", Name: "UserProfileCard", ZhName: "用户资料卡", Category: messagingCategory, Demo: userProfileCardDemo,
|
||||
Keys: "Tab 访问操作插槽;Enter / Space 激活按钮。", Code: `profile := mujica.UserProfile{Name: "Sakiko Togawa", Presence: mujica.PresenceBusy, Role: "Keyboard", Status: "In rehearsal"}
|
||||
Keys: "Tab 访问操作插槽;Enter / Space 激活按钮。", Code: `profile := messaging.UserProfile{Name: "Sakiko Togawa", Presence: messaging.PresenceBusy, Role: "Keyboard", Status: "In rehearsal"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.UserProfileCard(c, profile, mujica.UserProfileCardOptions{Actions: func() {
|
||||
mujica.Button(c, "Message", mujica.ButtonOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
messaging.UserProfileCard(c, profile, messaging.UserProfileCardOptions{Actions: func() {
|
||||
input.Button(c, "Message", input.ButtonOptions{})
|
||||
}})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "407", Name: "OnlineStatus", ZhName: "在线状态", Category: messagingCategory, Demo: onlineStatusDemo,
|
||||
Keys: "展示组件保持自然焦点序列。", Code: `view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.OnlineStatus(c, mujica.PresenceAway, mujica.OnlineStatusOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
messaging.OnlineStatus(c, messaging.PresenceAway, messaging.OnlineStatusOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "408", Name: "StatusSetter", ZhName: "状态设置", Category: messagingCategory, Demo: statusSetterDemo,
|
||||
Keys: "Tab 访问表情、文字、期限和清除按钮;选择器用方向键移动、Enter 确认、Escape 关闭。", Code: `status := mujica.CustomStatus{Emoji: "🎻", Text: "Rehearsing"}
|
||||
Keys: "Tab 访问表情、文字、期限和清除按钮;选择器用方向键移动、Enter 确认、Escape 关闭。", Code: `status := messaging.CustomStatus{Emoji: "🎻", Text: "Rehearsing"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.StatusSetter(c, &status, mujica.StatusSetterOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if messaging.StatusSetter(c, &status, messaging.StatusSetterOptions{}).Changed() {
|
||||
_ = status // publish it; clear it once status.Until passes
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "409", Name: "CallControls", ZhName: "通话控制", Category: messagingCategory, Demo: callControlsDemo,
|
||||
Keys: "Tab 进入工具栏;左右方向键移动;Enter / Space 激活。Cmd/Ctrl+D 静音,Cmd/Ctrl+E 摄像头,Cmd/Ctrl+Shift+S 共享,Cmd/Ctrl+Shift+H 挂断。", Code: `st := mujica.CallState{}
|
||||
Keys: "Tab 进入工具栏;左右方向键移动;Enter / Space 激活。Cmd/Ctrl+D 静音,Cmd/Ctrl+E 摄像头,Cmd/Ctrl+Shift+S 共享,Cmd/Ctrl+Shift+H 挂断。", Code: `st := messaging.CallState{}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.CallControls(c, st, mujica.CallControlsOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
messaging.CallControls(c, st, messaging.CallControlsOptions{
|
||||
OnMute: func(on bool) { st.Muted = on },
|
||||
OnCamera: func(off bool) { st.CameraOff = off },
|
||||
OnShare: func(on bool) { st.Sharing = on },
|
||||
@@ -91,20 +91,20 @@ view := func(c *ui.Context) {
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "410", Name: "VideoCallGrid", ZhName: "视频通话宫格", Category: messagingCategory, Demo: videoCallGridDemo,
|
||||
Keys: "Tab 进入宫格;方向键移动参与者焦点并读取状态。", Code: `people := []mujica.CallParticipant{
|
||||
Keys: "Tab 进入宫格;方向键移动参与者焦点并读取状态。", Code: `people := []messaging.CallParticipant{
|
||||
{ID: "1", Name: "Sakiko", Speaking: true}, // Frame: the latest decoded frame
|
||||
{ID: "2", Name: "Uika", Muted: true},
|
||||
}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.VideoCallGrid(c, people, mujica.VideoCallGridOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
messaging.VideoCallGrid(c, people, messaging.VideoCallGridOptions{})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "411", Name: "IncomingCallDialog", ZhName: "来电对话框", Category: messagingCategory, Demo: incomingCallDialogDemo,
|
||||
Keys: "默认焦点为拒绝;Tab 循环访问操作;Enter / Space 激活;Escape 拒绝;关闭后恢复焦点。", Code: `ringing := true
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.IncomingCallDialog(c, &ringing, mujica.IncomingCall{Caller: "Sakiko", Video: true}, mujica.IncomingCallOptions{
|
||||
core.Use(c, core.Settings{})
|
||||
messaging.IncomingCallDialog(c, &ringing, messaging.IncomingCall{Caller: "Sakiko", Video: true}, messaging.IncomingCallOptions{
|
||||
OnAccept: func(video bool) {}, // join, with the camera on when video
|
||||
OnDecline: func() {},
|
||||
})
|
||||
@@ -114,17 +114,17 @@ ui.Render(view, 400, 300, 1)`},
|
||||
Keys: "Tab 访问搜索、肤色、分类、表情;方向键在组内移动;Enter / Space 选择。", Code: `var recent []string
|
||||
tone := 0
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if r := mujica.EmojiPicker(c, mujica.EmojiPickerOptions{Recent: &recent, Tone: &tone}); r.Picked != "" {
|
||||
core.Use(c, core.Settings{})
|
||||
if r := messaging.EmojiPicker(c, messaging.EmojiPickerOptions{Recent: &recent, Tone: &tone}); r.Picked != "" {
|
||||
_ = r.Picked // insert it
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "413", Name: "PinnedMessages", ZhName: "置顶消息", Category: messagingCategory, Demo: pinnedMessagesDemo,
|
||||
Keys: "Tab 访问跳转与取消置顶;方向键移动;Enter / Space 激活。", Code: `pins := []mujica.ChatEntry{{ID: "a", Author: "Sakiko", Text: "Rehearsal moves to nine."}}
|
||||
Keys: "Tab 访问跳转与取消置顶;方向键移动;Enter / Space 激活。", Code: `pins := []messaging.ChatEntry{{ID: "a", Author: "Sakiko", Text: "Rehearsal moves to nine."}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.PinnedMessages(c, pins, mujica.PinnedMessagesOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
r := messaging.PinnedMessages(c, pins, messaging.PinnedMessagesOptions{})
|
||||
if r.Unpinned != "" {
|
||||
pins = pins[:0] // unpin it
|
||||
}
|
||||
@@ -132,67 +132,67 @@ view := func(c *ui.Context) {
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "414", Name: "ThreadPanel", ZhName: "话题侧栏", Category: messagingCategory, Demo: threadPanelDemo,
|
||||
Keys: "Tab 访问关闭、回复和发送;Enter 发送回复;Escape 关闭面板。", Code: `open := true
|
||||
root := mujica.ChatEntry{ID: "r", Author: "Sakiko", Text: "Set list for Friday?"}
|
||||
var replies []mujica.ChatEntry
|
||||
root := messaging.ChatEntry{ID: "r", Author: "Sakiko", Text: "Set list for Friday?"}
|
||||
var replies []messaging.ChatEntry
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if r := mujica.ThreadPanel(c, &open, root, replies, mujica.ThreadPanelOptions{}); r.Sent != "" {
|
||||
replies = append(replies, mujica.ChatEntry{ID: r.Sent, Author: "Me", Text: r.Sent, Own: true})
|
||||
core.Use(c, core.Settings{})
|
||||
if r := messaging.ThreadPanel(c, &open, root, replies, messaging.ThreadPanelOptions{}); r.Sent != "" {
|
||||
replies = append(replies, messaging.ChatEntry{ID: r.Sent, Author: "Me", Text: r.Sent, Own: true})
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "415", Name: "MailList", ZhName: "邮件列表", Category: messagingCategory, Demo: mailListDemo,
|
||||
Keys: "上下方向键、Home / End 移动选择;Enter 或双击打开;S 请求星标;Cmd/Ctrl+A 全选;Cmd/Ctrl 点击切换单项,Shift 点击选择范围。", Code: `var s mujica.ListState[string]
|
||||
mails := []mujica.MailItem{{ID: "1", From: "Sakiko", Subject: "Second act", Unread: true, Attachments: 1}}
|
||||
Keys: "上下方向键、Home / End 移动选择;Enter 或双击打开;S 请求星标;Cmd/Ctrl+A 全选;Cmd/Ctrl 点击切换单项,Shift 点击选择范围。", Code: `var s data.ListState[string]
|
||||
mails := []messaging.MailItem{{ID: "1", From: "Sakiko", Subject: "Second act", Unread: true, Attachments: 1}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if r := mujica.MailList(c, &s, mails, mujica.MailListOptions{}); r.Starred != "" {
|
||||
core.Use(c, core.Settings{})
|
||||
if r := messaging.MailList(c, &s, mails, messaging.MailListOptions{}); r.Starred != "" {
|
||||
mails[0].Starred = !mails[0].Starred
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "416", Name: "MailReader", ZhName: "邮件阅读器", Category: messagingCategory, Demo: mailReaderDemo,
|
||||
Keys: "Tab 访问链接、引用开关和附件;Enter / Space 激活;正文支持选择复制。", Code: `m := mujica.Mail{From: "Sakiko", Subject: "Second act", Body: "The score is attached.\n> Can you send it?",
|
||||
Attachments: []mujica.MailAttachment{{Name: "score.pdf", Size: 2048}}}
|
||||
Keys: "Tab 访问链接、引用开关和附件;Enter / Space 激活;正文支持选择复制。", Code: `m := messaging.Mail{From: "Sakiko", Subject: "Second act", Body: "The score is attached.\n> Can you send it?",
|
||||
Attachments: []messaging.MailAttachment{{Name: "score.pdf", Size: 2048}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if r := mujica.MailReader(c, m); r.Link != "" {
|
||||
core.Use(c, core.Settings{})
|
||||
if r := messaging.MailReader(c, m); r.Link != "" {
|
||||
c.OpenURL(r.Link)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "417", Name: "MailComposer", ZhName: "邮件编辑器", Category: messagingCategory, Demo: mailComposerDemo,
|
||||
Keys: "Tab 访问字段与操作;Cmd/Ctrl+S 保存草稿;组件内 Cmd/Ctrl+Enter 发送;正文 Enter 换行。", Code: `draft := mujica.MailDraft{To: []string{"uika@example.com"}}
|
||||
Keys: "Tab 访问字段与操作;Cmd/Ctrl+S 保存草稿;组件内 Cmd/Ctrl+Enter 发送;正文 Enter 换行。", Code: `draft := messaging.MailDraft{To: []string{"uika@example.com"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.MailComposer(c, &draft, mujica.MailComposerOptions{
|
||||
OnSaveDraft: func(d mujica.MailDraft) {}, // store d
|
||||
core.Use(c, core.Settings{})
|
||||
r := messaging.MailComposer(c, &draft, messaging.MailComposerOptions{
|
||||
OnSaveDraft: func(d messaging.MailDraft) {}, // store d
|
||||
})
|
||||
if r.Sent {
|
||||
draft = mujica.MailDraft{}
|
||||
draft = messaging.MailDraft{}
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "418", Name: "RecipientInput", ZhName: "收件人输入", Category: messagingCategory, Demo: recipientInputDemo,
|
||||
Keys: "Enter 或分隔符提交标签;空输入 Backspace 删除末项;上下方向键移动联系人建议,Enter 选择,Escape 收起建议。", Code: `var to []string
|
||||
contacts := []mujica.Contact{{Name: "Uika Misumi", Email: "uika@example.com"}}
|
||||
contacts := []messaging.Contact{{Name: "Uika Misumi", Email: "uika@example.com"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.RecipientInput(c, &to, mujica.RecipientInputOptions{Contacts: contacts, Label: "To"})
|
||||
core.Use(c, core.Settings{})
|
||||
messaging.RecipientInput(c, &to, messaging.RecipientInputOptions{Contacts: contacts, Label: "To"})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "419", Name: "QuotedText", ZhName: "引用文字", Category: messagingCategory, Demo: quotedTextDemo,
|
||||
Keys: "Tab 聚焦开关;Enter / Space 展开或折叠;展开内容支持选择复制。", Code: `open := false
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.QuotedText(c, "> Can you send the score?", &open)
|
||||
core.Use(c, core.Settings{})
|
||||
messaging.QuotedText(c, "> Can you send the score?", &open)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`},
|
||||
{ID: "420", Name: "SnoozePicker", ZhName: "稍后提醒", Category: messagingCategory, Demo: snoozePickerDemo,
|
||||
Keys: "Tab 进入;上下方向键选择快捷项;Enter / Space 激活;自定义时间沿用 DateTimePicker 的输入、确认和取消操作。", Code: `var until time.Time
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.SnoozePicker(c, &until, mujica.SnoozePickerOptions{}).Changed() {
|
||||
core.Use(c, core.Settings{})
|
||||
if messaging.SnoozePicker(c, &until, messaging.SnoozePickerOptions{}).Changed() {
|
||||
_ = until // hide the mail until then
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,22 +4,24 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/messaging"
|
||||
)
|
||||
|
||||
func callControlsDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
call mujica.CallState
|
||||
call messaging.CallState
|
||||
disabled bool
|
||||
ended bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "409", func() state { return state{} })
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.Button(c, "Reconnect", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Reconnect", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.ended = false
|
||||
log("call joined")
|
||||
}
|
||||
mujica.CallControls(c, s.call, mujica.CallControlsOptions{
|
||||
messaging.CallControls(c, s.call, messaging.CallControlsOptions{
|
||||
Disabled: s.disabled || s.ended,
|
||||
OnMute: func(on bool) { s.call.Muted = on; log(fmt.Sprintf("mute %v", on)) },
|
||||
OnCamera: func(off bool) { s.call.CameraOff = off; log(fmt.Sprintf("camera off %v", off)) },
|
||||
@@ -39,10 +41,10 @@ func videoCallGridDemo(c *ui.Context, log func(string)) {
|
||||
mediaCheck(c, log, &s.muted, "Muted")
|
||||
mediaCheck(c, log, &s.cameraOff, "Camera off")
|
||||
mediaCheck(c, log, &s.frame, "Caller-provided frame")
|
||||
if mujica.NumberInput(c, &s.columns, mujica.NumberInputOptions{Min: 0, Max: 3, Step: 1, Label: "Columns (0 = auto)"}).Changed() {
|
||||
if input.NumberInput(c, &s.columns, input.NumberInputOptions{Min: 0, Max: 3, Step: 1, Label: "Columns (0 = auto)"}).Changed() {
|
||||
log(fmt.Sprintf("columns %.0f", s.columns))
|
||||
}
|
||||
people := []mujica.CallParticipant{
|
||||
people := []messaging.CallParticipant{
|
||||
{ID: "1", Name: "Sakiko", Speaking: s.speaking, Muted: s.muted, CameraOff: s.cameraOff},
|
||||
{ID: "2", Name: "Uika", Muted: true}, {ID: "3", Name: "Mutsumi", CameraOff: true}, {ID: "4", Name: "Umiri"},
|
||||
}
|
||||
@@ -50,7 +52,7 @@ func videoCallGridDemo(c *ui.Context, log func(string)) {
|
||||
people[0].Frame = mediaBitmaps[0]
|
||||
people[3].Frame = mediaBitmaps[1]
|
||||
}
|
||||
mujica.VideoCallGrid(c, people, mujica.VideoCallGridOptions{Columns: int(s.columns)})
|
||||
messaging.VideoCallGrid(c, people, messaging.VideoCallGridOptions{Columns: int(s.columns)})
|
||||
}
|
||||
|
||||
func incomingCallDialogDemo(c *ui.Context, log func(string)) {
|
||||
@@ -62,11 +64,11 @@ func incomingCallDialogDemo(c *ui.Context, log func(string)) {
|
||||
mediaCheck(c, log, &s.video, "Video call")
|
||||
mediaCheck(c, log, &s.focusAccept, "Focus Accept")
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.Button(c, "Incoming call", mujica.ButtonOptions{Disabled: s.disabled}).Clicked() {
|
||||
if input.Button(c, "Incoming call", input.ButtonOptions{Disabled: s.disabled}).Clicked() {
|
||||
s.open = true
|
||||
log("incoming call")
|
||||
}
|
||||
mujica.IncomingCallDialog(c, &s.open, mujica.IncomingCall{Caller: "Sakiko Togawa", Video: s.video}, mujica.IncomingCallOptions{
|
||||
messaging.IncomingCallDialog(c, &s.open, messaging.IncomingCall{Caller: "Sakiko Togawa", Video: s.video}, messaging.IncomingCallOptions{
|
||||
Disabled: s.disabled, FocusAccept: s.focusAccept,
|
||||
OnAccept: func(video bool) { s.result = fmt.Sprintf("Accepted, video: %v", video); log(s.result) },
|
||||
OnDecline: func() { s.result = "Declined"; log(s.result) },
|
||||
|
||||
@@ -6,7 +6,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chat"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/display"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/messaging"
|
||||
)
|
||||
|
||||
func chatMessageDemo(c *ui.Context, log func(string)) {
|
||||
@@ -21,54 +25,54 @@ func chatMessageDemo(c *ui.Context, log func(string)) {
|
||||
mediaCheck(c, log, &s.reply, "Reply slot")
|
||||
mediaCheck(c, log, &s.reactions, "Reaction slot")
|
||||
at := time.Date(2026, 10, 5, 21, 7, 0, 0, time.Local)
|
||||
mujica.ChatMessage(c, mujica.ChatEntry{Author: "Uika", Time: at, Text: "Rehearsal at nine.", Own: s.own}, mujica.ChatMessageOptions{})
|
||||
opts := mujica.ChatMessageOptions{Continued: s.continued}
|
||||
messaging.ChatMessage(c, messaging.ChatEntry{Author: "Uika", Time: at, Text: "Rehearsal at nine.", Own: s.own}, messaging.ChatMessageOptions{})
|
||||
opts := messaging.ChatMessageOptions{Continued: s.continued}
|
||||
if s.reply {
|
||||
opts.Reply = func() {
|
||||
if mujica.QuoteReply(c, "Sakiko", "When do we start?").Clicked() {
|
||||
if chat.QuoteReply(c, "Sakiko", "When do we start?").Clicked() {
|
||||
log("jump to quoted message")
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.reactions {
|
||||
opts.Reactions = func() {
|
||||
if mujica.Toggle(c, &s.reacted, "Like", mujica.ToggleOptions{}).Changed() {
|
||||
if input.Toggle(c, &s.reacted, "Like", input.ToggleOptions{}).Changed() {
|
||||
log(fmt.Sprintf("reaction %v", s.reacted))
|
||||
}
|
||||
}
|
||||
}
|
||||
mujica.ChatMessage(c, mujica.ChatEntry{Author: "Uika", Time: at.Add(time.Minute), Text: "Bring the masks.", Own: s.own, Edited: s.edited}, opts)
|
||||
messaging.ChatMessage(c, messaging.ChatEntry{Author: "Uika", Time: at.Add(time.Minute), Text: "Bring the masks.", Own: s.own, Edited: s.edited}, opts)
|
||||
}
|
||||
|
||||
func unreadDividerDemo(c *ui.Context, log func(string)) {
|
||||
count := ui.Local(c.Root(), "403", func() float64 { return 3 })
|
||||
if mujica.NumberInput(c, count, mujica.NumberInputOptions{Min: 0, Max: 999, Step: 1, Label: "Unread count"}).Changed() {
|
||||
if input.NumberInput(c, count, input.NumberInputOptions{Min: 0, Max: 999, Step: 1, Label: "Unread count"}).Changed() {
|
||||
log(fmt.Sprintf("unread %.0f", *count))
|
||||
}
|
||||
mujica.UnreadDivider(c, int(*count))
|
||||
inputSection(c, "Zero count", func() { mujica.UnreadDivider(c, 0) })
|
||||
messaging.UnreadDivider(c, int(*count))
|
||||
inputSection(c, "Zero count", func() { messaging.UnreadDivider(c, 0) })
|
||||
}
|
||||
|
||||
func readReceiptDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
receipt mujica.ReceiptState
|
||||
receipt messaging.ReceiptState
|
||||
readers bool
|
||||
max float64
|
||||
}
|
||||
s := ui.Local(c.Root(), "404", func() state { return state{receipt: mujica.ReceiptRead, readers: true, max: 3} })
|
||||
items := []mujica.SelectOption[mujica.ReceiptState]{{Value: mujica.ReceiptSent, Label: "Sent"}, {Value: mujica.ReceiptDelivered, Label: "Delivered"}, {Value: mujica.ReceiptRead, Label: "Read"}}
|
||||
if mujica.Select(c, &s.receipt, items, mujica.SelectOptions{Label: "Receipt"}).Changed() {
|
||||
s := ui.Local(c.Root(), "404", func() state { return state{receipt: messaging.ReceiptRead, readers: true, max: 3} })
|
||||
items := []input.SelectOption[messaging.ReceiptState]{{Value: messaging.ReceiptSent, Label: "Sent"}, {Value: messaging.ReceiptDelivered, Label: "Delivered"}, {Value: messaging.ReceiptRead, Label: "Read"}}
|
||||
if input.Select(c, &s.receipt, items, input.SelectOptions{Label: "Receipt"}).Changed() {
|
||||
log(fmt.Sprintf("receipt %d", s.receipt))
|
||||
}
|
||||
mediaCheck(c, log, &s.readers, "Reader avatars")
|
||||
if mujica.NumberInput(c, &s.max, mujica.NumberInputOptions{Min: 1, Max: 4, Step: 1, Label: "Max avatars"}).Changed() {
|
||||
if input.NumberInput(c, &s.max, input.NumberInputOptions{Min: 1, Max: 4, Step: 1, Label: "Max avatars"}).Changed() {
|
||||
log(fmt.Sprintf("max %.0f", s.max))
|
||||
}
|
||||
opts := mujica.ReadReceiptOptions{Max: int(s.max)}
|
||||
opts := messaging.ReadReceiptOptions{Max: int(s.max)}
|
||||
if s.readers {
|
||||
opts.Readers = []mujica.AvatarItem{{Name: "Uika"}, {Name: "Mutsumi"}, {Name: "Umiri"}, {Name: "Nyamu"}}
|
||||
opts.Readers = []display.AvatarItem{{Name: "Uika"}, {Name: "Mutsumi"}, {Name: "Umiri"}, {Name: "Nyamu"}}
|
||||
}
|
||||
mujica.ReadReceipt(c, s.receipt, opts)
|
||||
messaging.ReadReceipt(c, s.receipt, opts)
|
||||
}
|
||||
|
||||
func emojiPickerDemo(c *ui.Context, log func(string)) {
|
||||
@@ -80,7 +84,7 @@ func emojiPickerDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
s := ui.Local(c.Root(), "412", func() state { return state{} })
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
if r := mujica.EmojiPicker(c, mujica.EmojiPickerOptions{Recent: &s.recent, Tone: &s.tone, Disabled: s.disabled}); r.Picked != "" {
|
||||
if r := messaging.EmojiPicker(c, messaging.EmojiPickerOptions{Recent: &s.recent, Tone: &s.tone, Disabled: s.disabled}); r.Picked != "" {
|
||||
s.picked = r.Picked
|
||||
log("emoji " + r.Picked)
|
||||
}
|
||||
@@ -89,26 +93,26 @@ func emojiPickerDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func pinnedMessagesDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
pins []mujica.ChatEntry
|
||||
pins []messaging.ChatEntry
|
||||
disabled bool
|
||||
jump string
|
||||
}
|
||||
newPins := func() []mujica.ChatEntry {
|
||||
return []mujica.ChatEntry{{ID: "a", Author: "Sakiko", Text: "Rehearsal moves to nine."}, {ID: "b", Author: "Uika", Text: "Masks are in the hall."}}
|
||||
newPins := func() []messaging.ChatEntry {
|
||||
return []messaging.ChatEntry{{ID: "a", Author: "Sakiko", Text: "Rehearsal moves to nine."}, {ID: "b", Author: "Uika", Text: "Masks are in the hall."}}
|
||||
}
|
||||
s := ui.Local(c.Root(), "413", func() state { return state{pins: newPins()} })
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.Button(c, "Restore pins", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Restore pins", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.pins = newPins()
|
||||
log("pins restored")
|
||||
}
|
||||
r := mujica.PinnedMessages(c, s.pins, mujica.PinnedMessagesOptions{Disabled: s.disabled, Height: 180})
|
||||
r := messaging.PinnedMessages(c, s.pins, messaging.PinnedMessagesOptions{Disabled: s.disabled, Height: 180})
|
||||
if r.Jump != "" {
|
||||
s.jump = r.Jump
|
||||
log("jump " + r.Jump)
|
||||
}
|
||||
if r.Unpinned != "" {
|
||||
s.pins = slices.DeleteFunc(slices.Clone(s.pins), func(m mujica.ChatEntry) bool { return m.ID == r.Unpinned })
|
||||
s.pins = slices.DeleteFunc(slices.Clone(s.pins), func(m messaging.ChatEntry) bool { return m.ID == r.Unpinned })
|
||||
log("unpin " + r.Unpinned)
|
||||
}
|
||||
ui.Text(c, "Jump target: "+s.jump)
|
||||
@@ -117,21 +121,21 @@ func pinnedMessagesDemo(c *ui.Context, log func(string)) {
|
||||
func threadPanelDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
open, disabled bool
|
||||
replies []mujica.ChatEntry
|
||||
replies []messaging.ChatEntry
|
||||
}
|
||||
s := ui.Local(c.Root(), "414", func() state {
|
||||
return state{open: true, replies: []mujica.ChatEntry{{ID: "1", Author: "Uika", Text: "Overture first."}}}
|
||||
return state{open: true, replies: []messaging.ChatEntry{{ID: "1", Author: "Uika", Text: "Overture first."}}}
|
||||
})
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.Button(c, "Open thread", mujica.ButtonOptions{Disabled: s.disabled}).Clicked() {
|
||||
if input.Button(c, "Open thread", input.ButtonOptions{Disabled: s.disabled}).Clicked() {
|
||||
s.open = true
|
||||
log("thread opened")
|
||||
}
|
||||
before := s.open
|
||||
ui.Row(c).Height(420).Children(func() {
|
||||
r := mujica.ThreadPanel(c, &s.open, mujica.ChatEntry{ID: "root", Author: "Sakiko", Text: "Set list for Friday?"}, s.replies, mujica.ThreadPanelOptions{Disabled: s.disabled})
|
||||
r := messaging.ThreadPanel(c, &s.open, messaging.ChatEntry{ID: "root", Author: "Sakiko", Text: "Set list for Friday?"}, s.replies, messaging.ThreadPanelOptions{Disabled: s.disabled})
|
||||
if r.Sent != "" {
|
||||
s.replies = append(s.replies, mujica.ChatEntry{ID: fmt.Sprint(len(s.replies) + 1), Author: "Me", Text: r.Sent, Own: true})
|
||||
s.replies = append(s.replies, messaging.ChatEntry{ID: fmt.Sprint(len(s.replies) + 1), Author: "Me", Text: r.Sent, Own: true})
|
||||
log("reply sent")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -5,18 +5,21 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/messaging"
|
||||
)
|
||||
|
||||
func mailListDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
list mujica.ListState[string]
|
||||
mails []mujica.MailItem
|
||||
list data.ListState[string]
|
||||
mails []messaging.MailItem
|
||||
disabled, empty bool
|
||||
opened string
|
||||
}
|
||||
s := ui.Local(c.Root(), "415", func() state {
|
||||
return state{mails: []mujica.MailItem{
|
||||
return state{mails: []messaging.MailItem{
|
||||
{ID: "1", From: "Sakiko", Subject: "Second act", Snippet: "The score is attached.", Unread: true, Attachments: 2},
|
||||
{ID: "2", From: "Uika", Subject: "Costumes", Snippet: "Fittings on Thursday.", Starred: true},
|
||||
{ID: "3", From: "Umiri", Subject: "Venue", Snippet: "The hall is booked."},
|
||||
@@ -24,10 +27,10 @@ func mailListDemo(c *ui.Context, log func(string)) {
|
||||
})
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
mediaCheck(c, log, &s.empty, "Empty")
|
||||
if mujica.Button(c, "Load 100,000 mails", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
s.mails = make([]mujica.MailItem, 100000)
|
||||
if input.Button(c, "Load 100,000 mails", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.mails = make([]messaging.MailItem, 100000)
|
||||
for i := range s.mails {
|
||||
s.mails[i] = mujica.MailItem{ID: fmt.Sprint(i), From: "Sender", Subject: fmt.Sprintf("Mail %d", i), Unread: i%2 == 0}
|
||||
s.mails[i] = messaging.MailItem{ID: fmt.Sprint(i), From: "Sender", Subject: fmt.Sprintf("Mail %d", i), Unread: i%2 == 0}
|
||||
}
|
||||
log("loaded 100,000 mails")
|
||||
}
|
||||
@@ -35,7 +38,7 @@ func mailListDemo(c *ui.Context, log func(string)) {
|
||||
if s.empty {
|
||||
mails = nil
|
||||
}
|
||||
r := mujica.MailList(c, &s.list, mails, mujica.MailListOptions{Disabled: s.disabled, Height: 240})
|
||||
r := messaging.MailList(c, &s.list, mails, messaging.MailListOptions{Disabled: s.disabled, Height: 240})
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("selected %d mails", s.list.Selection.Len()))
|
||||
}
|
||||
@@ -67,16 +70,16 @@ func mailReaderDemo(c *ui.Context, log func(string)) {
|
||||
mediaCheck(c, log, &s.markdown, "Safe Markdown")
|
||||
mediaCheck(c, log, &s.attachments, "Attachments")
|
||||
mediaCheck(c, log, &s.disabled, "Disabled actions")
|
||||
m := mujica.Mail{From: "Sakiko Togawa", To: []string{"Uika Misumi"}, Cc: []string{"Umiri Yahata"}, Subject: "Second act", Date: time.Date(2026, 10, 5, 21, 7, 0, 0, time.Local), Body: "The score is attached.\n> Can you send it tonight?"}
|
||||
m := messaging.Mail{From: "Sakiko Togawa", To: []string{"Uika Misumi"}, Cc: []string{"Umiri Yahata"}, Subject: "Second act", Date: time.Date(2026, 10, 5, 21, 7, 0, 0, time.Local), Body: "The score is attached.\n> Can you send it tonight?"}
|
||||
if s.markdown {
|
||||
m.Format = mujica.MailMarkdown
|
||||
m.Format = messaging.MailMarkdown
|
||||
m.Body = "**Rehearsal** at nine. [Venue](https://example.com)\n\n<script>alert(1)</script>"
|
||||
}
|
||||
if s.attachments {
|
||||
m.Attachments = []mujica.MailAttachment{{Name: "score.pdf", Size: 2048}}
|
||||
m.Attachments = []messaging.MailAttachment{{Name: "score.pdf", Size: 2048}}
|
||||
}
|
||||
ui.Column(c).Disabled(s.disabled).Children(func() {
|
||||
r := mujica.MailReader(c, m)
|
||||
r := messaging.MailReader(c, m)
|
||||
if r.Link != "" {
|
||||
log("link " + r.Link)
|
||||
}
|
||||
@@ -91,32 +94,32 @@ func mailReaderDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func mailComposerDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
draft mujica.MailDraft
|
||||
draft messaging.MailDraft
|
||||
disabled bool
|
||||
saved string
|
||||
}
|
||||
s := ui.Local(c.Root(), "417", func() state { return state{draft: mujica.MailDraft{To: []string{"uika@example.com"}}} })
|
||||
s := ui.Local(c.Root(), "417", func() state { return state{draft: messaging.MailDraft{To: []string{"uika@example.com"}}} })
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
r := mujica.MailComposer(c, &s.draft, mujica.MailComposerOptions{
|
||||
r := messaging.MailComposer(c, &s.draft, messaging.MailComposerOptions{
|
||||
Disabled: s.disabled, Contacts: messagingContacts,
|
||||
OnSaveDraft: func(d mujica.MailDraft) { s.saved = d.Subject; log("draft saved") },
|
||||
OnSaveDraft: func(d messaging.MailDraft) { s.saved = d.Subject; log("draft saved") },
|
||||
OnAttach: func() {
|
||||
s.draft.Attachments = append(s.draft.Attachments, mujica.MailAttachment{Name: fmt.Sprintf("score-%d.pdf", len(s.draft.Attachments)+1), Size: 2048})
|
||||
s.draft.Attachments = append(s.draft.Attachments, messaging.MailAttachment{Name: fmt.Sprintf("score-%d.pdf", len(s.draft.Attachments)+1), Size: 2048})
|
||||
log("attachment added")
|
||||
},
|
||||
})
|
||||
if r.Sent {
|
||||
log("mail sent")
|
||||
s.draft = mujica.MailDraft{}
|
||||
s.draft = messaging.MailDraft{}
|
||||
}
|
||||
if r.Discarded {
|
||||
log("draft discarded")
|
||||
s.draft = mujica.MailDraft{}
|
||||
s.draft = messaging.MailDraft{}
|
||||
}
|
||||
ui.Text(c, "Saved subject: "+s.saved)
|
||||
}
|
||||
|
||||
var messagingContacts = []mujica.Contact{{Name: "Uika Misumi", Email: "uika@example.com"}, {Name: "Umiri Yahata", Email: "umiri@example.com"}, {Name: "Sakiko Togawa", Email: "sakiko@example.com"}}
|
||||
var messagingContacts = []messaging.Contact{{Name: "Uika Misumi", Email: "uika@example.com"}, {Name: "Umiri Yahata", Email: "umiri@example.com"}, {Name: "Sakiko Togawa", Email: "sakiko@example.com"}}
|
||||
|
||||
func recipientInputDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
@@ -130,10 +133,10 @@ func recipientInputDemo(c *ui.Context, log func(string)) {
|
||||
if !s.contacts {
|
||||
contacts = nil
|
||||
}
|
||||
if mujica.RecipientInput(c, &s.to, mujica.RecipientInputOptions{Contacts: contacts, Label: "Recipients", Disabled: s.disabled}).Changed() {
|
||||
if messaging.RecipientInput(c, &s.to, messaging.RecipientInputOptions{Contacts: contacts, Label: "Recipients", Disabled: s.disabled}).Changed() {
|
||||
log(fmt.Sprintf("recipients %d", len(s.to)))
|
||||
}
|
||||
if mujica.Button(c, "Clear recipients", mujica.ButtonOptions{Variant: mujica.Secondary, Disabled: s.disabled}).Clicked() {
|
||||
if input.Button(c, "Clear recipients", input.ButtonOptions{Variant: input.Secondary, Disabled: s.disabled}).Clicked() {
|
||||
s.to = nil
|
||||
log("recipients cleared")
|
||||
}
|
||||
@@ -147,11 +150,11 @@ func quotedTextDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
s := ui.Local(c.Root(), "419", func() state { return state{text: "> Can you send the score?\n> Thanks."} })
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.TextArea(c, &s.text, mujica.TextAreaOptions{Label: "Quoted source", MinHeight: 80}).Changed() {
|
||||
if input.TextArea(c, &s.text, input.TextAreaOptions{Label: "Quoted source", MinHeight: 80}).Changed() {
|
||||
log("quote source edited")
|
||||
}
|
||||
ui.Column(c).Disabled(s.disabled).Children(func() {
|
||||
if mujica.QuotedText(c, s.text, &s.open).Changed() {
|
||||
if messaging.QuotedText(c, s.text, &s.open).Changed() {
|
||||
log(fmt.Sprintf("quote expanded %v", s.open))
|
||||
}
|
||||
})
|
||||
@@ -165,13 +168,13 @@ func snoozePickerDemo(c *ui.Context, log func(string)) {
|
||||
}
|
||||
s := ui.Local(c.Root(), "420", func() state { return state{morning: 9} })
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.NumberInput(c, &s.morning, mujica.NumberInputOptions{Min: 1, Max: 23, Step: 1, Label: "Morning hour"}).Changed() {
|
||||
if input.NumberInput(c, &s.morning, input.NumberInputOptions{Min: 1, Max: 23, Step: 1, Label: "Morning hour"}).Changed() {
|
||||
log(fmt.Sprintf("morning %.0f", s.morning))
|
||||
}
|
||||
if mujica.SnoozePicker(c, &s.until, mujica.SnoozePickerOptions{Disabled: s.disabled, MorningHour: int(s.morning)}).Changed() {
|
||||
if messaging.SnoozePicker(c, &s.until, messaging.SnoozePickerOptions{Disabled: s.disabled, MorningHour: int(s.morning)}).Changed() {
|
||||
log("snooze " + s.until.Format(time.RFC3339))
|
||||
}
|
||||
if mujica.Button(c, "Clear snooze", mujica.ButtonOptions{Variant: mujica.Secondary, Disabled: s.disabled}).Clicked() {
|
||||
if input.Button(c, "Clear snooze", input.ButtonOptions{Variant: input.Secondary, Disabled: s.disabled}).Clicked() {
|
||||
s.until = time.Time{}
|
||||
log("snooze cleared")
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ import (
|
||||
"slices"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/messaging"
|
||||
)
|
||||
|
||||
func channelListDemo(c *ui.Context, log func(string)) {
|
||||
@@ -13,15 +15,15 @@ func channelListDemo(c *ui.Context, log func(string)) {
|
||||
selected string
|
||||
disabled bool
|
||||
revision int
|
||||
groups []mujica.ChannelGroup
|
||||
groups []messaging.ChannelGroup
|
||||
}
|
||||
s := ui.Local(c.Root(), "401", func() state {
|
||||
return state{groups: []mujica.ChannelGroup{
|
||||
{ID: "channels", Title: "Channels", Channels: []mujica.Channel{
|
||||
return state{groups: []messaging.ChannelGroup{
|
||||
{ID: "channels", Title: "Channels", Channels: []messaging.Channel{
|
||||
{ID: "general", Name: "general", Unread: 3}, {ID: "rehearsal", Name: "rehearsal", Pinned: true},
|
||||
{ID: "random", Name: "random", Unread: 120, Muted: true},
|
||||
}},
|
||||
{ID: "direct", Title: "Direct messages", Channels: []mujica.Channel{{ID: "uika", Name: "Uika", Direct: true, Presence: mujica.PresenceOnline}}},
|
||||
{ID: "direct", Title: "Direct messages", Channels: []messaging.Channel{{ID: "uika", Name: "Uika", Direct: true, Presence: messaging.PresenceOnline}}},
|
||||
}}
|
||||
})
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
@@ -31,7 +33,7 @@ func channelListDemo(c *ui.Context, log func(string)) {
|
||||
if before != s.groups[0].Channels[0] {
|
||||
s.revision++
|
||||
}
|
||||
if mujica.ChannelList(c, &s.selected, s.groups, mujica.ChannelListOptions{Disabled: s.disabled, Revision: s.revision}).Changed() {
|
||||
if messaging.ChannelList(c, &s.selected, s.groups, messaging.ChannelListOptions{Disabled: s.disabled, Revision: s.revision}).Changed() {
|
||||
log("channel " + s.selected)
|
||||
}
|
||||
ui.Text(c, "Selected: "+s.selected)
|
||||
@@ -39,25 +41,25 @@ func channelListDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func memberListDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
list mujica.MemberListState
|
||||
list messaging.MemberListState
|
||||
disabled, empty bool
|
||||
members []mujica.Member
|
||||
members []messaging.Member
|
||||
}
|
||||
s := ui.Local(c.Root(), "405", func() state {
|
||||
return state{members: []mujica.Member{
|
||||
{ID: "1", Name: "Sakiko", Presence: mujica.PresenceOnline, Role: "Leader"},
|
||||
{ID: "2", Name: "Uika", Presence: mujica.PresenceBusy, Role: "Vocal"},
|
||||
{ID: "3", Name: "Mutsumi", Presence: mujica.PresenceAway}, {ID: "4", Name: "Umiri", Presence: mujica.PresenceOffline},
|
||||
return state{members: []messaging.Member{
|
||||
{ID: "1", Name: "Sakiko", Presence: messaging.PresenceOnline, Role: "Leader"},
|
||||
{ID: "2", Name: "Uika", Presence: messaging.PresenceBusy, Role: "Vocal"},
|
||||
{ID: "3", Name: "Mutsumi", Presence: messaging.PresenceAway}, {ID: "4", Name: "Umiri", Presence: messaging.PresenceOffline},
|
||||
}}
|
||||
})
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
mediaCheck(c, log, &s.empty, "Empty")
|
||||
if mujica.Button(c, "Toggle Uika presence", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Toggle Uika presence", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.members = slices.Clone(s.members)
|
||||
if s.members[1].Presence == mujica.PresenceOffline {
|
||||
s.members[1].Presence = mujica.PresenceOnline
|
||||
if s.members[1].Presence == messaging.PresenceOffline {
|
||||
s.members[1].Presence = messaging.PresenceOnline
|
||||
} else {
|
||||
s.members[1].Presence = mujica.PresenceOffline
|
||||
s.members[1].Presence = messaging.PresenceOffline
|
||||
}
|
||||
log("Uika presence changed")
|
||||
}
|
||||
@@ -65,7 +67,7 @@ func memberListDemo(c *ui.Context, log func(string)) {
|
||||
if s.empty {
|
||||
members = nil
|
||||
}
|
||||
r := mujica.MemberList(c, &s.list, members, mujica.MemberListOptions{Disabled: s.disabled, Height: 240})
|
||||
r := messaging.MemberList(c, &s.list, members, messaging.MemberListOptions{Disabled: s.disabled, Height: 240})
|
||||
if r.Changed() {
|
||||
log("member " + s.list.Selected)
|
||||
}
|
||||
@@ -76,61 +78,61 @@ func memberListDemo(c *ui.Context, log func(string)) {
|
||||
|
||||
func userProfileCardDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
presence mujica.PresenceStatus
|
||||
presence messaging.PresenceStatus
|
||||
actions, disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "406", func() state { return state{actions: true, presence: mujica.PresenceOnline} })
|
||||
s := ui.Local(c.Root(), "406", func() state { return state{actions: true, presence: messaging.PresenceOnline} })
|
||||
mediaCheck(c, log, &s.actions, "Actions")
|
||||
mediaCheck(c, log, &s.disabled, "Disable actions")
|
||||
messagingPresence(c, log, &s.presence)
|
||||
opts := mujica.UserProfileCardOptions{}
|
||||
opts := messaging.UserProfileCardOptions{}
|
||||
if s.actions {
|
||||
opts.Actions = func() {
|
||||
for _, action := range []string{"Message", "Call"} {
|
||||
if mujica.Button(c, action, mujica.ButtonOptions{Disabled: s.disabled}).Clicked() {
|
||||
if input.Button(c, action, input.ButtonOptions{Disabled: s.disabled}).Clicked() {
|
||||
log(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mujica.UserProfileCard(c, mujica.UserProfile{Name: "Sakiko Togawa", Presence: s.presence, Role: "Keyboard", Status: "In rehearsal", About: "Founder of the masquerade."}, opts)
|
||||
messaging.UserProfileCard(c, messaging.UserProfile{Name: "Sakiko Togawa", Presence: s.presence, Role: "Keyboard", Status: "In rehearsal", About: "Founder of the masquerade."}, opts)
|
||||
}
|
||||
|
||||
func onlineStatusDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
presence mujica.PresenceStatus
|
||||
presence messaging.PresenceStatus
|
||||
hidden bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "407", func() state { return state{presence: mujica.PresenceOnline} })
|
||||
s := ui.Local(c.Root(), "407", func() state { return state{presence: messaging.PresenceOnline} })
|
||||
mediaCheck(c, log, &s.hidden, "Hide text")
|
||||
messagingPresence(c, log, &s.presence)
|
||||
mujica.OnlineStatus(c, s.presence, mujica.OnlineStatusOptions{HideText: s.hidden})
|
||||
messaging.OnlineStatus(c, s.presence, messaging.OnlineStatusOptions{HideText: s.hidden})
|
||||
inputSection(c, "States", func() {
|
||||
for p := mujica.PresenceOffline; p <= mujica.PresenceBusy; p++ {
|
||||
mujica.OnlineStatus(c, p, mujica.OnlineStatusOptions{})
|
||||
for p := messaging.PresenceOffline; p <= messaging.PresenceBusy; p++ {
|
||||
messaging.OnlineStatus(c, p, messaging.OnlineStatusOptions{})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func statusSetterDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
status mujica.CustomStatus
|
||||
status messaging.CustomStatus
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "408", func() state { return state{} })
|
||||
mediaCheck(c, log, &s.disabled, "Disabled")
|
||||
if mujica.StatusSetter(c, &s.status, mujica.StatusSetterOptions{Disabled: s.disabled}).Changed() {
|
||||
if messaging.StatusSetter(c, &s.status, messaging.StatusSetterOptions{Disabled: s.disabled}).Changed() {
|
||||
log("status changed")
|
||||
}
|
||||
ui.Text(c, fmt.Sprintf("Text: %s; expires: %v", s.status.Text, s.status.Until))
|
||||
}
|
||||
|
||||
func messagingPresence(c *ui.Context, log func(string), value *mujica.PresenceStatus) {
|
||||
items := []mujica.SelectOption[mujica.PresenceStatus]{
|
||||
{Value: mujica.PresenceOnline, Label: "Online"}, {Value: mujica.PresenceAway, Label: "Away"},
|
||||
{Value: mujica.PresenceBusy, Label: "Busy"}, {Value: mujica.PresenceOffline, Label: "Offline"},
|
||||
func messagingPresence(c *ui.Context, log func(string), value *messaging.PresenceStatus) {
|
||||
items := []input.SelectOption[messaging.PresenceStatus]{
|
||||
{Value: messaging.PresenceOnline, Label: "Online"}, {Value: messaging.PresenceAway, Label: "Away"},
|
||||
{Value: messaging.PresenceBusy, Label: "Busy"}, {Value: messaging.PresenceOffline, Label: "Offline"},
|
||||
}
|
||||
if mujica.Select(c, value, items, mujica.SelectOptions{Label: "Presence"}).Changed() {
|
||||
if input.Select(c, value, items, input.SelectOptions{Label: "Presence"}).Changed() {
|
||||
log(fmt.Sprintf("presence %d", *value))
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,11 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
)
|
||||
|
||||
func messagingDemoTester(t *testing.T, id string, mode mujica.Mode) (*ui.Tester, *[]string) {
|
||||
func messagingDemoTester(t *testing.T, id string, mode core.Mode) (*ui.Tester, *[]string) {
|
||||
t.Helper()
|
||||
i := slices.IndexFunc(messagingPages, func(p page) bool { return p.ID == id })
|
||||
if i < 0 {
|
||||
@@ -21,7 +22,7 @@ func messagingDemoTester(t *testing.T, id string, mode mujica.Mode) (*ui.Tester,
|
||||
}
|
||||
events := &[]string{}
|
||||
tt := ui.NewTester(func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{Mode: mode})
|
||||
core.Use(c, core.Settings{Mode: mode})
|
||||
ui.Scroll(c).Fill().Padding(24).Gap(12).Children(func() {
|
||||
messagingPages[i].Demo(c, func(s string) { *events = append(*events, s) })
|
||||
})
|
||||
@@ -58,7 +59,7 @@ func TestMessagingDemosInteractAndLog(t *testing.T) {
|
||||
t.Fatalf("pages=%d, cases=%d", len(messagingPages), len(cases))
|
||||
}
|
||||
for _, tc := range cases {
|
||||
for _, mode := range []mujica.Mode{mujica.Light, mujica.Dark} {
|
||||
for _, mode := range []core.Mode{core.Light, core.Dark} {
|
||||
t.Run(tc.id+mode.String(), func(t *testing.T) {
|
||||
tt, events := messagingDemoTester(t, tc.id, mode)
|
||||
if err := tt.Click(tc.click); err != nil {
|
||||
@@ -105,7 +106,7 @@ func TestMessagingDemoCaptures(t *testing.T) {
|
||||
if p.Category != "Messaging & Mail" || p.Code == "" || p.Keys == "" {
|
||||
t.Fatalf("incomplete page %s", p.ID)
|
||||
}
|
||||
for _, mode := range []mujica.Mode{mujica.Light, mujica.Dark} {
|
||||
for _, mode := range []core.Mode{core.Light, core.Dark} {
|
||||
tt, _ := messagingDemoTester(t, p.ID, mode)
|
||||
f, err := os.Create(filepath.Join(dir, fmt.Sprintf("%s-%s.png", p.ID, mode)))
|
||||
if err != nil {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package main
|
||||
|
||||
import "github.com/egoist/mygo/ui"
|
||||
import (
|
||||
"github.com/egoist/mygo/ui"
|
||||
)
|
||||
|
||||
var mktchartPages = []page{
|
||||
{ID: "141", Name: "CandlestickChart", ZhName: "K 线", Category: "Market Charts", Demo: func(c *ui.Context, log func(string)) { mktDemo(c, log, "141") }, Code: mktCode141, Keys: mktKeys},
|
||||
|
||||
@@ -1,211 +1,211 @@
|
||||
package main
|
||||
|
||||
const mktCode141 = ` at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
candles := []mujica.Candle{
|
||||
candles := []chart.Candle{
|
||||
{Time: at, Open: 100, High: 103, Low: 99, Close: 102, Volume: 1200},
|
||||
{Time: at.Add(time.Hour), Open: 102, High: 104, Low: 100, Close: 101, Volume: 1600},
|
||||
{Time: at.Add(2 * time.Hour), Open: 101, High: 105, Low: 100, Close: 104, Volume: 900},
|
||||
}
|
||||
var window mujica.MarketView
|
||||
var window chart.MarketView
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.CandlestickChart(c, candles, &window, mujica.CandlestickOptions{Frame: mujica.ChartFrame{Label: "BTC", Height: 200}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.CandlestickChart(c, candles, &window, chart.CandlestickOptions{Frame: chart.ChartFrame{Label: "BTC", Height: 200}})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode142 = ` at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
candles := []mujica.Candle{
|
||||
candles := []chart.Candle{
|
||||
{Time: at, Open: 100, High: 103, Low: 99, Close: 102, Volume: 1200},
|
||||
{Time: at.Add(time.Hour), Open: 102, High: 104, Low: 100, Close: 101, Volume: 1600},
|
||||
{Time: at.Add(2 * time.Hour), Open: 101, High: 105, Low: 100, Close: 104, Volume: 900},
|
||||
}
|
||||
var window mujica.MarketView
|
||||
var window chart.MarketView
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.OHLCChart(c, candles, &window, mujica.CandlestickOptions{Frame: mujica.ChartFrame{Label: "BTC", Height: 200}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.OHLCChart(c, candles, &window, chart.CandlestickOptions{Frame: chart.ChartFrame{Label: "BTC", Height: 200}})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode143 = ` at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
candles := []mujica.Candle{
|
||||
candles := []chart.Candle{
|
||||
{Time: at, Open: 100, High: 103, Low: 99, Close: 102, Volume: 1200},
|
||||
{Time: at.Add(time.Hour), Open: 102, High: 104, Low: 100, Close: 101, Volume: 1600},
|
||||
{Time: at.Add(2 * time.Hour), Open: 101, High: 105, Low: 100, Close: 104, Volume: 900},
|
||||
}
|
||||
var window mujica.MarketView
|
||||
var window chart.MarketView
|
||||
raw := false
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.HeikinAshiChart(c, candles, &window, &raw, mujica.CandlestickOptions{Frame: mujica.ChartFrame{Label: "BTC", Height: 200}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.HeikinAshiChart(c, candles, &window, &raw, chart.CandlestickOptions{Frame: chart.ChartFrame{Label: "BTC", Height: 200}})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode144 = ` at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
points := []mujica.QuotePoint{{Time: at, Price: 101, Volume: 100}, {Time: at.Add(time.Hour), Price: 103, Volume: 200}}
|
||||
points := []chart.QuotePoint{{Time: at, Price: 101, Volume: 100}, {Time: at.Add(time.Hour), Price: 103, Volume: 200}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.LineQuoteChart(c, points, mujica.LineQuoteChartOptions{Frame: mujica.ChartFrame{Label: "BTC", Height: 200}, PrevClose: 100, RegularOpen: at.Add(30 * time.Minute), RegularClose: at.Add(45 * time.Minute)})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.LineQuoteChart(c, points, chart.LineQuoteChartOptions{Frame: chart.ChartFrame{Label: "BTC", Height: 200}, PrevClose: 100, RegularOpen: at.Add(30 * time.Minute), RegularClose: at.Add(45 * time.Minute)})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode145 = ` at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
candles := []mujica.Candle{
|
||||
candles := []chart.Candle{
|
||||
{Time: at, Open: 100, High: 103, Low: 99, Close: 102, Volume: 1200},
|
||||
{Time: at.Add(time.Hour), Open: 102, High: 104, Low: 100, Close: 101, Volume: 1600},
|
||||
{Time: at.Add(2 * time.Hour), Open: 101, High: 105, Low: 100, Close: 104, Volume: 900},
|
||||
}
|
||||
var window mujica.MarketView
|
||||
var window chart.MarketView
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.VolumeChart(c, candles, &window, mujica.VolumeChartOptions{Frame: mujica.ChartFrame{Label: "Volume"}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.VolumeChart(c, candles, &window, chart.VolumeChartOptions{Frame: chart.ChartFrame{Label: "Volume"}})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode146 = ` bids := []mujica.DepthLevel{{Price: 99, Size: 10}, {Price: 98, Size: 20}}
|
||||
asks := []mujica.DepthLevel{{Price: 101, Size: 15}, {Price: 102, Size: 25}}
|
||||
const mktCode146 = ` bids := []chart.DepthLevel{{Price: 99, Size: 10}, {Price: 98, Size: 20}}
|
||||
asks := []chart.DepthLevel{{Price: 101, Size: 15}, {Price: 102, Size: 25}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.DepthChart(c, bids, asks, mujica.DepthChartOptions{Frame: mujica.ChartFrame{Label: "BTC", Height: 200}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.DepthChart(c, bids, asks, chart.DepthChartOptions{Frame: chart.ChartFrame{Label: "BTC", Height: 200}})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode147 = ` bins := []mujica.ProfileBin{{Low: 99, High: 100, Volume: 30}, {Low: 100, High: 101, Volume: 80}, {Low: 101, High: 102, Volume: 40}}
|
||||
const mktCode147 = ` bins := []chart.ProfileBin{{Low: 99, High: 100, Volume: 30}, {Low: 100, High: 101, Volume: 80}, {Low: 101, High: 102, Volume: 40}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.VolumeProfile(c, bins, mujica.VolumeProfileOptions{Frame: mujica.ChartFrame{Label: "BTC", Height: 200}})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.VolumeProfile(c, bins, chart.VolumeProfileOptions{Frame: chart.ChartFrame{Label: "BTC", Height: 200}})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode148 = ` state := mujica.MultiChartState{Count: 2}
|
||||
const mktCode148 = ` state := chart.MultiChartState{Count: 2}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.MultiChartLayout(c, &state, mujica.MultiChartLayoutOptions{Height: 200}, func(i int, cell *mujica.ChartCell) {
|
||||
mujica.TextInput(c, &cell.Symbol, mujica.TextInputOptions{Label: "Symbol"})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.MultiChartLayout(c, &state, chart.MultiChartLayoutOptions{Height: 200}, func(i int, cell *chart.ChartCell) {
|
||||
input.TextInput(c, &cell.Symbol, input.TextInputOptions{Label: "Symbol"})
|
||||
})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode149 = ` at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
candles := []mujica.Candle{
|
||||
candles := []chart.Candle{
|
||||
{Time: at, Open: 100, High: 103, Low: 99, Close: 102, Volume: 1200},
|
||||
{Time: at.Add(time.Hour), Open: 102, High: 104, Low: 100, Close: 101, Volume: 1600},
|
||||
{Time: at.Add(2 * time.Hour), Open: 101, High: 105, Low: 100, Close: 104, Volume: 900},
|
||||
}
|
||||
var window mujica.MarketView
|
||||
var sync mujica.ChartSync
|
||||
var window chart.MarketView
|
||||
var sync chart.ChartSync
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
core.Use(c, core.Settings{})
|
||||
sync.Link(c, "price")
|
||||
mujica.CandlestickChart(c, candles, &window, mujica.CandlestickOptions{Frame: mujica.ChartFrame{Label: "BTC", Height: 200}, Sync: &sync, SyncID: "price"})
|
||||
chart.CandlestickChart(c, candles, &window, chart.CandlestickOptions{Frame: chart.ChartFrame{Label: "BTC", Height: 200}, Sync: &sync, SyncID: "price"})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode150 = ` value := mujica.MarketRange{Preset: mujica.Range1M}
|
||||
const mktCode150 = ` value := chart.MarketRange{Preset: chart.Range1M}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.TimeRangeSelector(c, &value, mujica.TimeRangeSelectorOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.TimeRangeSelector(c, &value, chart.TimeRangeSelectorOptions{})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode151 = ` value := mujica.Interval("1h")
|
||||
favorites := []mujica.Interval{"1m", "1h", "1D"}
|
||||
const mktCode151 = ` value := chart.Interval("1h")
|
||||
favorites := []chart.Interval{"1m", "1h", "1D"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.IntervalSelector(c, &value, &favorites, mujica.IntervalSelectorOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.IntervalSelector(c, &value, &favorites, chart.IntervalSelectorOptions{})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode152 = ` value := mujica.ChartCandles
|
||||
const mktCode152 = ` value := chart.ChartCandles
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.ChartTypeSwitcher(c, &value, mujica.ChartTypeSwitcherOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.ChartTypeSwitcher(c, &value, chart.ChartTypeSwitcherOptions{})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode153 = ` at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
candles := []mujica.Candle{
|
||||
candles := []chart.Candle{
|
||||
{Time: at, Open: 100, High: 103, Low: 99, Close: 102, Volume: 1200},
|
||||
{Time: at.Add(time.Hour), Open: 102, High: 104, Low: 100, Close: 101, Volume: 1600},
|
||||
{Time: at.Add(2 * time.Hour), Open: 101, High: 105, Low: 100, Close: 104, Volume: 900},
|
||||
}
|
||||
var window mujica.MarketView
|
||||
overlays := []mujica.IndicatorOverlay{{Kind: mujica.IndicatorMA, Params: []float64{2}}}
|
||||
var window chart.MarketView
|
||||
overlays := []chart.IndicatorOverlay{{Kind: chart.IndicatorMA, Params: []float64{2}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.CandlestickChart(c, candles, &window, mujica.CandlestickOptions{Frame: mujica.ChartFrame{Label: "BTC", Height: 200}, Overlays: overlays})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.CandlestickChart(c, candles, &window, chart.CandlestickOptions{Frame: chart.ChartFrame{Label: "BTC", Height: 200}, Overlays: overlays})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode154 = ` at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
candles := []mujica.Candle{
|
||||
candles := []chart.Candle{
|
||||
{Time: at, Open: 100, High: 103, Low: 99, Close: 102, Volume: 1200},
|
||||
{Time: at.Add(time.Hour), Open: 102, High: 104, Low: 100, Close: 101, Volume: 1600},
|
||||
{Time: at.Add(2 * time.Hour), Open: 101, High: 105, Low: 100, Close: 104, Volume: 900},
|
||||
}
|
||||
var window mujica.MarketView
|
||||
panes := mujica.IndicatorPanes{Panes: []mujica.IndicatorPaneSpec{{Kind: mujica.IndicatorRSI, Params: []float64{2}}}}
|
||||
var window chart.MarketView
|
||||
panes := chart.IndicatorPanes{Panes: []chart.IndicatorPaneSpec{{Kind: chart.IndicatorRSI, Params: []float64{2}}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.IndicatorPane(c, candles, &window, &panes, mujica.IndicatorPaneOptions{Height: 200, Label: "BTC"})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.IndicatorPane(c, candles, &window, &panes, chart.IndicatorPaneOptions{Height: 200, Label: "BTC"})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode155 = ` var overlays []mujica.IndicatorOverlay
|
||||
var panes mujica.IndicatorPanes
|
||||
const mktCode155 = ` var overlays []chart.IndicatorOverlay
|
||||
var panes chart.IndicatorPanes
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.IndicatorSelector(c, &overlays, &panes, mujica.IndicatorSelectorOptions{})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.IndicatorSelector(c, &overlays, &panes, chart.IndicatorSelectorOptions{})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode156 = ` at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
candles := []mujica.Candle{
|
||||
candles := []chart.Candle{
|
||||
{Time: at, Open: 100, High: 103, Low: 99, Close: 102, Volume: 1200},
|
||||
{Time: at.Add(time.Hour), Open: 102, High: 104, Low: 100, Close: 101, Volume: 1600},
|
||||
{Time: at.Add(2 * time.Hour), Open: 101, High: 105, Low: 100, Close: 104, Volume: 900},
|
||||
}
|
||||
var window mujica.MarketView
|
||||
drawings := mujica.DrawingTools{Tool: mujica.ToolTrendLine}
|
||||
var window chart.MarketView
|
||||
drawings := chart.DrawingTools{Tool: chart.ToolTrendLine}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.CandlestickChart(c, candles, &window, mujica.CandlestickOptions{Frame: mujica.ChartFrame{Label: "BTC", Height: 200}, Drawings: &drawings})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.CandlestickChart(c, candles, &window, chart.CandlestickOptions{Frame: chart.ChartFrame{Label: "BTC", Height: 200}, Drawings: &drawings})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode157 = ` var drawings mujica.DrawingTools
|
||||
const mktCode157 = ` var drawings chart.DrawingTools
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.DrawingToolbar(c, &drawings, mujica.DrawingToolbarOptions{Horizontal: true})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.DrawingToolbar(c, &drawings, chart.DrawingToolbarOptions{Horizontal: true})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode158 = ` at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
candles := []mujica.Candle{
|
||||
candles := []chart.Candle{
|
||||
{Time: at, Open: 100, High: 103, Low: 99, Close: 102, Volume: 1200},
|
||||
{Time: at.Add(time.Hour), Open: 102, High: 104, Low: 100, Close: 101, Volume: 1600},
|
||||
{Time: at.Add(2 * time.Hour), Open: 101, High: 105, Low: 100, Close: 104, Volume: 900},
|
||||
}
|
||||
var window mujica.MarketView
|
||||
symbols := []mujica.CompareSymbol{{Name: "ETH", Closes: []float64{20, 21, 23}}}
|
||||
var window chart.MarketView
|
||||
symbols := []chart.CompareSymbol{{Name: "ETH", Closes: []float64{20, 21, 23}}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.CompareLegend(c, &symbols)
|
||||
mujica.CandlestickChart(c, candles, &window, mujica.CandlestickOptions{Frame: mujica.ChartFrame{Label: "BTC", Height: 200}, Compare: &symbols})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.CompareLegend(c, &symbols)
|
||||
chart.CandlestickChart(c, candles, &window, chart.CandlestickOptions{Frame: chart.ChartFrame{Label: "BTC", Height: 200}, Compare: &symbols})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode159 = ` at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
candles := []mujica.Candle{
|
||||
candles := []chart.Candle{
|
||||
{Time: at, Open: 100, High: 103, Low: 99, Close: 102, Volume: 1200},
|
||||
{Time: at.Add(time.Hour), Open: 102, High: 104, Low: 100, Close: 101, Volume: 1600},
|
||||
{Time: at.Add(2 * time.Hour), Open: 101, High: 105, Low: 100, Close: 104, Volume: 900},
|
||||
}
|
||||
var window mujica.MarketView
|
||||
alerts := []mujica.PriceAlertLine{{ID: "take-profit", Price: 105}}
|
||||
var window chart.MarketView
|
||||
alerts := []chart.PriceAlertLine{{ID: "take-profit", Price: 105}}
|
||||
triggered := ""
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.CandlestickChart(c, candles, &window, mujica.CandlestickOptions{Frame: mujica.ChartFrame{Label: "BTC", Height: 200}, Alerts: &alerts, OnAlert: func(alert mujica.PriceAlertLine, last mujica.Candle) {
|
||||
core.Use(c, core.Settings{})
|
||||
chart.CandlestickChart(c, candles, &window, chart.CandlestickOptions{Frame: chart.ChartFrame{Label: "BTC", Height: 200}, Alerts: &alerts, OnAlert: func(alert chart.PriceAlertLine, last chart.Candle) {
|
||||
triggered = alert.ID
|
||||
}})
|
||||
ui.Text(c, triggered)
|
||||
@@ -213,7 +213,7 @@ const mktCode159 = ` at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
|
||||
const mktCode160 = ` view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.CandleCountdown(c, "1h", mujica.CandleCountdownOptions{Location: time.UTC})
|
||||
core.Use(c, core.Settings{})
|
||||
chart.CandleCountdown(c, "1h", chart.CandleCountdownOptions{Location: time.UTC})
|
||||
}
|
||||
ui.Render(view, 640, 400, 1)`
|
||||
@@ -5,7 +5,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chart"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
func mktDemo(c *ui.Context, log func(string), id string) {
|
||||
@@ -21,65 +23,65 @@ func mktDemo(c *ui.Context, log func(string), id string) {
|
||||
bins = nil
|
||||
}
|
||||
ui.Column(c).Disabled(s.disabled).Gap(12).FillWidth().Children(func() {
|
||||
opts := mujica.CandlestickOptions{Frame: frame, Type: s.chartType}
|
||||
chart := func() { mktLog(log, id, mujica.CandlestickChart(c, candles, &s.window, opts)) }
|
||||
opts := chart.CandlestickOptions{Frame: frame, Type: s.chartType}
|
||||
draw := func() { mktLog(log, id, chart.CandlestickChart(c, candles, &s.window, opts)) }
|
||||
switch id {
|
||||
case "141":
|
||||
if mujica.ChartTypeSwitcher(c, &s.chartType, mujica.ChartTypeSwitcherOptions{}).Changed() {
|
||||
if chart.ChartTypeSwitcher(c, &s.chartType, chart.ChartTypeSwitcherOptions{}).Changed() {
|
||||
log(fmt.Sprintf("Chart type: %d", s.chartType))
|
||||
}
|
||||
opts.Type = s.chartType
|
||||
chart()
|
||||
draw()
|
||||
case "142":
|
||||
mktLog(log, id, mujica.OHLCChart(c, candles, &s.window, opts))
|
||||
mktLog(log, id, chart.OHLCChart(c, candles, &s.window, opts))
|
||||
case "143":
|
||||
before := s.raw
|
||||
mktLog(log, id, mujica.HeikinAshiChart(c, candles, &s.window, &s.raw, opts))
|
||||
mktLog(log, id, chart.HeikinAshiChart(c, candles, &s.window, &s.raw, opts))
|
||||
if before != s.raw {
|
||||
log(fmt.Sprintf("Raw: %v", s.raw))
|
||||
}
|
||||
case "144":
|
||||
if mujica.NumberInput(c, &s.previous, mujica.NumberInputOptions{Min: 80, Max: 120, Step: 0.5, Label: "Previous close"}).Changed() {
|
||||
if input.NumberInput(c, &s.previous, input.NumberInputOptions{Min: 80, Max: 120, Step: 0.5, Label: "Previous close"}).Changed() {
|
||||
log(fmt.Sprintf("Previous close: %.2f", s.previous))
|
||||
}
|
||||
if mujica.Checkbox(c, &s.extended, "Extended sessions", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, &s.extended, "Extended sessions", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprintf("Extended sessions: %v", s.extended))
|
||||
}
|
||||
o := mujica.LineQuoteChartOptions{Frame: frame, PrevClose: s.previous}
|
||||
o := chart.LineQuoteChartOptions{Frame: frame, PrevClose: s.previous}
|
||||
if s.extended {
|
||||
o.RegularOpen = s.points[0].Time.Add(90 * time.Minute)
|
||||
o.RegularClose = s.points[0].Time.Add(8 * time.Hour)
|
||||
}
|
||||
mktLog(log, id, mujica.LineQuoteChart(c, points, o))
|
||||
mktLog(log, id, chart.LineQuoteChart(c, points, o))
|
||||
case "145":
|
||||
opts.Frame.Height = 150
|
||||
chart()
|
||||
draw()
|
||||
frame.Label = "Volume"
|
||||
frame.Height = 100
|
||||
mktLog(log, id, mujica.VolumeChart(c, candles, &s.window, mujica.VolumeChartOptions{Frame: frame}))
|
||||
mktLog(log, id, chart.VolumeChart(c, candles, &s.window, chart.VolumeChartOptions{Frame: frame}))
|
||||
case "146":
|
||||
mktLog(log, id, mujica.DepthChart(c, bids, asks, mujica.DepthChartOptions{Frame: frame}))
|
||||
mktLog(log, id, chart.DepthChart(c, bids, asks, chart.DepthChartOptions{Frame: frame}))
|
||||
case "147":
|
||||
if mujica.NumberInput(c, &s.fraction, mujica.NumberInputOptions{Min: 0.1, Max: 1, Step: 0.1, Label: "Value area fraction"}).Changed() {
|
||||
if input.NumberInput(c, &s.fraction, input.NumberInputOptions{Min: 0.1, Max: 1, Step: 0.1, Label: "Value area fraction"}).Changed() {
|
||||
log(fmt.Sprintf("Value area: %.1f", s.fraction))
|
||||
}
|
||||
mktLog(log, id, mujica.VolumeProfile(c, bins, mujica.VolumeProfileOptions{Frame: frame, ValueArea: s.fraction}))
|
||||
mktLog(log, id, chart.VolumeProfile(c, bins, chart.VolumeProfileOptions{Frame: frame, ValueArea: s.fraction}))
|
||||
case "148":
|
||||
if mujica.MultiChartLayout(c, &s.layout, mujica.MultiChartLayoutOptions{Height: 420}, func(i int, cell *mujica.ChartCell) {
|
||||
if mujica.TextInput(c, &cell.Symbol, mujica.TextInputOptions{Label: fmt.Sprintf("Symbol %d", i+1)}).Changed() {
|
||||
if chart.MultiChartLayout(c, &s.layout, chart.MultiChartLayoutOptions{Height: 420}, func(i int, cell *chart.ChartCell) {
|
||||
if input.TextInput(c, &cell.Symbol, input.TextInputOptions{Label: fmt.Sprintf("Symbol %d", i+1)}).Changed() {
|
||||
log(fmt.Sprintf("Cell %d: %s", i+1, cell.Symbol))
|
||||
}
|
||||
items := make([]mujica.SelectOption[mujica.Interval], len(mujica.Intervals))
|
||||
for j, iv := range mujica.Intervals {
|
||||
items[j] = mujica.SelectOption[mujica.Interval]{Value: iv, Label: string(iv)}
|
||||
items := make([]input.SelectOption[chart.Interval], len(chart.Intervals))
|
||||
for j, iv := range chart.Intervals {
|
||||
items[j] = input.SelectOption[chart.Interval]{Value: iv, Label: string(iv)}
|
||||
}
|
||||
if mujica.Select(c, &cell.Interval, items, mujica.SelectOptions{Label: fmt.Sprintf("Interval %d", i+1)}).Changed() {
|
||||
if input.Select(c, &cell.Interval, items, input.SelectOptions{Label: fmt.Sprintf("Interval %d", i+1)}).Changed() {
|
||||
log(fmt.Sprintf("Cell %d: %s", i+1, cell.Interval))
|
||||
}
|
||||
f := frame
|
||||
f.Label = cell.Symbol
|
||||
f.Height = 70
|
||||
mktLog(log, fmt.Sprint(i), mujica.CandlestickChart(c, candles, &cell.View, mujica.CandlestickOptions{Frame: f}))
|
||||
mktLog(log, fmt.Sprint(i), chart.CandlestickChart(c, candles, &cell.View, chart.CandlestickOptions{Frame: f}))
|
||||
}).Changed() {
|
||||
log(fmt.Sprintf("Layout: %d, active %d", s.layout.Count, s.layout.Active))
|
||||
}
|
||||
@@ -98,41 +100,41 @@ func mktDemo(c *ui.Context, log func(string), id string) {
|
||||
o.Frame.Label = chartID
|
||||
o.Sync = &s.sync
|
||||
o.SyncID = chartID
|
||||
mktLog(log, chartID, mujica.CandlestickChart(c, candles, v, o))
|
||||
mktLog(log, chartID, chart.CandlestickChart(c, candles, v, o))
|
||||
})
|
||||
}
|
||||
case "150":
|
||||
if mujica.TimeRangeSelector(c, &s.rangeValue, mujica.TimeRangeSelectorOptions{}).Changed() {
|
||||
if chart.TimeRangeSelector(c, &s.rangeValue, chart.TimeRangeSelectorOptions{}).Changed() {
|
||||
log(fmt.Sprintf("Range: %d", s.rangeValue.Preset))
|
||||
}
|
||||
from, to := s.rangeValue.Bounds(c.Now(), time.UTC)
|
||||
ui.Text(c, from.Format("2006-01-02")+" — "+to.Format("2006-01-02"))
|
||||
case "151":
|
||||
before := fmt.Sprint(s.favorites)
|
||||
if mujica.IntervalSelector(c, &s.interval, &s.favorites, mujica.IntervalSelectorOptions{}).Changed() {
|
||||
if chart.IntervalSelector(c, &s.interval, &s.favorites, chart.IntervalSelectorOptions{}).Changed() {
|
||||
log("Interval: " + string(s.interval))
|
||||
}
|
||||
if before != fmt.Sprint(s.favorites) {
|
||||
log("Favorites: " + fmt.Sprint(s.favorites))
|
||||
}
|
||||
mujica.CandleCountdown(c, s.interval, mujica.CandleCountdownOptions{})
|
||||
chart.CandleCountdown(c, s.interval, chart.CandleCountdownOptions{})
|
||||
case "152":
|
||||
if mujica.ChartTypeSwitcher(c, &s.chartType, mujica.ChartTypeSwitcherOptions{}).Changed() {
|
||||
if chart.ChartTypeSwitcher(c, &s.chartType, chart.ChartTypeSwitcherOptions{}).Changed() {
|
||||
log(fmt.Sprintf("Chart type: %d", s.chartType))
|
||||
}
|
||||
opts.Type = s.chartType
|
||||
chart()
|
||||
draw()
|
||||
case "153":
|
||||
if mujica.IndicatorSelector(c, &s.overlays, &s.panes, mujica.IndicatorSelectorOptions{}).Changed() {
|
||||
if chart.IndicatorSelector(c, &s.overlays, &s.panes, chart.IndicatorSelectorOptions{}).Changed() {
|
||||
log("Overlays changed")
|
||||
}
|
||||
opts.Overlays = s.overlays
|
||||
chart()
|
||||
draw()
|
||||
case "154":
|
||||
if mujica.IndicatorSelector(c, &s.overlays, &s.panes, mujica.IndicatorSelectorOptions{}).Changed() {
|
||||
if chart.IndicatorSelector(c, &s.overlays, &s.panes, chart.IndicatorSelectorOptions{}).Changed() {
|
||||
log("Indicators changed")
|
||||
}
|
||||
r := mujica.IndicatorPane(c, candles, &s.window, &s.panes, mujica.IndicatorPaneOptions{Label: "BTC", Height: 320, Status: frame.Status, Error: frame.Error})
|
||||
r := chart.IndicatorPane(c, candles, &s.window, &s.panes, chart.IndicatorPaneOptions{Label: "BTC", Height: 320, Status: frame.Status, Error: frame.Error})
|
||||
if r.PointChanged {
|
||||
log(fmt.Sprintf("Pane %d: point %d", r.Pane, r.Point))
|
||||
}
|
||||
@@ -143,43 +145,43 @@ func mktDemo(c *ui.Context, log func(string), id string) {
|
||||
log("Panes resized")
|
||||
}
|
||||
case "155":
|
||||
if mujica.IndicatorSelector(c, &s.overlays, &s.panes, mujica.IndicatorSelectorOptions{}).Changed() {
|
||||
if chart.IndicatorSelector(c, &s.overlays, &s.panes, chart.IndicatorSelectorOptions{}).Changed() {
|
||||
log("Indicators changed")
|
||||
}
|
||||
opts.Overlays = s.overlays
|
||||
chart()
|
||||
draw()
|
||||
case "156", "157":
|
||||
if mujica.TextInput(c, &s.drawings.Text, mujica.TextInputOptions{Label: "Annotation text"}).Changed() {
|
||||
if input.TextInput(c, &s.drawings.Text, input.TextInputOptions{Label: "Annotation text"}).Changed() {
|
||||
log("Annotation text changed")
|
||||
}
|
||||
if mujica.DrawingToolbar(c, &s.drawings, mujica.DrawingToolbarOptions{Horizontal: true}).Changed() {
|
||||
if chart.DrawingToolbar(c, &s.drawings, chart.DrawingToolbarOptions{Horizontal: true}).Changed() {
|
||||
log(fmt.Sprintf("Tool: %d, locked: %v", s.drawings.Tool, s.drawings.Locked))
|
||||
}
|
||||
before := fmt.Sprint(s.drawings.Items)
|
||||
opts.Drawings = &s.drawings
|
||||
chart()
|
||||
draw()
|
||||
if before != fmt.Sprint(s.drawings.Items) {
|
||||
log(fmt.Sprintf("Drawings: %d", len(s.drawings.Items)))
|
||||
}
|
||||
case "158":
|
||||
if mujica.CompareLegend(c, &s.compare).Changed() {
|
||||
if chart.CompareLegend(c, &s.compare).Changed() {
|
||||
log("Compared symbol removed")
|
||||
}
|
||||
if mujica.Button(c, "Restore ETH", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Restore ETH", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
s.compare = mktInitial().compare
|
||||
log("Restored ETH")
|
||||
}
|
||||
if len(candles) > 0 {
|
||||
opts.Compare = &s.compare
|
||||
}
|
||||
chart()
|
||||
draw()
|
||||
case "159":
|
||||
before := s.alerts[0].Price
|
||||
opts.Alerts = &s.alerts
|
||||
opts.OnAlert = func(a mujica.PriceAlertLine, last mujica.Candle) {
|
||||
opts.OnAlert = func(a chart.PriceAlertLine, last chart.Candle) {
|
||||
log(fmt.Sprintf("Alert %s crossed %.2f", a.ID, last.Close))
|
||||
}
|
||||
if mujica.Button(c, "Append crossing candle", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
if input.Button(c, "Append crossing candle", input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||
last := s.candles[len(s.candles)-1]
|
||||
next := last
|
||||
next.Time = last.Time.Add(time.Hour)
|
||||
@@ -194,19 +196,19 @@ func mktDemo(c *ui.Context, log func(string), id string) {
|
||||
candles = s.candles
|
||||
log("Appended candle")
|
||||
}
|
||||
chart()
|
||||
draw()
|
||||
if before != s.alerts[0].Price {
|
||||
log(fmt.Sprintf("Alert price: %.2f", s.alerts[0].Price))
|
||||
}
|
||||
case "160":
|
||||
if mujica.IntervalSelector(c, &s.interval, &s.favorites, mujica.IntervalSelectorOptions{}).Changed() {
|
||||
if chart.IntervalSelector(c, &s.interval, &s.favorites, chart.IntervalSelectorOptions{}).Changed() {
|
||||
log("Interval: " + string(s.interval))
|
||||
}
|
||||
if mujica.Checkbox(c, &s.hidden, "Hide countdown", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, &s.hidden, "Hide countdown", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprintf("Hidden: %v", s.hidden))
|
||||
}
|
||||
if !s.hidden {
|
||||
mujica.CandleCountdown(c, s.interval, mujica.CandleCountdownOptions{})
|
||||
chart.CandleCountdown(c, s.interval, chart.CandleCountdownOptions{})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -6,89 +6,93 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
mujica "mujicaui"
|
||||
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/chart"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
||||
)
|
||||
|
||||
type mktDemoState struct {
|
||||
candles []mujica.Candle
|
||||
points []mujica.QuotePoint
|
||||
bids, asks []mujica.DepthLevel
|
||||
bins []mujica.ProfileBin
|
||||
window mujica.MarketView
|
||||
second mujica.MarketView
|
||||
sync mujica.ChartSync
|
||||
layout mujica.MultiChartState
|
||||
interval mujica.Interval
|
||||
favorites []mujica.Interval
|
||||
rangeValue mujica.MarketRange
|
||||
chartType mujica.ChartType
|
||||
overlays []mujica.IndicatorOverlay
|
||||
panes mujica.IndicatorPanes
|
||||
drawings mujica.DrawingTools
|
||||
compare []mujica.CompareSymbol
|
||||
alerts []mujica.PriceAlertLine
|
||||
candles []chart.Candle
|
||||
points []chart.QuotePoint
|
||||
bids, asks []chart.DepthLevel
|
||||
bins []chart.ProfileBin
|
||||
window chart.MarketView
|
||||
second chart.MarketView
|
||||
sync chart.ChartSync
|
||||
layout chart.MultiChartState
|
||||
interval chart.Interval
|
||||
favorites []chart.Interval
|
||||
rangeValue chart.MarketRange
|
||||
chartType chart.ChartType
|
||||
overlays []chart.IndicatorOverlay
|
||||
panes chart.IndicatorPanes
|
||||
drawings chart.DrawingTools
|
||||
compare []chart.CompareSymbol
|
||||
alerts []chart.PriceAlertLine
|
||||
status string
|
||||
disabled, raw, redUp, hidden, extended bool
|
||||
previous, fraction float64
|
||||
}
|
||||
|
||||
func mktInitial() mktDemoState {
|
||||
s := mktDemoState{status: "Ready", interval: "1h", favorites: []mujica.Interval{"1m", "1h", "1D"}, previous: 100, fraction: 0.7, extended: true}
|
||||
s := mktDemoState{status: "Ready", interval: "1h", favorites: []chart.Interval{"1m", "1h", "1D"}, previous: 100, fraction: 0.7, extended: true}
|
||||
start := time.Date(2026, 10, 5, 8, 0, 0, 0, time.UTC)
|
||||
price := 100.0
|
||||
compare := make([]float64, 180)
|
||||
for i := range 180 {
|
||||
closeAt := price + math.Sin(float64(i)*0.7)*1.5 + math.Cos(float64(i)*0.13)*0.8
|
||||
s.candles = append(s.candles, mujica.Candle{Time: start.Add(time.Duration(i) * time.Hour), Open: price, High: max(price, closeAt) + 0.8, Low: min(price, closeAt) - 0.7, Close: closeAt, Volume: 1000 + float64(i%17)*130})
|
||||
s.points = append(s.points, mujica.QuotePoint{Time: start.Add(time.Duration(i) * 5 * time.Minute), Price: 100 + 2*math.Sin(float64(i)/10), Volume: 100 + float64(i%7)*20})
|
||||
s.candles = append(s.candles, chart.Candle{Time: start.Add(time.Duration(i) * time.Hour), Open: price, High: max(price, closeAt) + 0.8, Low: min(price, closeAt) - 0.7, Close: closeAt, Volume: 1000 + float64(i%17)*130})
|
||||
s.points = append(s.points, chart.QuotePoint{Time: start.Add(time.Duration(i) * 5 * time.Minute), Price: 100 + 2*math.Sin(float64(i)/10), Volume: 100 + float64(i%7)*20})
|
||||
compare[i] = 20 + float64(i)*0.08
|
||||
price = closeAt
|
||||
}
|
||||
for i := range 40 {
|
||||
s.bids = append(s.bids, mujica.DepthLevel{Price: 99.9 - float64(i)*0.1, Size: float64(10 + i*3)})
|
||||
s.asks = append(s.asks, mujica.DepthLevel{Price: 100.1 + float64(i)*0.1, Size: float64(8 + i*4)})
|
||||
s.bids = append(s.bids, chart.DepthLevel{Price: 99.9 - float64(i)*0.1, Size: float64(10 + i*3)})
|
||||
s.asks = append(s.asks, chart.DepthLevel{Price: 100.1 + float64(i)*0.1, Size: float64(8 + i*4)})
|
||||
}
|
||||
s.bins = mujica.VolumeProfileOf(s.candles, 24)
|
||||
s.compare = []mujica.CompareSymbol{{Name: "ETH", Closes: compare}}
|
||||
s.alerts = []mujica.PriceAlertLine{{ID: "target", Price: 104, Label: "Target"}}
|
||||
s.overlays = []mujica.IndicatorOverlay{{Kind: mujica.IndicatorMA, Params: []float64{10}}}
|
||||
s.panes = mujica.IndicatorPanes{Panes: []mujica.IndicatorPaneSpec{{Kind: mujica.IndicatorMACD}, {Kind: mujica.IndicatorRSI}}}
|
||||
s.bins = chart.VolumeProfileOf(s.candles, 24)
|
||||
s.compare = []chart.CompareSymbol{{Name: "ETH", Closes: compare}}
|
||||
s.alerts = []chart.PriceAlertLine{{ID: "target", Price: 104, Label: "Target"}}
|
||||
s.overlays = []chart.IndicatorOverlay{{Kind: chart.IndicatorMA, Params: []float64{10}}}
|
||||
s.panes = chart.IndicatorPanes{Panes: []chart.IndicatorPaneSpec{{Kind: chart.IndicatorMACD}, {Kind: chart.IndicatorRSI}}}
|
||||
s.layout.Count = 2
|
||||
for i := range s.layout.Cells {
|
||||
s.layout.Cells[i] = mujica.ChartCell{Symbol: fmt.Sprintf("Symbol %d", i+1), Interval: "1h"}
|
||||
s.layout.Cells[i] = chart.ChartCell{Symbol: fmt.Sprintf("Symbol %d", i+1), Interval: "1h"}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func mktControls(c *ui.Context, s *mktDemoState, log func(string)) mujica.ChartFrame {
|
||||
func mktControls(c *ui.Context, s *mktDemoState, log func(string)) chart.ChartFrame {
|
||||
ui.Row(c).Gap(12).Wrap().Children(func() {
|
||||
if mujica.Select(c, &s.status, []mujica.SelectOption[string]{{Value: "Ready", Label: "Ready"}, {Value: "Loading", Label: "Loading"}, {Value: "Failed", Label: "Failed"}, {Value: "Empty", Label: "Empty"}}, mujica.SelectOptions{Label: "Data state"}).Changed() {
|
||||
if input.Select(c, &s.status, []input.SelectOption[string]{{Value: "Ready", Label: "Ready"}, {Value: "Loading", Label: "Loading"}, {Value: "Failed", Label: "Failed"}, {Value: "Empty", Label: "Empty"}}, input.SelectOptions{Label: "Data state"}).Changed() {
|
||||
log("Data state: " + s.status)
|
||||
}
|
||||
if mujica.Checkbox(c, &s.disabled, "Disabled", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, &s.disabled, "Disabled", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprintf("Disabled: %v", s.disabled))
|
||||
}
|
||||
if mujica.Checkbox(c, &s.redUp, "Red up", mujica.CheckboxOptions{}).Changed() {
|
||||
if input.Checkbox(c, &s.redUp, "Red up", input.CheckboxOptions{}).Changed() {
|
||||
log(fmt.Sprintf("Red up: %v", s.redUp))
|
||||
}
|
||||
})
|
||||
trend := mujica.GreenUp
|
||||
trend := core.GreenUp
|
||||
if s.redUp {
|
||||
trend = mujica.RedUp
|
||||
trend = core.RedUp
|
||||
}
|
||||
mujica.UseTrend(c, trend)
|
||||
f := mujica.ChartFrame{Label: "BTC", Height: 220}
|
||||
core.UseTrend(c, trend)
|
||||
f := chart.ChartFrame{Label: "BTC", Height: 220}
|
||||
switch s.status {
|
||||
case "Loading":
|
||||
f.Status = mujica.DataLoading
|
||||
f.Status = data.DataLoading
|
||||
case "Failed":
|
||||
f.Status = mujica.DataFailed
|
||||
f.Status = data.DataFailed
|
||||
f.Error = "Archive unavailable"
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func mktLog(log func(string), name string, r mujica.ChartResult) {
|
||||
func mktLog(log func(string), name string, r chart.ChartResult) {
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("%s: point %d", name, r.Point))
|
||||
}
|
||||
|
||||
Loaded 100 of 1822 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user