fix(ux): 10 UI/UX bugs from pre-release audit (toward no-bug launch gate)

Found via parallel screen audit; each confirmed in code:
- AuthGate: auto-prompt biometrics on lock (useEffect was imported but unused)
- CodeBlock: horizontal scroll for long code lines (were wrapped/mangled)
- DiffView: horizontal scroll instead of numberOfLines=1 truncation
- chat: biometric-cancel on send shows feedback instead of silently dropping msg
- chat: send failure restores input + attachments and alerts
- chat: removed dead /compact + /clear builtin commands (advertised, no-op)
- sessions: delete + rename failures alert instead of silent; rename guarded
  against double-submit
- sessions: onRefresh spinner no longer hangs forever if a refresh rejects
- add/edit connection: validate URL has http(s):// scheme before save/test

typecheck clean, 65/65 unit tests pass. Runtime UI behavior still needs on-device
verification per the pre-posting test gate (HANDOFF §0b).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
engineer
2026-06-08 05:34:12 -07:00
parent 609bd45889
commit 42fa36f3cd
7 changed files with 103 additions and 57 deletions

View File

@@ -111,6 +111,7 @@ export default function SessionsScreen() {
const [isCreating, setIsCreating] = useState(false)
const [renaming, setRenaming] = useState<Session | null>(null)
const [renameText, setRenameText] = useState("")
const renamingInFlight = useRef(false)
const { sessions, isLoading, error, loadSessions, createSession, deleteSession } = useSessions()
const {
@@ -149,9 +150,14 @@ export default function SessionsScreen() {
const onRefresh = useCallback(async () => {
setRefreshing(true)
await Promise.all([loadSessions(), refreshProject()])
setRefreshing(false)
}, [])
try {
await Promise.all([loadSessions(), refreshProject()])
} catch (err) {
console.error("Refresh failed:", err)
} finally {
setRefreshing(false)
}
}, [loadSessions, refreshProject])
const handleRename = useCallback((session: Session) => {
setRenameText(session.title || "")
@@ -160,13 +166,21 @@ export default function SessionsScreen() {
const submitRename = useCallback(async () => {
const title = renameText.trim()
if (!title || !renaming) return
if (!title || !renaming || renamingInFlight.current) return
const renameClient = renaming.directory ? (clientForDirectory(renaming.directory) ?? client) : client
if (!renameClient) return
await renameClient.session.update(renaming.id, { title })
setRenaming(null)
setRenameText("")
loadSessions()
renamingInFlight.current = true
try {
await renameClient.session.update(renaming.id, { title })
setRenaming(null)
setRenameText("")
loadSessions()
} catch (err) {
console.error("Rename failed:", err)
Alert.alert("Rename failed", "Could not rename the session. Please try again.")
} finally {
renamingInFlight.current = false
}
}, [renaming, renameText, client, clientForDirectory, loadSessions])
const handleDelete = useCallback(
@@ -177,7 +191,12 @@ export default function SessionsScreen() {
text: "Delete",
style: "destructive",
onPress: async () => {
await deleteSession(session.id)
try {
await deleteSession(session.id)
} catch (err) {
console.error("Delete failed:", err)
Alert.alert("Delete failed", "Could not delete the session. Please try again.")
}
},
},
])