fix(tui): keep scoped archive groups separate
This commit is contained in:
parent
8fcbbda232
commit
9abbe55576
41
tui/tui.go
41
tui/tui.go
@ -2569,25 +2569,30 @@ func (m model) groupFields(item Item) (key, title, kind, scope string) {
|
||||
case LayoutChat:
|
||||
if m.groupMode == groupByAuthor {
|
||||
if author := strings.TrimSpace(itemAuthor(item)); author != "" {
|
||||
return "author:" + author, displayLabel(author), "person", strings.TrimSpace(item.Scope)
|
||||
scope := strings.TrimSpace(item.Scope)
|
||||
return scopedGroupKey("author", author, scope), displayLabel(author), "person", scope
|
||||
}
|
||||
}
|
||||
if m.groupMode == groupByThread {
|
||||
if thread := threadKey(item); thread != "" {
|
||||
scope := strings.TrimSpace(item.Scope)
|
||||
title := firstNonEmpty(item.Title, thread)
|
||||
return "thread:" + thread, displayLabel(title), "thread", strings.TrimSpace(item.Scope)
|
||||
return scopedGroupKey("thread", thread, scope), displayLabel(title), "thread", scope
|
||||
}
|
||||
}
|
||||
if container := strings.TrimSpace(item.Container); container != "" {
|
||||
return "container:" + container, displayLabel(container), "channel", strings.TrimSpace(item.Scope)
|
||||
scope := strings.TrimSpace(item.Scope)
|
||||
return scopedGroupKey("container", container, scope), displayLabel(container), "channel", scope
|
||||
}
|
||||
if author := strings.TrimSpace(itemAuthor(item)); author != "" {
|
||||
return "author:" + author, displayLabel(author), "person", strings.TrimSpace(item.Scope)
|
||||
scope := strings.TrimSpace(item.Scope)
|
||||
return scopedGroupKey("author", author, scope), displayLabel(author), "person", scope
|
||||
}
|
||||
case LayoutDocument:
|
||||
if m.groupMode == groupByContainer {
|
||||
if container := strings.TrimSpace(item.Container); container != "" {
|
||||
return "container:" + container, displayLabel(container), "database", strings.TrimSpace(item.Scope)
|
||||
scope := strings.TrimSpace(item.Scope)
|
||||
return scopedGroupKey("container", container, scope), displayLabel(container), "database", scope
|
||||
}
|
||||
}
|
||||
if m.groupMode == groupByScope {
|
||||
@ -2596,10 +2601,12 @@ func (m model) groupFields(item Item) (key, title, kind, scope string) {
|
||||
}
|
||||
}
|
||||
if parent := strings.TrimSpace(item.ParentID); parent != "" {
|
||||
return "parent:" + parent, displayLabel(parent), "parent", strings.TrimSpace(item.Scope)
|
||||
scope := strings.TrimSpace(item.Scope)
|
||||
return scopedGroupKey("parent", parent, scope), displayLabel(parent), "parent", scope
|
||||
}
|
||||
if container := strings.TrimSpace(item.Container); container != "" {
|
||||
return "container:" + container, displayLabel(container), "database", strings.TrimSpace(item.Scope)
|
||||
scope := strings.TrimSpace(item.Scope)
|
||||
return scopedGroupKey("container", container, scope), displayLabel(container), "database", scope
|
||||
}
|
||||
}
|
||||
for _, value := range []struct {
|
||||
@ -2619,6 +2626,15 @@ func (m model) groupFields(item Item) (key, title, kind, scope string) {
|
||||
return "row:" + title, displayLabel(title), firstNonEmpty(itemKind(item), "row"), strings.TrimSpace(item.Scope)
|
||||
}
|
||||
|
||||
func scopedGroupKey(kind, value, scope string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
scope = strings.TrimSpace(scope)
|
||||
if scope == "" {
|
||||
return kind + ":" + value
|
||||
}
|
||||
return kind + ":" + scope + "/" + value
|
||||
}
|
||||
|
||||
func compareGroups(left, right itemGroup, mode sortMode) (bool, bool) {
|
||||
switch mode {
|
||||
case sortCount:
|
||||
@ -3438,11 +3454,14 @@ func (m model) groupColumns(width int) []tableColumn {
|
||||
}
|
||||
|
||||
func (m model) shouldShowGroupScopeColumn() bool {
|
||||
if m.layoutPreset == LayoutChat {
|
||||
return false
|
||||
}
|
||||
seen := map[string]struct{}{}
|
||||
for _, group := range m.groups {
|
||||
if strings.TrimSpace(group.Scope) != "" {
|
||||
scope := strings.TrimSpace(group.Scope)
|
||||
if scope == "" {
|
||||
continue
|
||||
}
|
||||
seen[strings.ToLower(scope)] = struct{}{}
|
||||
if m.layoutPreset != LayoutChat || len(seen) > 1 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@ -400,6 +400,26 @@ func TestGroupColumnsOmitEmptyScope(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatGroupsDoNotMergeSameChannelAcrossScopes(t *testing.T) {
|
||||
m := newModel(Options{Layout: LayoutChat, Items: []Item{
|
||||
Row{Kind: "message", Scope: "workspace-a", Container: "general", Title: "one", CreatedAt: "2026-05-02T12:00:00Z"}.ItemForLayout(LayoutChat),
|
||||
Row{Kind: "message", Scope: "workspace-b", Container: "general", Title: "two", CreatedAt: "2026-05-02T13:00:00Z"}.ItemForLayout(LayoutChat),
|
||||
}})
|
||||
if len(m.groups) != 2 {
|
||||
t.Fatalf("same channel names in different scopes should stay separate: %#v", m.groups)
|
||||
}
|
||||
columns := m.groupColumns(90)
|
||||
foundScope := false
|
||||
for _, column := range columns {
|
||||
if column.Key == "scope" {
|
||||
foundScope = true
|
||||
}
|
||||
}
|
||||
if !foundScope {
|
||||
t.Fatalf("multi-scope chat groups should expose the scope column: %#v", columns)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVeryNarrowPanesStillShowCompactColumns(t *testing.T) {
|
||||
group := itemGroup{Kind: "channel", Count: 18, Latest: "2026-05-02T12:00:00Z", Title: "github-secure-session-4"}
|
||||
groupHeader := groupListHeader(28, sortDefault)
|
||||
@ -746,7 +766,7 @@ func TestChatMembersDefaultToNewestFirstLikeGitcrawl(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatMembersScopeSortUsesScopeNotContainer(t *testing.T) {
|
||||
func TestChatGroupScopeSortUsesScopeNotContainer(t *testing.T) {
|
||||
m := newModel(Options{
|
||||
Title: "slacrawl archive",
|
||||
Layout: LayoutChat,
|
||||
@ -755,13 +775,12 @@ func TestChatMembersScopeSortUsesScopeNotContainer(t *testing.T) {
|
||||
Row{Kind: "message", ID: "two", Scope: "a-workspace", Container: "general", Title: "two"}.ItemForLayout(LayoutChat),
|
||||
},
|
||||
})
|
||||
m.setMemberSortMode(sortScope)
|
||||
members := m.currentGroupMembers()
|
||||
if len(members) != 2 {
|
||||
t.Fatalf("members = %#v", members)
|
||||
m.setSortMode(sortScope)
|
||||
if len(m.groups) != 2 {
|
||||
t.Fatalf("groups = %#v", m.groups)
|
||||
}
|
||||
if got := m.items[members[0]].ID; got != "two" {
|
||||
t.Fatalf("scope-sorted first member = %q, want a-workspace row", got)
|
||||
if got := m.groups[0].Scope; got != "a-workspace" {
|
||||
t.Fatalf("scope-sorted first group = %q, want a-workspace", got)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1607,6 +1626,16 @@ func TestDocumentExplorerGroupsParentsAndListsPages(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocumentGroupsDoNotMergeSameParentAcrossWorkspaces(t *testing.T) {
|
||||
m := newModel(Options{Layout: LayoutDocument, Items: []Item{
|
||||
Row{Kind: "page", Scope: "workspace-a", ParentID: "Client Dashboards", Title: "one", UpdatedAt: "2026-05-02T12:00:00Z"}.ItemForLayout(LayoutDocument),
|
||||
Row{Kind: "page", Scope: "workspace-b", ParentID: "Client Dashboards", Title: "two", UpdatedAt: "2026-05-02T13:00:00Z"}.ItemForLayout(LayoutDocument),
|
||||
}})
|
||||
if len(m.groups) != 2 {
|
||||
t.Fatalf("same parent names in different workspaces should stay separate: %#v", m.groups)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocumentContextColumnsAvoidEmptyChatAuthorSlot(t *testing.T) {
|
||||
m := newModel(Options{
|
||||
Title: "notcrawl archive",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user