Chat Sessions: components 241-260
This commit is contained in:
22 files changed
+83
-88
No files matched your search
+3
-3
@@ -10,7 +10,7 @@ import (
|
||||
"mujicaui/theme"
|
||||
)
|
||||
|
||||
// ArtifactKind is what an artifact holds. The zero value is ArtifactCode.
|
||||
// ArtifactKind is what an artifact holds; zero is ArtifactCode.
|
||||
type ArtifactKind int
|
||||
|
||||
const (
|
||||
@@ -23,7 +23,7 @@ const (
|
||||
type ArtifactCardOptions struct {
|
||||
Kind ArtifactKind
|
||||
Title string
|
||||
// Detail is a short fact, such as "Go · 120 lines".
|
||||
// Detail is a short fact, such as "Go, 120 lines".
|
||||
Detail string
|
||||
// Streaming shows the artifact is still being written.
|
||||
Streaming bool
|
||||
@@ -34,7 +34,7 @@ var artifactKinds = [3]struct{ icon, name string }{
|
||||
{"file", "artifact_card.code"}, {"file-text", "artifact_card.document"}, {"layout-grid", "artifact_card.web"},
|
||||
}
|
||||
|
||||
// ArtifactCard stands for an artifact inside a message; a click or Enter asks to open the artifact panel.
|
||||
// ArtifactCard stands for an artifact; activating asks to open it.
|
||||
func ArtifactCard(c *ui.Context, opts ArtifactCardOptions) ChoiceResult {
|
||||
if opts.Title == "" {
|
||||
panic("mujica: ArtifactCard needs a Title")
|
||||
|
||||
+3
-3
@@ -9,7 +9,7 @@ import (
|
||||
"mujicaui/theme"
|
||||
)
|
||||
|
||||
// CapabilityCard explains one thing the assistant can do, with example prompts.
|
||||
// CapabilityCard explains one assistant capability with example prompts.
|
||||
type CapabilityCard struct {
|
||||
Title, Description string
|
||||
Icon *ui.SVG
|
||||
@@ -30,7 +30,7 @@ type CapabilityCardsView struct {
|
||||
Example string
|
||||
}
|
||||
|
||||
// CapabilityCards shows what the assistant can do; clicking an example hands it to the caller.
|
||||
// CapabilityCards shows capabilities; a clicked example goes to the caller.
|
||||
func CapabilityCards(c *ui.Context, cards []CapabilityCard, opts CapabilityCardsOptions) CapabilityCardsView {
|
||||
if opts.Columns < 0 {
|
||||
panic("mujica: CapabilityCards Columns is negative")
|
||||
@@ -76,7 +76,7 @@ func CapabilityCards(c *ui.Context, cards []CapabilityCard, opts CapabilityCards
|
||||
return v
|
||||
}
|
||||
|
||||
// capabilityExample is a quoted example prompt button; it reports a click.
|
||||
// capabilityExample is a quoted example button reporting a click.
|
||||
func capabilityExample(c *ui.Context, k theme.Tokens, text string) bool {
|
||||
d := Density(c)
|
||||
b := ui.ButtonBase(c).MinHeight(d.ControlHeight()).Padding(d.Unit(), d.Unit()*3).Justify(ui.Start).
|
||||
|
||||
+4
-4
@@ -8,7 +8,7 @@ import (
|
||||
"mujicaui/icons"
|
||||
)
|
||||
|
||||
// ChatEmptyKind is why a chat view is empty. The zero value is ChatEmptyNew.
|
||||
// ChatEmptyKind is why a chat is empty; zero is ChatEmptyNew.
|
||||
type ChatEmptyKind int
|
||||
|
||||
const (
|
||||
@@ -20,18 +20,18 @@ const (
|
||||
ChatEmptyOffline
|
||||
)
|
||||
|
||||
// ChatEmptyStateOptions configure a ChatEmptyState; empty texts use the kind's built-in copy.
|
||||
// ChatEmptyStateOptions configure a ChatEmptyState; empty texts use built-in copy.
|
||||
type ChatEmptyStateOptions struct {
|
||||
Kind ChatEmptyKind
|
||||
// Query, for ChatEmptyNoResults, is named in the description.
|
||||
Query string
|
||||
Title string
|
||||
Description string
|
||||
// Action replaces the kind's button text: Start, Clear search or Retry.
|
||||
// Action replaces the kind's built-in button text.
|
||||
Action string
|
||||
}
|
||||
|
||||
// ChatEmptyState explains an empty chat (new, no results, offline) and offers the next step.
|
||||
// ChatEmptyState explains an empty chat and offers the next step.
|
||||
func ChatEmptyState(c *ui.Context, opts ChatEmptyStateOptions) EmptyStateResult {
|
||||
switch opts.Kind {
|
||||
case ChatEmptyNew:
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"mujicaui/theme"
|
||||
)
|
||||
|
||||
// ExportFormat is the text form of an export. The zero value is ExportMarkdown.
|
||||
// ExportFormat is an export's text form; zero is ExportMarkdown.
|
||||
type ExportFormat int
|
||||
|
||||
const (
|
||||
@@ -22,7 +22,7 @@ const (
|
||||
ExportText
|
||||
)
|
||||
|
||||
// ExportRange is which messages an export holds. The zero value is ExportAll.
|
||||
// ExportRange is which messages an export holds; zero is ExportAll.
|
||||
type ExportRange int
|
||||
|
||||
const (
|
||||
@@ -35,7 +35,7 @@ const (
|
||||
|
||||
var exportRoles = map[SessionRole]string{SessionUser: "user", SessionAssistant: "assistant", SessionSystem: "system"}
|
||||
|
||||
// ExportConversation writes messages as Markdown, JSON or plain text; it does not parse their text.
|
||||
// ExportConversation writes messages as Markdown, JSON or plain text.
|
||||
func ExportConversation(title string, messages []SessionMessage, format ExportFormat) string {
|
||||
var b strings.Builder
|
||||
switch format {
|
||||
@@ -145,13 +145,13 @@ type ConversationExportOptions struct {
|
||||
// ConversationExportView is a built ConversationExport and the export asked for.
|
||||
type ConversationExportView struct {
|
||||
Element *ui.Element
|
||||
// Exported reports Content is ready for the caller to write, as Format.
|
||||
// Exported reports Content is ready for the caller to write.
|
||||
Exported bool
|
||||
Content string
|
||||
Format ExportFormat
|
||||
}
|
||||
|
||||
// ConversationExport picks a format and range, previews the result and hands the text to the caller to save.
|
||||
// ConversationExport picks format and range, previews, and hands over text.
|
||||
func ConversationExport(c *ui.Context, s *ConversationExportState, messages []SessionMessage, opts ConversationExportOptions) ConversationExportView {
|
||||
if s == nil || opts.Title == "" {
|
||||
panic("mujica: ConversationExport needs a state and a Title")
|
||||
|
||||
@@ -14,21 +14,21 @@ import (
|
||||
|
||||
// ConversationItemOptions configure a ConversationItem.
|
||||
type ConversationItemOptions struct {
|
||||
// Today decides the time stamp's form; zero uses the window clock.
|
||||
// Today decides the time stamp; zero uses the clock.
|
||||
Today Date
|
||||
Selected bool
|
||||
Disabled bool
|
||||
// Draggable lets the row be dragged onto a ProjectList as a ConversationDrag.
|
||||
// Draggable lets the row be dropped on a ProjectList.
|
||||
Draggable bool
|
||||
}
|
||||
|
||||
// ConversationDrag is the value a dragged conversation carries.
|
||||
type ConversationDrag struct{ ID string }
|
||||
|
||||
// ConversationItemView is a built ConversationItem and what the user asked for this frame.
|
||||
// ConversationItemView is a built ConversationItem and its requests.
|
||||
type ConversationItemView struct {
|
||||
Element *ui.Element
|
||||
// Opened, Deleted, Exported and PinToggled are requests for the caller to carry out.
|
||||
// Opened, Deleted, Exported and PinToggled are requests for the caller.
|
||||
Opened, Deleted, Exported, PinToggled bool
|
||||
// Renamed reports a confirmed new title, already in conv.Title.
|
||||
Renamed bool
|
||||
@@ -41,7 +41,7 @@ type conversationItemState struct {
|
||||
|
||||
type conversationItemKey struct{}
|
||||
|
||||
// ConversationItem is a conversation row: title, time, and a menu to rename in place, pin, export or delete.
|
||||
// ConversationItem is a conversation row with rename, pin, export, delete.
|
||||
func ConversationItem(c *ui.Context, conv *ChatConversation, opts ConversationItemOptions) ConversationItemView {
|
||||
if conv == nil || conv.ID == "" {
|
||||
panic("mujica: ConversationItem needs a conversation with an ID")
|
||||
@@ -118,7 +118,7 @@ func ConversationItem(c *ui.Context, conv *ChatConversation, opts ConversationIt
|
||||
return v
|
||||
}
|
||||
|
||||
// conversationItemEditor is the in-place title field: Enter keeps a non-blank title, Escape cancels.
|
||||
// conversationItemEditor edits the title in place; Escape cancels.
|
||||
func conversationItemEditor(c *ui.Context, k theme.Tokens, conv *ChatConversation, st *conversationItemState, v *ConversationItemView) {
|
||||
d := Density(c)
|
||||
in := ui.TextInputBase(c, &st.draft).Grow(1).MinHeight(d.ControlHeight()-4).Padding(0, d.Unit()*2).
|
||||
@@ -142,7 +142,7 @@ func conversationItemEditor(c *ui.Context, k theme.Tokens, conv *ChatConversatio
|
||||
}
|
||||
}
|
||||
|
||||
// conversationItemMenu is a borderless ellipsis button opening the menu build fills.
|
||||
// conversationItemMenu is a borderless ellipsis button opening a menu.
|
||||
func conversationItemMenu(c *ui.Context, label string, build func(m *PopupMenu)) *ui.Element {
|
||||
b := iconAction(c, icons.Must("ellipsis"), label).Role(ui.RoleMenuButton).Shrink(0)
|
||||
open := ui.Local(b, conversationItemKey{}, func() bool { return false })
|
||||
|
||||
@@ -37,7 +37,7 @@ type ConversationListState struct {
|
||||
plan conversationListPlan
|
||||
}
|
||||
|
||||
// conversationListPlan is the grouped rows, rebuilt only when the data or day changes.
|
||||
// conversationListPlan holds grouped rows, rebuilt on new data or day.
|
||||
type conversationListPlan struct {
|
||||
src []ChatConversation
|
||||
day Date
|
||||
@@ -45,7 +45,7 @@ type conversationListPlan struct {
|
||||
built bool
|
||||
}
|
||||
|
||||
// conversationListRow is group header g (item < 0) or an index into the items.
|
||||
// conversationListRow is a header (item < 0) or item index.
|
||||
type conversationListRow struct {
|
||||
group, item int
|
||||
}
|
||||
@@ -60,10 +60,10 @@ type ConversationListView struct {
|
||||
// Changed reports a newly chosen conversation, in State.Selected.
|
||||
func (v ConversationListView) Changed() bool { return v.changed }
|
||||
|
||||
// Submitted reports Enter or a double click on the chosen conversation.
|
||||
// Submitted reports Enter or a double click on the choice.
|
||||
func (v ConversationListView) Submitted() bool { return v.submitted }
|
||||
|
||||
// ConversationList groups conversations into pinned, today, yesterday, past 7 days and older, building only rows in view.
|
||||
// ConversationList groups conversations by recency, building only visible rows.
|
||||
func ConversationList(c *ui.Context, s *ConversationListState, items []ChatConversation, opts ConversationListOptions, row func(conv ChatConversation)) ConversationListView {
|
||||
if s == nil {
|
||||
panic("mujica: ConversationList needs a state")
|
||||
@@ -181,7 +181,7 @@ var conversationListGroups = [5]string{
|
||||
"conversation_list.week", "conversation_list.older",
|
||||
}
|
||||
|
||||
// conversationListGroup is the group index of conv: pinned, today, yesterday, 7 days, older.
|
||||
// conversationListGroup returns the recency group index of conv.
|
||||
func conversationListGroup(conv ChatConversation, today Date) int {
|
||||
day := DateOf(conv.Updated)
|
||||
switch {
|
||||
|
||||
@@ -20,9 +20,9 @@ type ConversationSearchHit struct {
|
||||
|
||||
// ConversationSearchOptions configure a ConversationSearch.
|
||||
type ConversationSearchOptions struct {
|
||||
// Loading shows the caller's search for the query is under way.
|
||||
// Loading shows the caller's search is under way.
|
||||
Loading bool
|
||||
// Error is the caller's failed search; it shows with a retry button.
|
||||
// Error describes a failed search, shown with a retry button.
|
||||
Error string
|
||||
Placeholder string
|
||||
Label string
|
||||
@@ -32,9 +32,9 @@ type ConversationSearchOptions struct {
|
||||
// ConversationSearchView is a built ConversationSearch and this frame's events.
|
||||
type ConversationSearchView struct {
|
||||
Element *ui.Element
|
||||
// QueryChanged asks the caller to search again for the new query.
|
||||
// QueryChanged asks the caller to search for the new query.
|
||||
QueryChanged bool
|
||||
// Chosen is the hit opened by a click or Enter, empty for none.
|
||||
// Chosen is the hit opened this frame, or empty.
|
||||
Chosen string
|
||||
Retry bool
|
||||
}
|
||||
@@ -47,7 +47,7 @@ type conversationSearchState struct {
|
||||
|
||||
type conversationSearchKey struct{}
|
||||
|
||||
// ConversationSearch searches titles and content; the caller supplies hits for the query and the matches are highlighted.
|
||||
// ConversationSearch shows caller-supplied hits for a query, highlighting matches.
|
||||
func ConversationSearch(c *ui.Context, query *string, hits []ConversationSearchHit, opts ConversationSearchOptions) ConversationSearchView {
|
||||
if query == nil {
|
||||
panic("mujica: ConversationSearch needs a query")
|
||||
@@ -97,7 +97,7 @@ func conversationSearchList(c *ui.Context, s *conversationSearchState, q string,
|
||||
s.rows.Key = func(i int) any { return hits[i].ID }
|
||||
s.rows.Label = func(i int) string { return hits[i].Title }
|
||||
s.rows.Selected = &s.cursor
|
||||
// Rows have one height, so the list can size itself before measuring them.
|
||||
// Rows share one height, so the list sizes itself early.
|
||||
rowH := d.ControlHeight() + FontSize(c, theme.CaptionSize)*theme.LineHeight + 2
|
||||
restore := dataSelectTheme(c, 0)
|
||||
list := ui.List(c, &s.rows, len(hits), func(i int) {
|
||||
@@ -134,7 +134,7 @@ func conversationSearchList(c *ui.Context, s *conversationSearchState, q string,
|
||||
return chosen
|
||||
}
|
||||
|
||||
// conversationSearchSpans splits text into spans, marking each case-insensitive match of q.
|
||||
// conversationSearchSpans marks each case-insensitive match of q in text.
|
||||
func conversationSearchSpans(text, q string, k theme.Tokens) []ui.Span {
|
||||
r := []rune(text)
|
||||
var spans []ui.Span
|
||||
@@ -152,7 +152,7 @@ func conversationSearchSpans(text, q string, k theme.Tokens) []ui.Span {
|
||||
return spans
|
||||
}
|
||||
|
||||
// conversationSearchMatches returns rune ranges of case-insensitive matches of q in text.
|
||||
// conversationSearchMatches returns rune ranges where q matches, ignoring case.
|
||||
func conversationSearchMatches(text, q string) [][2]int {
|
||||
t, p := []rune(text), []rune(q)
|
||||
if len(p) == 0 {
|
||||
|
||||
+4
-7
@@ -7,11 +7,10 @@ import (
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"mujicaui/internal"
|
||||
"mujicaui/theme"
|
||||
)
|
||||
|
||||
// CostEstimatorOptions configure a CostEstimator. Prices are per million tokens, from the caller.
|
||||
// CostEstimatorOptions configure a CostEstimator; prices are per million tokens.
|
||||
type CostEstimatorOptions struct {
|
||||
InputTokens, OutputTokens int
|
||||
InputPrice, OutputPrice float64
|
||||
@@ -21,7 +20,7 @@ type CostEstimatorOptions struct {
|
||||
Digits int
|
||||
}
|
||||
|
||||
// CostEstimate is the cost of the input, the output and both.
|
||||
// CostEstimate is the input, output and total cost.
|
||||
type CostEstimate struct{ Input, Output, Total float64 }
|
||||
|
||||
// EstimateCost prices token counts at per-million rates.
|
||||
@@ -31,7 +30,7 @@ func EstimateCost(inputTokens, outputTokens int, inputPrice, outputPrice float64
|
||||
return CostEstimate{in, out, in + out}
|
||||
}
|
||||
|
||||
// CostEstimator shows input and output tokens and their estimated cost at the caller's prices.
|
||||
// CostEstimator shows token counts and cost at caller prices.
|
||||
func CostEstimator(c *ui.Context, opts CostEstimatorOptions) *ui.Element {
|
||||
if opts.InputTokens < 0 || opts.OutputTokens < 0 {
|
||||
panic("mujica: CostEstimator token counts must not be negative")
|
||||
@@ -76,9 +75,7 @@ func CostEstimator(c *ui.Context, opts CostEstimatorOptions) *ui.Element {
|
||||
cell(msg(c, "cost_estimator.output"), k.Text, false)
|
||||
cell(strconv.Itoa(opts.OutputTokens), k.Text, true)
|
||||
cell(money(est.Output), k.Text, true)
|
||||
ui.Box(c).ColumnSpan(3).Height(9).Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
internal.Rule(p, r.X, r.X+r.W, r.Y+r.H/2, k.Ornament)
|
||||
})
|
||||
ui.Box(c).ColumnSpan(3).Height(theme.BorderWidth).MarginY(d.Unit()).Background(k.Ornament)
|
||||
ui.Text(c, msg(c, "cost_estimator.total")).Font(theme.SerifFont).TextColor(k.Text)
|
||||
cell(strconv.Itoa(opts.InputTokens+opts.OutputTokens), k.Text, true)
|
||||
ui.Text(c, money(est.Total)).Bold().TextColor(k.AccentText).JustifySelf(ui.End).FontFeatures("tnum")
|
||||
|
||||
+3
-5
@@ -16,7 +16,7 @@ type DateSeparatorOptions struct {
|
||||
Today Date
|
||||
}
|
||||
|
||||
// DateSeparator marks where a day's messages begin: Today, Yesterday, or the full date in the window's language.
|
||||
// DateSeparator marks where a day's messages begin, localized.
|
||||
func DateSeparator(c *ui.Context, day Date, opts DateSeparatorOptions) *ui.Element {
|
||||
if !day.Valid() {
|
||||
panic("mujica: DateSeparator needs a valid date")
|
||||
@@ -30,9 +30,7 @@ func DateSeparator(c *ui.Context, day Date, opts DateSeparatorOptions) *ui.Eleme
|
||||
label := dateSeparatorLabel(c, day, today)
|
||||
row := ui.Row(c).FillWidth().Gap(u*3).AlignItems(ui.Center).Padding(u*2, 0).Role(ui.RoleText).Label(label)
|
||||
line := func() {
|
||||
ui.Box(c).Grow(1).Height(9).Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
internal.Rule(p, r.X, r.X+r.W, r.Y+r.H/2, k.Border)
|
||||
})
|
||||
ui.Box(c).Grow(1).Height(theme.BorderWidth).Background(k.Border)
|
||||
}
|
||||
row.Children(func() {
|
||||
line()
|
||||
@@ -59,7 +57,7 @@ func dateSeparatorLabel(c *ui.Context, day, today Date) string {
|
||||
msg(c, fmt.Sprintf("calendar.month_%d", day.Month)), day.Day, day.Year)
|
||||
}
|
||||
|
||||
// dateSeparatorWhen is a short time stamp: the clock today, else the relative day or date.
|
||||
// dateSeparatorWhen is a short stamp: time, day or date.
|
||||
func dateSeparatorWhen(c *ui.Context, t time.Time, today Date) string {
|
||||
day := DateOf(t)
|
||||
if day == today {
|
||||
|
||||
+4
-4
@@ -10,7 +10,7 @@ import (
|
||||
"mujicaui/theme"
|
||||
)
|
||||
|
||||
// FeedbackRating is the thumb the user gave. The zero value is FeedbackUp.
|
||||
// FeedbackRating is the thumb given; zero is FeedbackUp.
|
||||
type FeedbackRating int
|
||||
|
||||
const (
|
||||
@@ -18,7 +18,7 @@ const (
|
||||
FeedbackDown
|
||||
)
|
||||
|
||||
// FeedbackValue is what the user wrote: chosen reasons and a comment.
|
||||
// FeedbackValue is the chosen reasons and a comment.
|
||||
type FeedbackValue struct {
|
||||
Reasons []string
|
||||
Comment string
|
||||
@@ -29,7 +29,7 @@ type FeedbackFormOptions struct {
|
||||
Rating FeedbackRating
|
||||
// Reasons replaces the built-in reasons for the rating.
|
||||
Reasons []string
|
||||
// Submitting shows the caller sending the feedback and blocks the form.
|
||||
// Submitting shows the caller sending; it blocks the form.
|
||||
Submitting bool
|
||||
Disabled bool
|
||||
}
|
||||
@@ -45,7 +45,7 @@ var feedbackReasons = [2][]string{
|
||||
{"feedback_form.down.wrong", "feedback_form.down.unhelpful", "feedback_form.down.unsafe", "feedback_form.down.style"},
|
||||
}
|
||||
|
||||
// FeedbackForm asks why after a thumbs up or down: reason chips and a comment, then Submit or Cancel.
|
||||
// FeedbackForm asks for reasons and a comment after a rating.
|
||||
func FeedbackForm(c *ui.Context, value *FeedbackValue, opts FeedbackFormOptions) FeedbackFormView {
|
||||
if value == nil {
|
||||
panic("mujica: FeedbackForm needs a value")
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"mujicaui/theme"
|
||||
)
|
||||
|
||||
// KnowledgeFile is a file in a project's knowledge, sized in tokens by the caller.
|
||||
// KnowledgeFile is a project knowledge file, sized in tokens.
|
||||
type KnowledgeFile struct {
|
||||
ID, Name string
|
||||
Tokens int
|
||||
@@ -26,16 +26,16 @@ type ProjectKnowledgePanelOptions struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
// ProjectKnowledgePanelView is a built panel and what the user asked for.
|
||||
// ProjectKnowledgePanelView is a built panel and its requests.
|
||||
type ProjectKnowledgePanelView struct {
|
||||
Element *ui.Element
|
||||
// Add asks the caller to pick files to add.
|
||||
Add bool
|
||||
// Removed is the ID of a file to remove, empty for none.
|
||||
// Removed is the ID of a file to remove.
|
||||
Removed string
|
||||
}
|
||||
|
||||
// ProjectKnowledgePanel lists a project's knowledge files with add, remove and how full it is.
|
||||
// ProjectKnowledgePanel lists knowledge files with add, remove and usage.
|
||||
func ProjectKnowledgePanel(c *ui.Context, files []KnowledgeFile, opts ProjectKnowledgePanelOptions) ProjectKnowledgePanelView {
|
||||
if opts.Capacity <= 0 {
|
||||
panic("mujica: ProjectKnowledgePanel needs a positive Capacity")
|
||||
@@ -90,7 +90,7 @@ func ProjectKnowledgePanel(c *ui.Context, files []KnowledgeFile, opts ProjectKno
|
||||
return v
|
||||
}
|
||||
|
||||
// projectKnowledgeMeter shows used of capacity; near and over the limit add a word and an icon, not only color.
|
||||
// projectKnowledgeMeter shows usage; limits get words and icons.
|
||||
func projectKnowledgeMeter(c *ui.Context, k theme.Tokens, used, capacity int) {
|
||||
d := Density(c)
|
||||
frac := float64(used) / float64(capacity)
|
||||
|
||||
+5
-5
@@ -12,7 +12,7 @@ import (
|
||||
"mujicaui/theme"
|
||||
)
|
||||
|
||||
// ChatProject is a project that holds conversations; Folder groups projects, empty for none.
|
||||
// ChatProject holds conversations; Folder groups projects, empty for none.
|
||||
type ChatProject struct {
|
||||
ID, Name, Folder string
|
||||
}
|
||||
@@ -23,7 +23,7 @@ type ProjectListOptions struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
// ProjectListState is the chosen project and which folders and projects are open.
|
||||
// ProjectListState is the chosen project and open folders and projects.
|
||||
type ProjectListState struct {
|
||||
// Selected is the chosen project's ID; empty for none.
|
||||
Selected string
|
||||
@@ -38,12 +38,12 @@ type ProjectListView struct {
|
||||
Changed bool
|
||||
// Opened is a conversation the user opened, empty for none.
|
||||
Opened string
|
||||
// Moved asks the caller to move conversation MoveID into project MoveTo.
|
||||
// Moved asks the caller to move MoveID into MoveTo.
|
||||
Moved bool
|
||||
MoveID, MoveTo string
|
||||
}
|
||||
|
||||
// ProjectList shows folders of projects and their conversations; drop a ConversationDrag on a project, or use Move to, to file it.
|
||||
// ProjectList shows project folders and conversations; drops move them.
|
||||
func ProjectList(c *ui.Context, s *ProjectListState, projects []ChatProject, conversations func(projectID string) []ChatConversation, opts ProjectListOptions) ProjectListView {
|
||||
if s == nil || conversations == nil {
|
||||
panic("mujica: ProjectList needs a state and conversations")
|
||||
@@ -91,7 +91,7 @@ func ProjectList(c *ui.Context, s *ProjectListState, projects []ChatProject, con
|
||||
return v
|
||||
}
|
||||
|
||||
// projectListFolder is a folder heading on a CollapsibleBase trigger, which reports expanded.
|
||||
// projectListFolder is a folder heading on a CollapsibleBase trigger.
|
||||
func projectListFolder(c *ui.Context, title string, open *bool) *ui.Element {
|
||||
k := Tokens(c)
|
||||
d := Density(c)
|
||||
|
||||
+8
-8
@@ -13,7 +13,7 @@ import (
|
||||
"mujicaui/theme"
|
||||
)
|
||||
|
||||
// PromptTemplate is a reusable prompt; {{name}} in Body is a variable to fill before inserting.
|
||||
// PromptTemplate is a reusable prompt; {{name}} marks a variable.
|
||||
type PromptTemplate struct {
|
||||
ID, Category, Title, Body string
|
||||
}
|
||||
@@ -24,7 +24,7 @@ type PromptLibraryOptions struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
// PromptLibraryState is the library's category, search, chosen template and variable values.
|
||||
// PromptLibraryState holds category, query, chosen template and variable values.
|
||||
type PromptLibraryState struct {
|
||||
// Category filters templates; empty shows all.
|
||||
Category string
|
||||
@@ -34,7 +34,7 @@ type PromptLibraryState struct {
|
||||
shown promptLibraryShown
|
||||
}
|
||||
|
||||
// promptLibraryShown caches the filtered templates until the data, query or category changes.
|
||||
// promptLibraryShown caches filtered templates until data, query or category change.
|
||||
type promptLibraryShown struct {
|
||||
src []PromptTemplate
|
||||
query, category string
|
||||
@@ -43,10 +43,10 @@ type promptLibraryShown struct {
|
||||
built bool
|
||||
}
|
||||
|
||||
// PromptLibraryView is a built PromptLibrary and the text inserted this frame.
|
||||
// PromptLibraryView is a built PromptLibrary and inserted text.
|
||||
type PromptLibraryView struct {
|
||||
Element *ui.Element
|
||||
// Inserted is the chosen template with its variables filled, empty for none.
|
||||
// Inserted is the filled template, or empty.
|
||||
Inserted string
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ func PromptLibraryFill(body string, values map[string]string) string {
|
||||
})
|
||||
}
|
||||
|
||||
// PromptLibrary browses templates by category and search, previews one, fills its variables and inserts it.
|
||||
// PromptLibrary browses, previews, fills and inserts prompt templates.
|
||||
func PromptLibrary(c *ui.Context, s *PromptLibraryState, templates []PromptTemplate, opts PromptLibraryOptions) PromptLibraryView {
|
||||
if s == nil {
|
||||
panic("mujica: PromptLibrary needs a state")
|
||||
@@ -152,7 +152,7 @@ func promptLibraryCategory(c *ui.Context, k theme.Tokens, s *PromptLibraryState,
|
||||
}
|
||||
}
|
||||
|
||||
// selectableRow is a focusable row chosen by click, Enter or Space, marked with Selection and a diamond.
|
||||
// selectableRow is a focusable row marked chosen with a diamond.
|
||||
func selectableRow(c *ui.Context, k theme.Tokens, key, label string, chosen bool) bool {
|
||||
d := Density(c)
|
||||
b := ui.ButtonBase(c).Key(key).MinHeight(d.ControlHeight()).Padding(0, d.Unit()*2).Gap(d.Unit() * 2).Justify(ui.Start).
|
||||
@@ -176,7 +176,7 @@ func selectableRow(c *ui.Context, k theme.Tokens, key, label string, chosen bool
|
||||
return b.Clicked() && !b.IsDisabled()
|
||||
}
|
||||
|
||||
// promptLibraryPreview shows the filled body, a field per variable, and Insert once all are filled.
|
||||
// promptLibraryPreview shows the body, variable fields and Insert.
|
||||
func promptLibraryPreview(c *ui.Context, k theme.Tokens, s *PromptLibraryState, t PromptTemplate, disabled bool) string {
|
||||
d := Density(c)
|
||||
vars := PromptLibraryVariables(t.Body)
|
||||
|
||||
+3
-3
@@ -29,7 +29,7 @@ type RegenerateChoice struct {
|
||||
|
||||
// RegenerateMenuOptions configure a RegenerateMenu.
|
||||
type RegenerateMenuOptions struct {
|
||||
// Models lists models to retry with; Current is marked and left out.
|
||||
// Models lists models to retry with; Current is left out.
|
||||
Models []string
|
||||
Current string
|
||||
// Variants replaces the built-in Longer, Shorter, Simpler and More detailed.
|
||||
@@ -37,14 +37,14 @@ type RegenerateMenuOptions struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
// RegenerateMenuView is a built menu and the retry chosen this frame.
|
||||
// RegenerateMenuView is a built menu and the chosen retry.
|
||||
type RegenerateMenuView struct {
|
||||
Element *ui.Element
|
||||
Chosen bool
|
||||
Choice RegenerateChoice
|
||||
}
|
||||
|
||||
// RegenerateMenu offers regenerate, retry with another model, and variants such as longer or shorter.
|
||||
// RegenerateMenu offers retry, another model, and rewrite variants.
|
||||
func RegenerateMenu(c *ui.Context, opts RegenerateMenuOptions) RegenerateMenuView {
|
||||
variants := opts.Variants
|
||||
if variants == nil {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"mujicaui/theme"
|
||||
)
|
||||
|
||||
// SessionRole is who wrote a message. The zero value is SessionUser.
|
||||
// SessionRole is who wrote a message; zero is SessionUser.
|
||||
type SessionRole int
|
||||
|
||||
const (
|
||||
@@ -22,7 +22,7 @@ const (
|
||||
SessionSystem
|
||||
)
|
||||
|
||||
// SessionMessage is one message of a saved conversation, as plain text.
|
||||
// SessionMessage is one saved message, as plain text.
|
||||
type SessionMessage struct {
|
||||
Role SessionRole
|
||||
// Author names the writer; empty uses the role's name.
|
||||
@@ -39,7 +39,7 @@ type SharedConversationViewOptions struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
// SharedConversationView shows a shared conversation read-only, with who shared it and when; it reports Copy link.
|
||||
// SharedConversationView shows a shared conversation read-only with Copy link.
|
||||
func SharedConversationView(c *ui.Context, messages []SessionMessage, opts SharedConversationViewOptions) ChoiceResult {
|
||||
if opts.Title == "" {
|
||||
panic("mujica: SharedConversationView needs a Title")
|
||||
@@ -78,7 +78,7 @@ func SharedConversationView(c *ui.Context, messages []SessionMessage, opts Share
|
||||
|
||||
type sharedConversationKey struct{}
|
||||
|
||||
// sessionMessageView is one read-only message: author, time and selectable plain text.
|
||||
// sessionMessageView shows one read-only message with author and time.
|
||||
func sessionMessageView(c *ui.Context, k theme.Tokens, m SessionMessage) {
|
||||
d := Density(c)
|
||||
if m.Role < SessionUser || m.Role > SessionSystem {
|
||||
|
||||
@@ -17,7 +17,7 @@ type StopGeneratingButtonOptions struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
// StopGeneratingButton asks to stop a reply while one is generating; Cmd+. (Ctrl+. elsewhere) does too.
|
||||
// StopGeneratingButton asks to stop generating; Mod+. does too.
|
||||
func StopGeneratingButton(c *ui.Context, opts StopGeneratingButtonOptions) ChoiceResult {
|
||||
if !opts.Generating {
|
||||
return ChoiceResult{}
|
||||
|
||||
+3
-3
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
// SuggestionChipsOptions configure SuggestionChips.
|
||||
type SuggestionChipsOptions struct {
|
||||
// Send makes a click send the suggestion; by default it fills the input.
|
||||
// Send makes a click send instead of filling the input.
|
||||
Send bool
|
||||
// Label names the group, such as "Follow-up questions".
|
||||
Label string
|
||||
@@ -24,11 +24,11 @@ type SuggestionChipsView struct {
|
||||
Element *ui.Element
|
||||
// Chosen is the suggestion clicked, empty for none.
|
||||
Chosen string
|
||||
// Sent reports that Chosen should be sent now; otherwise it is already in the input.
|
||||
// Sent asks to send Chosen now instead of filling.
|
||||
Sent bool
|
||||
}
|
||||
|
||||
// SuggestionChips offers starter prompts or follow-ups; a click fills *input, or with Send asks to send.
|
||||
// SuggestionChips offers prompts that fill *input or ask to send.
|
||||
func SuggestionChips(c *ui.Context, input *string, suggestions []string, opts SuggestionChipsOptions) SuggestionChipsView {
|
||||
if input == nil {
|
||||
panic("mujica: SuggestionChips needs an input")
|
||||
|
||||
@@ -17,23 +17,23 @@ import (
|
||||
type SystemPromptEditorOptions struct {
|
||||
// Default is what Restore default puts back.
|
||||
Default string
|
||||
// Saved is the text last saved; a different value shows as unsaved.
|
||||
// Saved is the text last saved; differences show as unsaved.
|
||||
Saved string
|
||||
// MaxLength is the character limit; over it, Save is blocked. Zero is none.
|
||||
// MaxLength limits characters, blocking Save beyond it; zero is none.
|
||||
MaxLength int
|
||||
// Saving shows the caller's save in progress and blocks editing.
|
||||
Saving bool
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
// SystemPromptEditorView is a built editor and what the user asked for.
|
||||
// SystemPromptEditorView is a built editor and its requests.
|
||||
type SystemPromptEditorView struct {
|
||||
Element *ui.Element
|
||||
// Save asks the caller to save *value; Restored reports the default was put back.
|
||||
// Save asks to save *value; Restored reports the default returned.
|
||||
Save, Restored bool
|
||||
}
|
||||
|
||||
// SystemPromptEditor edits a system prompt with a character count, restore default, an unsaved mark and Mod+S to save.
|
||||
// SystemPromptEditor edits a system prompt with count, restore and save.
|
||||
func SystemPromptEditor(c *ui.Context, value *string, opts SystemPromptEditorOptions) SystemPromptEditorView {
|
||||
if value == nil {
|
||||
panic("mujica: SystemPromptEditor needs a value")
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 19 KiB |
@@ -11,11 +11,11 @@ import (
|
||||
|
||||
// ThinkingIndicatorOptions configure a ThinkingIndicator.
|
||||
type ThinkingIndicatorOptions struct {
|
||||
// Phase is the caller's stage text, such as "Searching the web"; empty uses "Thinking".
|
||||
// Phase is the caller's stage text; empty uses "Thinking".
|
||||
Phase string
|
||||
}
|
||||
|
||||
// ThinkingIndicator shows the assistant is working: three diamonds rising in turn and the phase; still under reduced motion.
|
||||
// ThinkingIndicator shows rising diamonds and a phase, still when reduced.
|
||||
func ThinkingIndicator(c *ui.Context, opts ThinkingIndicatorOptions) *ui.Element {
|
||||
k := Tokens(c)
|
||||
d := Density(c)
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ type WelcomeScreenOptions struct {
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
// WelcomeScreen greets a new conversation with capability cards and a starter-prompt slot; it reports a chosen example.
|
||||
// WelcomeScreen greets a new conversation with cards and prompts.
|
||||
func WelcomeScreen(c *ui.Context, opts WelcomeScreenOptions) CapabilityCardsView {
|
||||
if opts.Greeting == "" {
|
||||
panic("mujica: WelcomeScreen needs a Greeting")
|
||||
|
||||
Reference in new issue
Block a user