fix(ui): correct mirrored chat empty-state + clipped IP placeholder; add readiness-check skill; privacy page nav/meta

- app/session/[id].tsx: chat empty-state rendered mirrored on Android (inverted FlatList) — now an untransformed overlay
- app/connection/add.tsx: shortened clipped host placeholder
- .agents/skills/readiness-check: production-readiness gate (Play+F-Droid published, app+site health)
- docs/privacy: back-nav + canonical/theme-color/description meta
Verified: tsc clean, CUA smoke green (run 26814702062)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
engineer
2026-06-03 06:16:22 -07:00
parent c46f273ecd
commit 6563f5ef53
5 changed files with 415 additions and 13 deletions

View File

@@ -165,7 +165,7 @@ export default function AddConnectionScreen() {
<View style={styles.ipRow}>
<TextInput
style={[styles.input, styles.ipInput, isDark && styles.inputDark]}
placeholder="192.168.1.100 or my-mac.ts.net"
placeholder="192.168.1.100"
placeholderTextColor={isDark ? "#666666" : "#999999"}
value={ip}
onChangeText={setIp}

View File

@@ -516,14 +516,16 @@ export default function SessionScreen() {
</View>
) : null
}
ListEmptyComponent={
<View style={s.emptyInverted}>
<Ionicons name="chatbubble-outline" size={48} color={isDark ? "#444444" : "#cccccc"} />
<Text style={[s.emptyText, isDark && s.metaDark]}>Start a conversation</Text>
<Text style={[s.emptyHint, isDark && s.metaDark]}>Type / for commands</Text>
</View>
}
/>
{/* Empty state rendered OUTSIDE the inverted list to avoid the
inverted transform mirroring its text/icon (see #ui-mirror). */}
{messageData.length === 0 && (
<View style={s.emptyOverlay} pointerEvents="none">
<Ionicons name="chatbubble-outline" size={48} color={isDark ? "#444444" : "#cccccc"} />
<Text style={[s.emptyText, isDark && s.metaDark]}>Start a conversation</Text>
<Text style={[s.emptyHint, isDark && s.metaDark]}>Type / for commands</Text>
</View>
)}
{showScrollButton && (
<TouchableOpacity style={[s.scrollBtn, isDark && s.scrollBtnDark]} onPress={() => scrollToBottom(true)}>
<Ionicons name="chevron-down" size={24} color={isDark ? "#ffffff" : "#0a0a0a"} />
@@ -690,13 +692,13 @@ const s = StyleSheet.create({
},
loadingMoreText: { fontSize: 13, color: "#999999" },
// Empty (inverted list flips content, so use transform to un-flip)
emptyInverted: {
flex: 1,
// Empty state overlay — sits on top of the (empty) inverted list, untransformed,
// so its text/icon render upright and un-mirrored on Android.
emptyOverlay: {
...StyleSheet.absoluteFillObject,
justifyContent: "center",
alignItems: "center",
paddingVertical: 64,
transform: [{ scaleY: -1 }],
},
// Empty