Ariver
2026-05-10 192a009cb0b0f1b34b39d7ba3a7d1d790867e677
Lock colorless container interactions
4 files modified
60 ■■■■■ changed files
Apptag/ContentView.swift 44 ●●●●● patch | view | raw | blame | history
Apptag/Info.plist 2 ●●● patch | view | raw | blame | history
CHANGELOG.md 5 ●●●●● patch | view | raw | blame | history
Decisions.md 9 ●●●● patch | view | raw | blame | history
Apptag/ContentView.swift
@@ -204,6 +204,8 @@
    @State private var draggedTagNames: [String] = []  // live drag order
    @State private var dragItem: String? = nil          // currently dragged tag
    @State private var hoveredContainer: String? = nil  // colored container lift
    // Fixed interaction for "Colorless Container": hover fills persistently; click clears.
    @State private var filledColorlessContainer: String? = nil
    // Configurable defaults
    @AppStorage("defaultGroupName") private var defaultGroupName = "Other"
@@ -337,10 +339,16 @@
                ForEach(tagLabels) { tag in
                    TagPill(name: tag.name, colorIndex: tag.colorIndex,
                            action: {
                        if displayMode == "container" {
                            toggleColorlessFill(tag.id)
                        }
                        scrollTo(tag.id)
                    })
                    .onHover { hovering in
                        if hovering { scrollTo(tag.id) }
                        if hovering {
                            fillColorlessContainer(tag.id)
                            scrollTo(tag.id)
                        }
                    }
                }
            }.padding(.horizontal, 24)
@@ -353,10 +361,16 @@
                ForEach(tagLabels) { tag in
                    SideTagPill(name: tag.name, colorIndex: tag.colorIndex,
                                action: {
                        if displayMode == "container" {
                            toggleColorlessFill(tag.id)
                        }
                        scrollTo(tag.id)
                    })
                    .onHover { hovering in
                        if hovering { scrollTo(tag.id) }
                        if hovering {
                            fillColorlessContainer(tag.id)
                            scrollTo(tag.id)
                        }
                    }
                }
            }.padding(12)
@@ -455,6 +469,8 @@
    private func masonryCard(_ group: TagGroup, width: CGFloat) -> some View {
        let isColored = displayMode == "coloredContainer"
        let isColorless = displayMode == "container"
        let isColorlessFilled = isColorless && filledColorlessContainer == group.name
        let isHovered = hoveredContainer == group.name
        let tagColor = Color(nsColor: TagColor.nsColor(for: tagColors[group.name] ?? 0))
        return VStack(alignment: .leading, spacing: 6) {
@@ -485,7 +501,7 @@
        .padding(16)
        .background(
            RoundedRectangle(cornerRadius: 14)
                .fill(isColored ? tagColor.opacity(0.30) : Color.clear)
                .fill((isColored || isColorlessFilled) ? tagColor.opacity(0.30) : Color.clear)
                .background(
                    RoundedRectangle(cornerRadius: 14)
                        .fill(.ultraThinMaterial)
@@ -501,7 +517,17 @@
        .scaleEffect(isColored && isHovered ? 1.015 : 1.0)
        .animation(.spring(response: 0.25, dampingFraction: 0.82), value: isHovered)
        .onHover { hovering in
            hoveredContainer = hovering ? group.name : nil
            if isColored {
                hoveredContainer = hovering ? group.name : nil
            } else if hovering {
                fillColorlessContainer(group.name)
            }
        }
        .contentShape(RoundedRectangle(cornerRadius: 14))
        .onTapGesture {
            if isColorlessFilled {
                filledColorlessContainer = nil
            }
        }
    }
@@ -767,6 +793,16 @@
        withAnimation(.easeInOut(duration: 0.25)) { scrollProxy?.scrollTo(id, anchor: .top) }
    }
    private func fillColorlessContainer(_ id: String) {
        guard displayMode == "container" else { return }
        filledColorlessContainer = id
    }
    private func toggleColorlessFill(_ id: String) {
        guard displayMode == "container" else { return }
        filledColorlessContainer = (filledColorlessContainer == id) ? nil : id
    }
    func refreshApps() {
        DispatchQueue.global(qos: .userInitiated).async {
            var apps = AppIndexer.scan()
Apptag/Info.plist
@@ -19,7 +19,7 @@
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>3.2.0</string>
    <string>3.2.1</string>
    <key>CFBundleVersion</key>
    <string>1</string>
    <key>LSMinimumSystemVersion</key>
CHANGELOG.md
@@ -1,5 +1,10 @@
# Apptag Changelog
## [3.2.1] — 2026-05-10
- 固定「无色容器」交互逻辑:hover 标签或容器时保持标签色填充,再次点击标签或点击容器空白处清除填充
- 保持「彩色容器」逻辑独立:默认标签色填充,hover 仅增加阴影凸起效果
## [3.2.0] — 2026-05-10
- 新增 App 列表显示样式「彩色容器」:所有容器默认填充标签色,鼠标 hover 容器时增加阴影凸起效果
Decisions.md
@@ -1,2 +1,9 @@
项目决策记录(DECISIONS.md)
由 Hermes Agent 自动维护,用于记录每个版本的需求背景和决策过程。
由 Hermes Agent 自动维护,用于记录每个版本的需求背景和决策过程。
## 2026-05-10 — 无色容器交互锁定
- 「无色容器」是 App 列表第 2 种视图样式,对应 `displayMode == "container"`。
- 固定交互:hover 标签或对应容器区域时,容器保持标签色填充;鼠标移开后不自动清除。
- 清除方式:再次点击同一标签,或点击对应容器内部空白处。
- 后续迭代新增/修改其他样式时,不应改变以上无色容器交互逻辑;「彩色容器」应使用独立交互状态。