Skip to content

Version 670 - clean2#29122

Merged
chrisnojima merged 109 commits into
masterfrom
nojima/HOTPOT-next-670-clean-2
Jun 8, 2026
Merged

Version 670 - clean2#29122
chrisnojima merged 109 commits into
masterfrom
nojima/HOTPOT-next-670-clean-2

Conversation

@chrisnojima-zoom

Copy link
Copy Markdown
Contributor

No description provided.

@chrisnojima-zoom

Copy link
Copy Markdown
Contributor Author

Replaces #28898 . squashed to help git

chrisnojima and others added 27 commits May 29, 2026 09:44
- Delete TeamWithPopup (unused everywhere)
- Delete timeline-marker.meta.tsx (single color constant, inlined)
- Move TimelineMarker to system-git-push/ (only caller)
- Move PlatformIcon to profile/ (only callers are in profile/)
- Remove BottomSheetModal/Backdrop/ScrollView from index (only used
  internally in popup/, not via Kb.*)
- Remove ProfileCard from index (wired internally, never imported
  externally)
- Box2Animated removed; callers use createAnimatedComponent(Kb.Box2)
  or Animated.createAnimatedComponent(Kb.Box2) locally
- Remove dead ReAnimated={} export from index
- Remove duplicate ConnectedNameWithIcon alias (same as NameWithIcon)
- box.tsx: import Reanimated from ./reanimated instead of directly
  from react-native-reanimated
* devices: clean up structure and organization

* skills: move simplify-ui-section to repo skill

* devices: simplify ui section — extract icon type helper, merge stylesheets, rename context

* devices: fold rpc.tsx into common.tsx, flatten getDeviceIconType signature, dedupe icon logic

* devices: fix getIcon fallback to be type-aware, use object-literal pattern

* skill: add Box2 props and gap guidance to simplify-ui-section

Documents which style properties can be lifted into Box2 props (purely
structural, no visual change) and how to use gap/gapStart/gapEnd to
replace per-child margins (slight visual change — must be validated with
user before applying). Also relaxes the hard-line no-visual-changes rule
to allow validated visual changes with explicit user sign-off.

* common-adapters: add ClickableBox3 (Box2 + click props), migrate devices/

- Extract box2ClassNames() from Box2 desktop branch into shared helper
- Export box2SharedProps from box.tsx
- ClickableBox3Props = Box2Props & {onClick?, onLongPress?, hitSlop?}
- CB3 desktop: box2ClassNames + clickable-box2 CSS class
- CB3 mobile: box2SharedProps + Pressable
- Migrate devices/ CB1/CB2 → CB3, eliminate inner Box2 wrappers
- Simplify mobileAddHeader style (flex/position props move to CB3 props)
- Add plans/clickablebox3.md migration plan with per-directory checklist
- Add/update migrate-clickable-box skill for CB3 migration

* plans: mark CB3 implementation and devices/ pilot as done

* common-adapters: move ClickableBox3 impl into box.tsx

* WIP

* devices: fix TLF list overflow in revoke screen

Replace fixed-height virtualized List with ScrollView so long TLF names that wrap to multiple lines don't overlap adjacent rows.

* devices: simplify UI — remove redundant wrappers, move HeaderTitle to common
* update ui skill

* simplify-ui-section skill: add iteration loop after implementation

Adds Step 6 directing the model to re-read and re-analyze files after
each implementation pass, stopping only when a full pass yields no
meaningful findings. Also updates the flowchart to show the loop.

* provision: lazy-require expo-camera to fix Electron crash

expo-camera uses native code that crashes on Electron at import time.
Move the require inside the mobile-only component body so it never
evaluates on desktop.

* migrate CB/CB2 → CB3: git, incoming-share, signup, provision, people, settings

* fix settings-item CB3: add fullWidth to prevent alignSelf:center shrinking list rows

* fix ClickableBox3: forward missing props to both desktop and mobile

Desktop: onMouseDown/Leave/Move/Up, onContextMenu, title, tooltip/data-tooltip.
Mobile: onLayout, collapsable, pointerEvents.

* WIP

* cb3 2 (#29263)

* migrate CB/CB2 → CB3: profile

* migrate CB/CB2 → CB3: tracker, menubar, app, router-v2

* clean up iconContainer: remove redundant clickable/flexBoxColumn, fold in icon size

* update skill

* add onMouseEnter to Box2Props and forward it in ClickableBox3 desktop

* fix lint: suppress consistent-type-imports for expo-camera require cast

* skill
* metro: block non-source folders from file watcher

* deps: bump expo and related packages
* clickablebox 3 - teams (#29265)

* clickablebox 3 - team-building (#29265)

* clickablebox 3 - fs (#29265)

* only pick up the folders from shared

* fix profile dropdown button full-width regression on mobile (#29265)

* fix fs sort button content-sizing regression on desktop (#29265)

* fix fs path-item header and destination-picker full-width regression on mobile (#29265)

* update migrate-clickable-box skill with fullWidth gotchas (#29265)

* cb3 5 (#29267)

* migrate shared/chat/ ClickableBox/CB2 → CB3

* migrate shared/common-adapters/ ClickableBox/CB2 → CB3

* complete ClickableBox3 migration: remove legacy CB1/CB2 exports

* WIP

* fixes

* Fix info panel tabs: natural width, centered text, divider spans full tab width

* WIP

* fix prove-your dialog items to be full width

Without fullWidth={true}, ClickableBox3 gets alignSelf:center applied,
centering each provider row instead of spanning the modal width.

* fix CardListItem body text centering: add fullWidth to body Box2

Nested Box2 direction="vertical" without fullWidth gets box2_centered
(align-self:center) applied, horizontally centering it within its
column flex parent. Fixed across all CardListItem callers and in
list-item.tsx's body wrapper.

* remove ChoiceList/RichButton, replace usages with ListItem Card

* remove ClickableBox/ClickableBox2 and migration plan — all usages migrated to ClickableBox3

* mass clean (#29268)

* simplify-ui: git/ — remove redundant wrappers, dead styles/comments, normalize patterns

* simplify-ui: devices/ — deduplicate icon logic, extract constants, replace style props

* simplify-ui: wallets/ — rename Container, extract shared styles, remove redundant props

* simplify-ui: tracker/ — fix hover-opacity typo, dedup utilities, rename Container, replace flexShrink props

* simplify-ui: settings/ — remove redundant wrappers, move styles to props, clean dead code

* simplify-ui: crypto/ — inline IIFEs, merge imports, clean up style props and names

* simplify-ui: teams/ — merge styles, fix functions-as-values, extract string constants, remove wrappers

* simplify-ui: signup/ — extract shared input width, fix prop shorthands, remove dead code

* simplify-ui: people/ — remove identity maps, dead code, inline wrappers, move style props

* simplify-ui: profile/ — remove dead code, dedup styles, replace Kb shims, noShrink props

* address PR feedback: safe nav in async callback, fix avatar overlay positioning, restore .tsx extension

* simplify-ui: full codebase pass — rename Containers, remove dead code, move style props

Ran simplify-ui-section skill across all UI directories via parallel subagents:
chat/, common-adapters/, fs/, login/, provision/, team-building/, unlock-folders/,
menubar/, pinentry/, incoming-share/, deeplinks/, router-v2/, and revisits of
profile/, people/, signup/, crypto/, teams/, settings/, tracker/, wallets/,
devices/, git/.

* fix back button hover area: remove inline-block and size(14) from iconContainer

The 14×14px fixed size caused the hover highlight to appear only in the
upper-left corner while the icon extended beyond it. The container now
sizes naturally around the icon with padding defining the hit area.

* fix Checkbox centering: add alignSelf=flex-start to ClickableBox3

* WIP

* fix iOS e2e: restore testID on mobile crypto nav rows

The RichButton→ListItem migration dropped testID; wrap ListItem in Box2 to carry it.
* Add e2e screen tests for Electron desktop

- Test coverage for: chat, crypto, devices, files, git, people/profile, settings (all subpages), teams (browse + inner tabs)
- Add testIDs to nav tabs (tab-bar.desktop.tsx), settings subpages, teams body/tabs, files TLF rows, profile page
- Fixture reloads page at worker start to clear stale state
- navigateToTeams uses force:true to bypass WebkitAppRegion:drag blocking the Teams nav tab when a team detail is open
- teams-inner tests use text-based selectors to avoid dependency on testIDs that require app rebuild

* Fix skipped e2e desktop tests for files navigation and git rows

- Add GIT_REPO_ROW testID to git row component (was defined but never applied)
- Remove test.fixme guards from files folder navigation tests (KBFS UI works without mount)
- Fix files folder navigation assertions to use filter textbox instead of files-browser testID (nav stack keeps root screen mounted/hidden, causing strict mode violations)
- Remove conditional skip from git repo row test

* Add iOS Maestro flows for files navigation and git, add e2e skill

- Add files-folders.yaml: tap each TLF type row, verify back button appears (confirms subfolder navigation), navigate back to root
- Add git.yaml: navigate via More → Git, verify git-repo-list renders, conditional screenshot if rows present
- Update branch scripts to run files-folders + git flows on both platforms
- Add keybase-e2e-tests skill capturing two-harness structure, testID rules, iOS nav patterns, and Playwright gotchas
- Update plans/flow-test.md to reference skill and track progress on buckets 12 and 13

* Bucket 2: crypto output tests (Electron all 4, iOS encrypt+sign)

- Add CRYPTO_OUTPUT testID to CryptoOutput success-state Box2 in output.tsx
- crypto-outputs.test.ts: type text in each tab, auto-run triggers, verify output renders
  - Decrypt: encrypts first then feeds ciphertext to decrypt input
  - Verify: signs first then feeds signed text to verify input
  - Scope CRYPTO_OUTPUT query to tab container (TabRouter mounts all tabs simultaneously)
  - Use .last() for textbox selection (encrypt tab has two: recipients + main input)
- crypto-outputs.yaml (iOS): encrypt and sign outputs; decrypt/verify need clipboard support
- Update branch scripts and plan

* Bucket 3: chat conversation view iOS flow

- chat-conversation.yaml: open first inbox row, verify message list renders, navigate back
- Desktop tests already existed and pass
- Update branch scripts and plan

* Buckets 5+6: settings subpages iOS flow (About, Advanced, Display, Notifications, Feedback)

- settings-subpages.yaml: navigate More → each settings page, verify testID renders, back
- Desktop tests already exist and pass (9 tests: Advanced, About, Backup, Chat, Display,
  Feedback, Files, Notifications, Screen Protector)
- Password modal skipped (needs Account settings navigation + no testID)
- Update branch scripts and plan; note Bucket 6 partially covered by existing iOS flows

* Buckets 9 + 11: team detail tabs and people/profile iOS flows

- teams-inner.yaml: open first team, navigate Members/Settings/Bots/Channels tabs
- people-profile.yaml: People feed renders; profile via conditional username tap in feed
- Desktop tests already existed and pass (6 tests)
- Update branch scripts and plan

* Buckets 6+8: device detail test + settings subpages Bucket 6 iOS

- Add DEVICE_PAGE testID to device-page.tsx + test-ids.ts
- device-detail.test.ts: click first device row, verify device-page renders
- device-detail.yaml: More → Devices → tap row → verify device-page
- Extend settings-subpages.yaml with Chat, Files, Backup (Bucket 6 iOS items)
- Update branch scripts and plan

* Bucket 10: team member page test

- Add TEAMS_MEMBER_PAGE testID to member/index.new.tsx + test-ids.ts
- team-member.test.ts: click smoke user's username in member list, verify page renders
- team-member.yaml: iOS equivalent using KB_SMOKE_USER env var
- Update branch scripts and plan

* Fix crypto-outputs and settings-subpages iOS e2e tests

crypto-outputs: tapOn text "Encrypt" was hitting the nav header title
instead of the button. Added testID support to ButtonProps/ButtonNative
and CRYPTO_RUN_BUTTON testID to InputActionsBar. Encrypt/sign output is a
modal sheet with Cancel (not backButton), so navigation updated to
Cancel + backButton. Added scrollUntilVisible for Crypto since the More
screen retains scroll position across test runs.

settings-subpages: "About" and "Notifications" require scrolling (in a
separate section below the Settings list). Added scrollUntilVisible for
both. Reordered items top-to-bottom to minimize needed scrolls. Fixed
settings-files testID missing from the mobile render path.

escape-to-tabs: added swipe DOWN at end to reset scroll position at the
start of each flow, preventing stale More screen scroll state from
breaking subsequent flows.

* Plumb testIDs through ListItem and StillCommon instead of wrapper boxes

Rather than wrapping components in an extra Box2 just to attach a testID,
add testID support to ListItem (passed to ClickableBox3) and StillCommon
(passed to ListItem). Remove the wrapper boxes in tlf-type.tsx and
teams/team/index.tsx. Move TEAMS_BODY testID to the SectionList.

* Use KeyboardStickyView for crypto input action bars on iOS (#29270)

* Use KeyboardStickyView for crypto input action bars on iOS

Replaces KeyboardAvoidingView2 with KeyboardStickyView (react-native-keyboard-controller) for the bottom action bar on all four crypto input pages (encrypt, decrypt, sign, verify). The sticky view uses Reanimated to track keyboard height frame-by-frame on the UI thread, eliminating the layout-recalculation lag that caused the bar to trail the keyboard animation.

* try codegraph

* Address Copilot PR feedback on e2e desktop tests

- Skip own-profile test when KB_SMOKE_USER is unset
- Use NAV_TAB_CHAT/NAV_TAB_FILES testID constants instead of text= selectors
- Use NAV_TAB_CHAT constant in fixtures.ts waitFor (was hard-coded string)
- Update flow-test.md and PERF-TESTING.md script names: electron→desktop

* Address second round of Copilot PR feedback

- Wire testID as data-testid in ButtonDesktop
- git repo row test: wait for rows with timeout instead of instant count check
- teams members tab: assert on TEAMS_MEMBER_LIST testID instead of brittle text
…29274)

Batch simplification pass across ~150 files in chat, common-adapters,
teams, fs, settings, profile, login, team-building, provision, crypto,
signup, unlock-folders, people, devices, tracker, wallets, git, app,
menubar, pinentry, and incoming-share.

Key changes:
- Box2 props: migrate alignItems/centerChildren/fullWidth/noShrink/flex/
  padding/relative/overflow out of style objects
- Style utilities: Styles.size(), paddingH/V(), marginH/V(), padding(),
  bottomDivider(), border(), fillAbsolute(), rounded(), textEllipsis
- Remove redundant wrapper Box2 layers and dead style entries
- Collapse double-nested Box2s where inner props can merge to outer

Net: -325 lines
…29275) (#29275)

143 files changed: move layout properties from stylesheets to Box2 props
(fullWidth, padding, alignItems, noShrink, relative, flex, etc.), replace
inline style objects with Kb.Styles utilities (size, paddingH/V, marginH/V,
bottomDivider, textEllipsis), and remove dead style keys and unused imports.
Also fix pre-existing `electron` → `isElectron` bug in add-to-channels.tsx.
* Add Storybook for desktop platform with devices/ stories

- .storybook/main.ts: webpack5 config reusing desktop alias/babel/define patterns
- .storybook/mocks/electron.ts: KB2 stub so platform.tsx loads without Electron preload
- .storybook/preview.ts: injects globalThis._fromPreload, base decorator
- .storybook/preview-head.html: font-face + CSS reset via staticDirs fonts
- staticDirs: fonts/electron + images served at correct URL paths
- tsconfig.desktop.json: include .storybook/ files
- devices/device-icon.stories.tsx: 7 stories
- devices/row.stories.tsx: 6 stories (incl. BadgedDeviceIDsContext)
- devices/device-page.stories.tsx: 6 stories
- plans/storybook.md: todo list of remaining shared/ folders

Run with: cd shared && yarn storybook

* Add storybook screenshots to desktop e2e report and baseline

- generate-electron-report: parse storybook-desktop/ PNGs, render as
  separate section with slider diff vs storybook-prev/ baseline
- generate-ios-report: add .section-hdr CSS for full-width grid divider
- save-baseline now also saves storybook screenshots to storybook-prev/
- package.json: add storybook:screenshot script

* storybook

* Add slideshow and lazy loading to e2e HTML reports

- `loading="lazy"` on all report images
- Slideshow button in header: opens full-screen modal, cycles images at 0.75s with CSS fade-in
- Controls bar: prev/next/pause buttons + counter (e.g. 5/233)
- Keyboard: ←/→ navigate, Space toggles play/pause, Esc closes

* Fix dark mode in storybook screenshots and e2e tests

Use page.emulateMedia({colorScheme}) to set prefers-color-scheme via CDP
instead of document.documentElement.style.colorScheme. This activates
@media (prefers-color-scheme: dark) CSS rules that the real app depends on.

- screenshot-storybook.mts: emulateMedia before each light/dark capture
- fixtures.ts: emulateMedia replaces setNativeTheme for dark e2e project
- playwright.config.ts: add electron-flows-dark project

* WIP

* Replace chrisnojima with testuser in all stories/tests

* Address Copilot PR feedback

- Use Playwright bundled Chromium when CHROME_PATH is unset (fixes CI)
- Skip dark mode dispatch when preference is already correct
- Remove unnecessary `as any` cast in announcement story

* Address more Copilot PR feedback

- Fix webpack resolve.extensions order so .desktop.tsx/.desktop.ts take precedence over .tsx/.ts
- Wrap fixture setup() in try/finally so emulateMedia cleanup always runs
- Bind static file server to 127.0.0.1 and sandbox request paths to buildDir

* Fix screenshot summary to show done/total instead of total

* Address more Copilot PR feedback

- Treat flaky-but-passing Playwright tests as passed
- Escape label/name in HTML alt attributes
- Remove unnecessary ?? fallback on non-nullable projectName

* Address more Copilot PR feedback

- Clear storybook output dir before each run to remove stale screenshots
- Include skipped tests as passing in electron report
- Fix storybook label path separator replacement to be global
* Speed up devices list by using local cache for lastUsedTime

DeviceHistoryList was calling SyncSecretsForce on every load, triggering
a blocking network request to key/fetch_private that took 0.8–4s.

Now reads lastUsedTime from local LevelDB cache (sub-millisecond), with
a background goroutine that force-syncs after the response is returned.
When the background sync completes, a keyfamilyChanged notification fires
so the devices screen auto-refreshes with fresh data — no user action needed.

* Address PR feedback: fix misleading comment and retry on sync failure

- Fix comment in device_history.go: cache fallback is "out-of-date" not "expired"
- Reset lastDeviceSync on background sync failure so next UI refresh retries immediately instead of waiting out the TTL

* Address PR feedback: consistent unknown lastUsed UX, targeted notify

- device-page.tsx: show "Last used unknown" in timeline when lastUsed is
  missing (non-revoked device), matching list row behavior
- device.go: call NotifyRouter.HandleKeyfamilyChanged directly instead of
  KeyfamilyChanged to avoid unnecessary BustLocalUserCache + HandleUserChanged
  on background sync completion

* Address PR feedback: uid-filter keyfamilyChanged, fix UID source, clarify comment

- Filter keyfamilyChanged listener to current user's uid to avoid
  unnecessary DeviceHistoryList RPCs when other users' caches bust
- Use mctx.ActiveDevice().UID() in backgroundSyncDevices instead of
  h.G().Env.GetUID() for correct/robust uid source
- Fix misleading "empty or out-of-date" comment in getLastUsedTimes;
  forced sync only triggers on empty cache (zero devices)

* Address PR feedback: use defer for mutex unlock

* Address PR feedback: remove unnecessary closure around mutex

* Add NotifyDeviceHistory notification; replace keyfamilyChanged with deviceHistoryChanged for device list refresh

* Wire up deviceHistoryChanged notification on TS side

* Address PR feedback: merge redundant shouldSync conditionals

* Fix stale comment referencing keyfamilyChanged

* Revert keyfamily: true; subscribe to devicehistory instead

* Remove keyfamilyChanged RPC wiring added by this PR; not needed

* Regenerate protocol files via make build; add deviceHistoryChanged to enabled-calls.json

* rerun gen

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* format

* regen

* Fix TestDeviceHistoryBasic: force sync when active device missing from cache

During signup, SyncSecrets populates the LevelDB cache with only the
desktop device before the paper key is provisioned. SyncSecretsFromCache
then finds a valid cache (desktop has non-zero LastUsedTime), skips the
network sync, and the paper device ends up absent from lastUsedTimes,
leaving LastUsedTime = 0.

Fix: after building lastUsedTimes from cache, scan non-revoked devices.
If any is missing, call SyncSecretsForce once to get fresh data covering
all provisioned devices.

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix(crypto): left-align signed-by sender strip

* fix(tabs): center tab label within its full-width container

* unify reporting
…9281)

ReloginContainer wired useRequestAutoInvite()'s raw fn directly as
onSignup, so the click event became the `username` arg, flowed through
navigateAppend as a route param, and crashed signup's username.trim().

Make the hook param required (username: string) so a bare assignment to
a () => void handler prop is now a compile error, preventing the event
from being laundered into a string-typed slot. No-username callers pass
'' explicitly.
Teams tab was blank for new users — no create/join buttons, no lone
wolf message. Kb.List returned null when items.length === 0, silently
discarding ListHeaderComponent and ListFooterComponent.
* refactor(chat): add makeMessageWrapper factory to cut wrapper boilerplate

Collapse 14 near-identical message-row wrapper.tsx files (~19 lines each)
into ~7-line makeMessageWrapper calls. Factory captures the
useWrapperMessageWithMessage hook, type guard, and WrapperMessage wrapping;
each render callback keeps its own lazy require('./container') so the child
stays code-split. system-sbs-resolve and placeholder stay hand-written
(extra hooks). Net -162 lines.

* refactor(chat): merge selectable row containers into their views

selectable-small-team-container and selectable-big-team-channel-container
were redux-era passthrough wrappers (call hook, build props, render view).
Fold the useInboxRowSmall/Big hook, getRowStyles, participant sort, and
name-split logic into selectable-small-team and selectable-big-team-channel
directly; delete the container files; point inbox-search at the views.
Also drop the dead subColor branch from getRowStyles (never consumed).

* refactor(chat): fold system message containers into their wrappers

For 9 system message types with no external importers, inline the
container.tsx component directly into wrapper.tsx (one file per type) and
drop the require('./container') + typeof-cast indirection now that
makeMessageWrapper handles the boilerplate. Trades the per-type lazy chunk
for fewer files. Left separate: system-joined, system-users-added-to-conv,
system-old-profile-reset-notice, system-sbs-resolve (cross-imported), and
system-change-avatar (index-based).

* tooling(chat): add analyze-styles script for style-helper gap detection

AST-based scanner (extract + analyze) that finds inline style objects which
could use Kb.Styles helpers (border, padding, size, textEllipsis, dividers)
and surfaces new-helper candidate clusters. Backs the keybase-style-analysis
skill. Run from shared/: node scripts/analyze-styles.mts extract|analyze.

* refactor(chat): move layout props from style objects to Box2 props

users suggestor: alignItems:'center' style + justifyContent='center' prop ->
centerChildren on the special-mention icon box.
selectable-small-team: flexShrink:0 -> noShrink prop on the badge box.

These are the only two movable-prop sites in chat; an AST scan confirms the
rest of the chat tree already expresses layout via Box2 props.

* skill(merge-passthrough-containers): detect+fold redux-era pass-through containers

AST detector finds default-exported components that only call hooks, build a
props object, and render ONE self-closing sibling view (no children of their
own). Reports view-reuse and cross-feature signals so shared/adapter views are
flagged SKIP, leaving genuine 1:1 container/view pairs to merge. SKILL.md
documents the verify-then-merge process. Run from shared/:
node ../.claude/skills/merge-passthrough-containers/scripts/find-passthrough-containers.mts <dir>

* refactor: fold pass-through containers into their views

mention, tlf-info-line, relogin containers each just called hooks,
built props, and rendered one sibling view. Move that logic into the
view, delete the container, repoint importers.

* fix: address PR feedback on wrapper child check and report script escaping

- makeMessageWrapper: distinguish empty ReactNode (0, '') from null/undefined/false
- analyze-styles buildHtml: escape < in embedded JSON to prevent </script> breakout

* chore: remove stale wrapper comment

* fix(e2e-report): make 'Pixel diff only' filter actually filter

Card data-has-diff was set whenever a baseline comparison ran, even
when zero pixels changed, so every card matched and the toggle did
nothing. Tag the attribute only when changed > 0 while keeping the
checkbox visible whenever baselines exist.

* fix: address PR feedback on relogin effect, sort, and skill doc path

- relogin: update prevErrorRef inside effect so empty->non-empty error
  transition detection works after the first render
- selectable-small-team: clone before sort, short-circuit empty filter,
  use transitive comparator
- merge-passthrough-containers script: correct run path to ../.claude

* fix(desktop): dim modal backdrop over inbox header

React Navigation gives each screen header zIndex:1, so the inbox
search + new-chat header painted above the modal backdrop. Give the
backdrop z-index:2 so it dims the header too.

* fix(desktop): allow frame-src for localhost PDF rendering

Chromium renders <embed> PDFs via an internal viewer frame governed by
frame-src, so frame-src 'none' blocked KBFS-served PDFs with
ERR_BLOCKED_BY_CSP. Restore frame-src http://127.0.0.1:*.
- electron 42.3.0 -> 42.3.3 (+ checksums)
- react-native-reanimated 4.4.0 -> 4.4.1 (drop obsolete patch)
- react-native-keyboard-controller 1.21.8 -> 1.21.9
- @callstack/liquid-glass 0.7.1 -> 0.8.0
- date-fns 4.3.0 -> 4.4.0
- storybook 10.4.1 -> 10.4.2
- eslint 10.4.0 -> 10.4.1, typescript-eslint 8.60.0 -> 8.60.1
- @typescript/native-preview dev bump
)

* fix(mobile): restrict screen rotation to full-screen attachments

Lock all mobile screens to portrait by default; only the chat and files
full-screen attachment previews opt back into rotation via orientation: 'all'.

* fix(mobile): allow full-screen attachment rotation on Android too

Drop the iOS-only gate so chat and files full-screen previews get
orientation: 'all' on both platforms; presentation: 'transparentModal'
stays iOS-only.
* fix(ios): bundle device Debug via Expo CLI to avoid packed source-map crash

RN community bundle CLI lacks Expo metro-config's packed-source-map
unwrap patch, so a device Debug build (bundles with --sourcemap-output)
threw 'Unexpected module with full source map found' on the require.js
polyfill. Point CLI_PATH/BUNDLE_COMMAND at @expo/cli export:embed.

Use a plain path rather than 'node --print require.resolve' so it works
when Xcode's stripped PATH leaves NODE_BINARY empty at source time.

* fix(chat): mark live messages read in open thread on phones

On phones, chatConversation lives in the root stack as a sibling of the
tab navigator (above the tab bar), so it sits at routes[1+] alongside
real modals. getVisiblePath treated everything at routes[1+] as a modal,
so getVisibleScreen(includeModals=false) — used by getSelectedConversation
— dropped chatConversation and returned noConversationIDKey. That made
isUserActivelyLookingAtThisThread always false on phones, so incoming
messages received while viewing a thread were added with markAsRead:false
and showed as unread after navigating back. Initial mark-read worked
because it uses the loadMoreMessages path, not isUserActivelyLooking.

Distinguish real modals from genuinely-visible pushed root screens via a
config-derived set instead of a hand-maintained allow-list. A serialized
NavigationState route doesn't carry its presentation, so modal-ness can't
be detected structurally; modalRoutes is the single source of truth.
router-v2 registers it at startup via setModalRouteNames.

* fix(chat): seed inbox row participants in activity order

Small-team inbox rows seeded their participant list from the canonical
TLF name (alphabetical), then got overwritten by the participant fetch
in activity order, causing a visible reshuffle on startup. Seed from the
cached WriterNames (activity order) instead, intersected with the
TLF-name writers to exclude bots, dropping self except in self-convs, so
the seed matches what the frontend settles on and the row stays put.

* fix(chat): collapse iOS thread header actions into overflow menu

Replace the two separate native header buttons (search + info) with a
single ellipsis overflow menu so the thread header reclaims room under
the new styling.

* fix(chat): address PR feedback on inbox row seeding

Extract TLF writers from the writer portion only (before #readers and
extension suffix) when seeding ad-hoc inbox row names; reset modal route
names after each router-visible test to avoid worker-level leakage.

* fix(chat): require modal route registration; filter resolved PR comments

Throw from isRootModalRoute if setModalRouteNames was never called instead
of silently treating all root-stack modals as non-modals. Point the test
nav state index at the topmost route. Switch the Copilot feedback script to
GraphQL review threads so resolved/outdated comments are filtered out.

* fix(chat): enable iOS 26 tab bar minimize on all tabs

tabBarMinimizeBehavior was force-disabled by a top-level 'none' override
and was never set on liquid glass. Enable it for iOS.

List-based tab roots (inbox, teams, files) wrap their scroll view in a
styled Box2; RN view-flattening hoists the scroll view to a sibling so
iOS 26 can't find the tab's content scroll view. Add collapsable={false}
on those wrappers. The fs Errs container also always rendered an empty
Box2 as the first child, dead-ending scroll-view detection — return null
when there are no errors.

Minimize still requires enough scrollable content to trigger (short lists
like People/More/fs-recents won't minimize), which is expected behavior.
* fix(android): guard iOS-only expo-image configureCache

configureCache is iOS-only native in expo-image 56; unguarded module-load
call threw on Android, aborting registerComponent and crashing startup.

* fix(android): apply bottom safe-area inset to pushed stack screens

Android targets SDK 35+ which enforces edge-to-edge, so pushed stack
screens drew content under the system nav bar. Mirror TabScreenWrapper
and wrap StackScreenWrapper in RNScreensSafeAreaView with a bottom edge
on Android.

* fix(mobile): hide bottom tab bar when drilling into Files folders

The bottom tab bar only hides for screens that live in the app root stack
(above the tabs). Other tabs push distinct routes there; Files was the
exception because tapping a folder re-pushed fsRoot inside the fsTab stack,
keeping the bar visible.

Add fsBrowse (same screen as fsRoot, but not a tab root) so on phones it
lands in the root stack and hides the tab bar on push, matching every other
tab. Route within-Files folder navigation (folder taps, parent nav, conflict
/reset banners, destination-picker) through fsBrowse; leave outside-Files
entries (navToPath, deeplinks, linking) on fsRoot so they still land in the
Files tab. public-reminder dismiss now targets the current route so its
replace collapses to setParams instead of a cross-stack replace; daemon
treats fsBrowse as an fs screen.

* chore(android): bump deps, fix gradle warnings, fix android:debug launch

- bump work-runtime 2.11.1->2.11.2, firebase-messaging 25.0.1->25.0.2,
  msgpack-core 0.9.10->0.9.12
- fix Gradle 9->10 space-assignment deprecations in our build.gradle files
  (ndkVersion/namespace/multiDexEnabled/signingConfig/maven url)
- android:debug now builds+installs+launches MainActivity directly via a
  script instead of expo run:android, avoiding the broken
  keybase://expo-development-client deep link (no expo-dev-client bundled)
- legendapp/list, react-navigation alphas, expo patches, electron-packager,
  keyboard-controller, typescript native-preview, @types/react
- fix useRoute/NavigationContainer typing for nav alpha bump
- update-dependencies skill: always bump @types/react + sync resolutions
* fix(crypto): center copy toast text and fix Android off-center

- Add textAlign center to crypto copy-to-clipboard toast text so wrapped
  lines center inside the circular toast (iOS + Android)
- Remove KeyboardAvoidingView2 from native Toast: behavior='height' on
  Android collapsed the container, anchoring the fillAbsolute overlay to
  the trigger button instead of the screen. A pointerEvents='none' toast
  has no input, so keyboard avoidance was unnecessary.

* fix done button in crypto

* feat(modals): Done-right for info/viewer modals

Add doneModalOptions helper + HeaderRightButton: info-only/viewer/live-apply
modals now dismiss with a right-side Done (iOS convention) instead of left
Cancel. Move crypto output modals' Done from left to right. Flip chatInfoPanel,
chatSearchBots, chatLocationPreview, chatUnfurlMapPopup, settingsContactsJoined,
profileImport, profileProveWebsiteChoice. Narrow HeaderLeftButton mode to
back|cancel.

* fix(settings): left-align proxy type radio buttons on mobile

RadioButton renders a ClickableBox without fullWidth, defaulting to
alignSelf: center, so each proxy type radio centered itself. Pin
alignSelf to flex-start.

* fix(common-adapters): left-align RadioButton on mobile

ClickableBox defaults to alignSelf: center without fullWidth, centering
each radio. Pin container alignSelf to flex-start so radios left-align
everywhere (proxy settings, link previews, etc). Drop the per-instance
override added to proxy settings.

* fix(common-adapters): tighten doneModalOptions return type

GetOptionsRet is a union including undefined; the function always returns
an object, so spread call sites (chat/profile routes) had a possibly-undefined
spread. Narrow to NonNullable<GetOptionsRet>.
@chrisnojima chrisnojima changed the title WIP: Version 670 - clean2 Version 670 - clean2 Jun 8, 2026
@chrisnojima chrisnojima merged commit 1943197 into master Jun 8, 2026
@chrisnojima chrisnojima deleted the nojima/HOTPOT-next-670-clean-2 branch June 8, 2026 16:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants