Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .agents/skills/verify-deepseekbot/features/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ One file per user-facing feature. Each file has four sections: `Sub-features`, `
| Phone pairing and paired devices | no | `scripts/e2e-mobile-pairing-web.mjs`, `scripts/e2e-device-pairing.mjs`, `scripts/e2e-mobile-sim.mjs`, `scripts/e2e-mobile-pair-android.mjs` | [phone-pairing.md](phone-pairing.md) |
| Phone Chats drawer views and Android Host accent | no | none (shared-device native path) | [phone-drawer-views.md](phone-drawer-views.md) |
| Phone Chats swipe actions and Remote Host section creation | no | none (shared-device native path) | [phone-chat-row-actions.md](phone-chat-row-actions.md) |
| Phone Remote Host sections and responsive Pinned grid | no | shared-device queue; `scripts/e2e-mobile-sim.mjs` | [phone-pinned-grid.md](phone-pinned-grid.md) |
| Native mobile message composer | no (except Bot reply) | none (shared-device native path) | [phone-composer.md](phone-composer.md) |
| Native mobile chat backgrounds | no (except live-reply regression) | none (shared-device native path) | [phone-chat-backgrounds.md](phone-chat-backgrounds.md) |
| Android launch, sheets, shell and chat presentation | no | Launch: queued native check; interactions: `scripts/e2e-mobile-android-interactions.mjs` | [phone-android-interactions.md](phone-android-interactions.md) |
Expand Down
31 changes: 31 additions & 0 deletions .agents/skills/verify-deepseekbot/features/phone-pinned-grid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Phone Remote Host sections and Pinned grid

## Sub-features

- `organization`: selected Remote Host sections, pins, membership and order come from `rosterGet`; global/per-scope sorting comes from the same Host's native `settings/describe` namespace used by Web. A refresh reads both; there is no device-local arrangement.
- `pinned-grid`: non-sticky Pinned header and virtualized card bands precede ordinary rows. Cards show avatar/name/status/unread, without preview or time. Empty Pinned is hidden; empty custom sections remain visible. Column policy follows measured container width and system text scale.
- `collapse-search`: collapse is device-local per Host. Search presents full rows even for collapsed/pinned matches, title prefix before pins; clearing restores the Host arrangement without writes. All presents the current Host organization and separate Cloud rows.
- `operations`: ordinary rows retain swipe and iOS peek. Cards retain opening/peek and named native action menus/accessibility actions. Unpin only unpins and returns to retained membership. The inherited Add to section/create flow remains pin-preserving in this slice.

## How to get to it (user POV)

Pair a Remote Host, open Chats and choose that Host in the drawer. Pin a conversation from its menu, then return to Chats. Expand/collapse Pinned or a custom section. Search for a pinned name and clear search. Choose All and a Cloud workspace to inspect source separation.

## Driving it with verify-app

Submit this journey to the centralized test center through [shared device QA](../../../../docs/agents/device-qa.md); the designated runner owns `scripts/e2e-mobile-sim.mjs`, Metro, builds and QA Hosts. No model is required. Do not reuse or mutate a frozen acceptance candidate. Record exact base/head, binary identity, OS, viewport, locale, system text size and appearance.

1. Seed one disposable Host with a DM and group, pins but no custom sections; compare base flat rows with candidate Pinned grid. Verify avatar/name, working and unread state, absent preview/time, no duplicate ordinary row, and the final row in a long list. Scroll: Pinned must leave the viewport normally.
2. Create Work and Empty through the existing mobile section sheet; assign ordinary chats and pin one Work chat. Read `deepseekbot roster` on the same Host. The pin must retain Work membership, be absent from Work display, and return there on Unpin. An empty custom section remains reachable; an empty Pinned header disappears.
3. Use Web or the existing CLI `settings botharness-client` / reviewed settings writes plus roster ordering commands to set global manual/updated and scope manual/updated/inherit. Place loose rows between section blocks. Refresh mobile and compare canonical order to Web, including a chat without messages (`updatedAt` fallback). New messages must not reorder a manual scope. A failed settings refresh must retain the last confirmed roster and sorting; recover by retrying the refresh. Settings-only Web changes currently require refresh/reconnect on mobile.
4. Collapse Pinned and Work. Search for pinned and sectioned names: full rows with previews appear, ordered by title match then pin. Clear search and recover collapse/order. Switch to a second disposable Host with matching section IDs; its collapse state must be independent. Switch back and confirm the original preference. Collapse/search must not write arrangement.
5. Choose All: current Host groups/grid remain; Cloud rows retain their own labels/routes and cannot become Host pin/section targets. Choose a Cloud workspace: no Host section header or grid remains. No claim of multi-Host aggregation is made.
6. On ordinary rows repeat [phone-chat-row-actions](phone-chat-row-actions.md), including edge swipe, menu, mark-read and section creation. On cards open the chat/commit iOS preview once, Unpin, Mute, Mark as read and Add to section through native menus and VoiceOver/TalkBack actions. Preserve the owning Host route. Add to section remains pin-preserving; move-as-unpin-and-assign is not delivered here.
7. Capture matched native before/after on iOS and Android at default/intermediate/maximum system text × light/dark. Use long English/CJK names, rotation and live default→maximum→default resizing. Check grid column changes, full name wrapping, all targets, status/unread legibility, native focus and search with the actual soft keyboard. Pure width/font-scale projection tests do not prove glyph geometry or native accessibility.
8. Restore device settings; remove only task-owned fixtures. Keep sanitized screenshots, accessibility snapshots, console output and canonical reads with the test-center batch report.

## Gotchas

- Native screenshots, keyboard, screen-reader interaction and Human visual acceptance remain unrun until the designated runner records them. Source/DOM fixtures isolate native boundaries and are not native geometry evidence.
- No Organize/drag mode, local pin/order store, section rename/delete, or cross-Host aggregation in this slice. Obtain Human visual feedback before implementing the next slice.
- #2554 row layout and fixture follow-up are independently owned. Requalify the combined candidate when integrated; do not treat its frozen acceptance as evidence for this grid.
6 changes: 5 additions & 1 deletion CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -494,9 +494,13 @@ The Human-confirmed end of a Channel’s active membership and routing while ret
_Avoid_: Hidden Channel, Content Purge, provider conversation deletion

**Channel section**:
A user-created, collapsible grouping of Channels in the Channel list panel. Local display arrangement, not part of a Soul.
A user-created, collapsible grouping of Channels in the Channel list panel. Its membership and order belong to the owning Host's display arrangement, not to a Soul.
_Avoid_: folder, category, group

**Pinned section (置顶分区)**:
A built-in presentation of a Host's pinned Channels, separate from user-created Channel-section membership. Pinning preserves a Channel's original section membership while displaying it only in the Pinned section; unpinning restores its ordinary placement.
_Avoid_: copied Channel, user-created section, device-local pin store

**Section order (区块顺序)**:
The relative order of Channel sections in the Channel list panel: creation order by default, user-arranged afterwards. Ungrouped Channels may occupy top-level positions between sections without becoming sections.
_Avoid_: priority, layout order
Expand Down
6 changes: 5 additions & 1 deletion CONTEXT.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,9 +491,13 @@ _避免使用_:deleted Channel、archived Channel、muted Channel、Content Pu
_避免使用_:Hidden Channel、Content Purge、provider conversation deletion

**Channel section**:
用户创建、可折叠的 Channel 分组,显示在频道列表面板中。它只是本地 display arrangement,不属于 Soul。
用户创建、可折叠的 Channel 分组,显示在频道列表面板中。归属与顺序属于对应 Host 的 display arrangement,不属于 Soul。
_避免使用_:folder、category、group

**Pinned section (置顶分区)**:
内置的展示分区,呈现某个 Host 已置顶的 Channel,与用户创建的 Channel section 归属独立。置顶保留 Channel 的原分区归属,但只在置顶分区展示;取消置顶后恢复普通位置。
_避免使用_:Channel 副本、用户创建的分区、设备本地置顶存储

**Section order (区块顺序)**:
频道列表面板中 Channel section 的相对顺序:默认按创建顺序,之后可由用户排列。未分组 Channel 可以占据 section 之间的顶层位置,但不会因此成为 section。
_避免使用_:priority、layout order
Expand Down
13 changes: 13 additions & 0 deletions apps/mobile/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,19 @@ The shell is set by [ADR-0211](../../docs/adr/0211-the-mobile-app-has-two-tabs-w
- Creation binds the chosen Host ID or cloud account scope plus Organization ID. Cloud rechecks current identity and ownership before opening/submitting; stale scope refuses. Computer creation is unavailable on the phone after full WebView removal; CloudClient idempotent mutations remain the cloud business owner. Dismissal never starts a background operation.

- **Chats** shows the current Host:
- **Mobile organization target (Human-confirmed, 2026-10-22):** all choices below are accepted. Delivery is staged: #2571 implements Host section presentation and the Pinned grid first; Organize/drag and move-as-unpin-and-assign follow Human visual feedback. Native acceptance is a separate gate. These decisions supersede the older desktop-only organization restriction below.
- Only the built-in Pinned section uses a responsive avatar/name grid, with unread and working-state indicators and no message preview or timestamp. Columns adapt to available width and system text size. Ordinary Channel sections retain conversation rows with message previews.
- Pinned is first in the scroll content, scrolls normally, is hidden when empty in ordinary browsing, and cannot be reordered below ordinary sections. A pinned Channel appears there only, retaining its underlying section membership; ordinary unpin restores that placement.
- An explicit Organize Chats mode exposes drag handles for section order, pinned-card order and moving Channels between sections. Ordinary browsing preserves row swipe actions and iOS preview. Equivalent menu operations remain accessible without dragging.
- Section membership, pins, ordering and sort modes use the corresponding Host's canonical data and match Web. Dragging rows within a scope changes only that scope to manual sorting; new messages do not displace manual order. The scope menu can restore newest-message sorting.
- Dragging a Channel into Pinned pins it without changing its original section. Dragging a pinned Channel into an ordinary section unpins it and assigns that section. The Unpin menu action only removes the pin.
- The Move to section menu action has the same result as dropping into a destination section: it unpins and assigns that section. Use Move to section wording instead of Add to section. This supersedes PR #2552's pin-preserving assignment behavior; that frozen candidate is not evidence for the new design.
- Ordinary section blocks and ungrouped Channel rows can both move in the Host's top-level order, including across one another. Ungrouped rows remain loose entries, without a synthetic bottom section. Pinned stays outside that reorderable sequence.
- Organization runs only in the complete list of one selected Host. Invoking it from All or search first selects the corresponding Host and clears the search. All still presents Host sections, but Cloud entries never become targets of Host organization operations.
- Pinned is collapsible like ordinary sections. Collapse state is a device-local preference scoped to the Host. In Organize Chats mode, an empty Pinned section stays visible as a drop target; ordinary browsing hides it.
- Each completed drag or move immediately saves to the owning Host; Done only leaves Organize Chats mode. On failure, return to the last Host-confirmed arrangement and show a retryable error. An uncertain response requires canonical readback before replaying a mutation; changing Host invalidates the pending interaction.
- Search temporarily presents a flat conversation list, including ordinary rows for pinned matches. Rank title matches first and pins next; omit empty section headers and disable organization. Clearing search restores the grouped/grid presentation without changing Host ordering or membership.
- The Human confirmed all twelve interview decisions and the delivery sequence on 2026-10-22. First deliver actual Host section presentation and the Pinned grid for Human feedback; then extend Organize Chats and drag operations. Frozen PRs #2551, #2552 and #2554 retain their existing scopes and evidence; a new candidate requires separate qualification.
- Conversation rows keep the title and timestamp on one horizontal line at every system text size. The title takes the remaining width and truncates with tail ellipsis; the timestamp keeps its own single-line slot. Preview/status and count stay on a second line. A conversation without a timestamp does not reserve an empty metadata line. Preserve native font scaling and the complete accessible title/preview.
- The header has a compact Host title on the left and a Settings gear on the right, on both iOS and Android. Search and Settings are `HeaderAction` buttons, native to each platform: on iOS 26 and later they are SwiftUI Liquid Glass circles (`buttonStyle('glass')`) with SF Symbols (`magnifyingglass`, `gearshape`), so the system supplies the press, Reduce Transparency and Increase Contrast looks. The symbol follows Dynamic Type only up to `xxxLarge`, as system toolbar symbols do, so at accessibility sizes it stays inside its fixed glass circle while the button keeps its label; earlier iOS keeps the opaque `raised` circles. Android uses Material 3 standard icon buttons: no container, a 40dp circular ripple (`materialIconButton.container`) inside the 48dp touch target, like the actions of a Material top app bar. The Android search back and clear buttons use the same icon button. This row stays horizontal at every system text size: the Host chip shrinks, its name occupies one line with tail ellipsis, and the Settings target stays at the trailing edge. Keep native font scaling and the full accessible Host name; both targets retain the platform minimum touch size. Other Host-name presentations may wrap. The Host picker stays mounted between visits; its translation follows a critically damped UI-thread spring and its scrim is clamped. Hidden contents leave the accessibility tree.
- **+** is the circle beside the bottom navigation island. It opens the native **New PersonaBot** / **New Channel** menu and uses the app's create flow, or the full client where the app has none yet.
Expand Down
Loading
Loading