feat: add discoverable terminal tabs#33
Conversation
|
Warning Review limit reached
More reviews will be available in 2 minutes and 44 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (18)
📝 WalkthroughWalkthroughThis PR adds a configurable terminal tab display mode (Classic tabbed layout vs. Strip overlay mode) with supporting UI components, settings persistence, and auth-gated host selection. It also introduces pure-JVM AES-256-GCM backup encryption as a fallback when native support is unavailable, updates SSH key encoding to Android's Base64 library, and refactors terminal state handling for lifecycle awareness. ChangesTerminal Tab Mode System and Session Updates
Sequence DiagramssequenceDiagram
participant User
participant TerminalScreen
participant TerminalServerPicker
participant AuthService
participant SessionRepo
User->>TerminalScreen: Opens terminal in strip mode
TerminalScreen->>TerminalScreen: Load terminalTabMode from settings
TerminalScreen->>TerminalScreen: Render TerminalTabStrip at top
User->>TerminalScreen: Press 't' key to add connection
TerminalScreen->>TerminalServerPicker: Show picker overlay
TerminalServerPicker->>TerminalServerPicker: Load hosts from database
User->>TerminalServerPicker: Select host
TerminalServerPicker->>TerminalScreen: onHostSelected(TabSpec)
TerminalScreen->>TerminalScreen: Enrich TabSpec with SSH keys
alt requireAuthOnConnect enabled
TerminalScreen->>AuthService: requireUserVerification
AuthService->>TerminalScreen: VerificationResult
end
TerminalScreen->>SessionRepo: openTab(TabSpec)
SessionRepo->>TerminalScreen: Return TabSession
TerminalScreen->>TerminalScreen: Add tab to active list
flowchart TD
A["encrypt/decrypt called"] --> B{"Native bridge<br/>loaded?"}
B -->|Yes| C["Attempt native<br/>encryption"]
C --> D{"UnsatisfiedLinkError<br/>or unavailable?"}
D -->|No| E["Return native result"]
D -->|Yes| F["Fall through"]
B -->|No| F
F --> G["encryptWithJvm or<br/>decryptWithJvm"]
G --> H{"Container format<br/>valid?"}
H -->|Yes, decrypt| I["Derive AES key<br/>HMAC-SHA1-KDF"]
I --> J["AES-256-GCM<br/>decrypt with AAD"]
J --> K{"AEAD tag<br/>valid?"}
K -->|Yes| L["Return plaintext"]
K -->|No| M["InvalidBackupPassphraseException"]
H -->|No| N["BackupFormatException"]
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
️✅ There are no secrets present in this pull request anymore.If these secrets were true positive and are still valid, we highly recommend you to revoke them. 🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalScreen.kt (1)
1512-1542: ⚡ Quick winSimplify dispatcher switching: remove redundant coroutine launches.
The code switches to
Dispatchers.Main(line 1528) and then immediately launches a new coroutine onDispatchers.Main(line 1535). Since we're already on the Main dispatcher afterwithContext(Dispatchers.Main), thepickerScope.launch(Dispatchers.Main)wrapper is redundant. Similarly, line 1540 can callopenOrPromptdirectly.♻️ Simplify by calling openOrPrompt directly
val mustVerify = requireAuthOnConnectSetting || hostRequiresAuth if (mustVerify) { withContext(Dispatchers.Main) { requireUserVerification( context = context, title = "Verify to connect", subtitle = "Authenticate to open this server session", ) { result -> if (result == VerificationResult.Success) { - pickerScope.launch(Dispatchers.Main) { openOrPrompt(enrichedSpec) } + openOrPrompt(enrichedSpec) } } } } else { - withContext(Dispatchers.Main) { openOrPrompt(enrichedSpec) } + withContext(Dispatchers.Main) { + openOrPrompt(enrichedSpec) + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalScreen.kt` around lines 1512 - 1542, Inside the Dispatchers.IO block you switch to Dispatchers.Main with withContext, then redundantly launch a new Main coroutine; remove the nested pickerScope.launch(Dispatchers.Main) and call openOrPrompt(enrichedSpec) directly in the requireUserVerification Success callback, and likewise call openOrPrompt(enrichedSpec) directly inside the existing withContext(Dispatchers.Main) else branch; keep the outer pickerScope.launch(Dispatchers.IO), requireUserVerification(...) and withContext(Dispatchers.Main) surrounding code unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@android/app/src/main/java/com/jossephus/chuchu/service/backup/ChuchuBackupCodec.kt`:
- Around line 242-247: In the InvalidBackupPassphraseException fallback, avoid
creating an immutable String via passphrase.concatToString(); instead encode the
CharArray directly to UTF-8 bytes and avoid intermediate String: wrap the
passphrase with CharBuffer.wrap(passphrase), use
StandardCharsets.UTF_8.encode(charBuf) to get a ByteBuffer, copy bb.remaining()
bytes into a new ByteArray (legacyPassphraseBytes) via
bb.get(legacyPassphraseBytes), then call decryptContainer(container,
legacyPassphraseBytes) and finally securely wipe legacyPassphraseBytes.fill(0);
also clear/overwrite the ByteBuffer backing array if present (bb.clear() and if
bb.hasArray() fill bb.array() with zeros) to ensure no leftover plaintext
remains.
---
Nitpick comments:
In
`@android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalScreen.kt`:
- Around line 1512-1542: Inside the Dispatchers.IO block you switch to
Dispatchers.Main with withContext, then redundantly launch a new Main coroutine;
remove the nested pickerScope.launch(Dispatchers.Main) and call
openOrPrompt(enrichedSpec) directly in the requireUserVerification Success
callback, and likewise call openOrPrompt(enrichedSpec) directly inside the
existing withContext(Dispatchers.Main) else branch; keep the outer
pickerScope.launch(Dispatchers.IO), requireUserVerification(...) and
withContext(Dispatchers.Main) surrounding code unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e1e1663a-9799-485a-837a-4ced82083fef
📒 Files selected for processing (18)
android/app/src/main/java/com/jossephus/chuchu/data/repository/SettingsRepository.ktandroid/app/src/main/java/com/jossephus/chuchu/service/backup/ChuchuBackupCodec.ktandroid/app/src/main/java/com/jossephus/chuchu/service/ssh/RsaKeyGenerator.ktandroid/app/src/main/java/com/jossephus/chuchu/service/terminal/TerminalSessionEngine.ktandroid/app/src/main/java/com/jossephus/chuchu/service/terminal/TerminalSessionRepository.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/ApplicationNavController.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Settings/SettingsScreen.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Settings/TerminalAccessorySettings.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/CommandPalette.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/PaletteHint.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalScreen.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalServerPicker.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalTabManager.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalTabMode.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalTabStrip.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalViewModel.ktandroid/app/src/test/java/com/jossephus/chuchu/data/backup/ChuchuBackupCoreTest.ktzig-src/src/bridge/chuchu_backup.zig
💤 Files with no reviewable changes (2)
- android/app/src/main/java/com/jossephus/chuchu/service/terminal/TerminalSessionRepository.kt
- android/app/src/main/java/com/jossephus/chuchu/service/terminal/TerminalSessionEngine.kt
5d92a4a to
33ea210
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (6)
android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalTabManager.kt (3)
190-195: ⚡ Quick winAdd stable keys to LazyColumn items for efficient recomposition.
Using
items(entries.size)without keys can cause unnecessary recompositions and animation issues when tabs are added/removed/reordered. TheTabSession.idprovides a natural stable key.Suggested fix
LazyColumn( state = listState, modifier = Modifier.fillMaxWidth().heightIn(max = 360.dp), ) { - items(entries.size) { idx -> - val tab = entries[idx] + items(entries, key = { it.id }) { tab -> + val idx = entries.indexOf(tab) val isActive = tab.id == activeTabIdAlternatively, if index is needed for focus comparison, use
itemsIndexed:- items(entries.size) { idx -> - val tab = entries[idx] + itemsIndexed(entries, key = { _, tab -> tab.id }) { idx, tab -> val isActive = tab.id == activeTabId🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalTabManager.kt` around lines 190 - 195, The LazyColumn currently uses items(entries.size) which lacks stable keys; update the call to use items(items = entries, key = { it.id }) (or itemsIndexed(entries, key = { _, item -> item.id } if you need the index for focus logic) so each TabSession uses its TabSession.id as the stable key; change the block around LazyColumn/entries/items to pass entries directly and supply the key lambda to avoid unnecessary recompositions and animation glitches.
141-149: 💤 Low valuePageUp/PageDown navigate by single item instead of page-sized jumps.
Key.PageUpandKey.PageDowncurrently move focus by ±1 index, identical to arrow keys. For longer tab lists, users may expect page-sized jumps (e.g., ±5 items or viewport-based).This is minor since the list is capped at 360dp height, limiting visible items.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalTabManager.kt` around lines 141 - 149, Currently Key.PageUp/Key.PageDown are handled exactly like arrow keys; change their branch in TerminalTabManager.kt so that when event.type == KeyEventType.KeyUp and entries.isNotEmpty() you adjust focusedTabIndex by a page jump rather than ±1: compute a pageSize (suggest using a constant like 5 or derive from visible item count) then call onFocusedTabIndexChange((focusedTabIndex - pageSize).mod(entries.size)) for PageUp and onFocusedTabIndexChange((focusedTabIndex + pageSize).mod(entries.size)) for PageDown; preserve the Key.DirectionUp/Down behavior and the modulo wrapping using entries.size.
405-414: 💤 Low valueKeep
statusDotColorconsistent withTerminalTabStrip
TerminalTabManager.statusDotColormapsSessionStatustocolorsexactly the same way asTerminalTabStrip(Connected→success, Connecting/Reconnecting→accent, Disconnected/Error→error). Consider extracting the shared mapping to a common helper to prevent future drift.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalTabManager.kt` around lines 405 - 414, statusDotColor in TerminalTabManager duplicates the SessionStatus→Color mapping used in TerminalTabStrip; extract the shared mapping into a single helper (e.g., mapSessionStatusToColor or SessionStatus.colorForPalette) and have both TerminalTabManager.statusDotColor and TerminalTabStrip call that helper so the Connected/Connecting/Reconnecting/Disconnected/Error cases are defined in one place to prevent drift.android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalScreen.kt (1)
874-901: 💤 Low valueMove/guard the strip “no terminal sessions” empty-state out of Connected/Reconnecting
TerminalSessionRepository.sessionStateis derived fromactiveTab; when_tabsis empty,activeTabbecomesnullandsessionStateswitches toSessionState()(not the active tab’s session state).- That makes
tabMode == TerminalTabMode.Strip && tabs.isEmpty()insideSessionStatus.Connected, SessionStatus.Reconnectingat most a transient timing/recomposition artifact; move this UI to the disconnected/no-active-tab path (or guard it withsessionState.status) to avoid dead/contradictory state rendering.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalScreen.kt` around lines 874 - 901, The empty-state UI block that checks "tabMode == TerminalTabMode.Strip && tabs.isEmpty()" is being rendered inside the Connected/Reconnecting branches and can flash due to TerminalSessionRepository.sessionState being derived from activeTab (which becomes null when _tabs is empty); move this empty-state UI out of the Connected/Reconnecting rendering path into the disconnected/no-active-tab branch (where sessionState.status indicates not connected) or add an explicit guard using sessionState.status (e.g., only show when sessionState.status is Disconnected/NoActiveTab) so the "+ new connection" strip state is only rendered when there truly is no active session, referencing TerminalTabMode.Strip, tabs.isEmpty(), sessionState and activeTab to locate and fix the logic.android/app/src/main/java/com/jossephus/chuchu/service/backup/ChuchuBackupCodec.kt (2)
339-365: 💤 Low valueConsider wiping intermediate key derivation arrays.
The
uandblockarrays hold key derivation intermediates that could theoretically aid key recovery if memory is compromised. While less sensitive than the passphrase itself, wiping them aligns with the defensive approach already applied tokeyBytesandpassphraseBytes.🔒 Suggested improvement
val copySize = minOf(block.size, output.size - generated) block.copyInto(output, destinationOffset = generated, startIndex = 0, endIndex = copySize) generated += copySize blockIndex += 1 + block.fill(0) + u.fill(0) } output🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@android/app/src/main/java/com/jossephus/chuchu/service/backup/ChuchuBackupCodec.kt` around lines 339 - 365, The deriveAesKey function leaves intermediate byte arrays (u and block) in memory; after each block is XORed and copied into output, overwrite those arrays (e.g., call fill(0) on u and block) to zeroize sensitive intermediates before they go out of scope, and also ensure any leftover buffers are cleared in the catch/finally path so u/block cannot remain in memory after deriveAesKey returns.
532-536: 💤 Low value
readSizedBytesis currently unused
readSizedBytesinChuchuBackupCodec.ktis only present as its own definition (no other Kotlin references/call sites found). If it’s not intended for immediate use, remove it; otherwise document the intended future use.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@android/app/src/main/java/com/jossephus/chuchu/service/backup/ChuchuBackupCodec.kt` around lines 532 - 536, The function readSizedBytes is defined but unused; either remove it or document its intended future use and keep it. If removing, delete the readSizedBytes(...) definition from ChuchuBackupCodec.kt and run a project-wide search to ensure no hidden references; if keeping, add a KDoc above readSizedBytes explaining its purpose and expected call sites (e.g., for fixed-length fields), and mark it with `@Suppress`("unused") if you want to avoid linter warnings until it’s referenced.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalScreen.kt`:
- Around line 1512-1542: The biometric callback from requireUserVerification can
execute after the picker/composable has been dismissed, so guard against stale
callbacks by tracking picker lifecycle: introduce a boolean/AtomicBoolean (e.g.,
pickerActive or reuse showServerPicker) set true before launching the
pickerScope coroutine and set false when the picker is closed/unmounted, then in
the requireUserVerification callback check result == VerificationResult.Success
&& pickerActive (or lifecycleOwner.isAtLeast(STARTED)) before calling
openOrPrompt(enrichedSpec); update code around pickerScope,
requireUserVerification, and openOrPrompt to consult that flag rather than
unconditionally invoking openOrPrompt.
---
Nitpick comments:
In
`@android/app/src/main/java/com/jossephus/chuchu/service/backup/ChuchuBackupCodec.kt`:
- Around line 339-365: The deriveAesKey function leaves intermediate byte arrays
(u and block) in memory; after each block is XORed and copied into output,
overwrite those arrays (e.g., call fill(0) on u and block) to zeroize sensitive
intermediates before they go out of scope, and also ensure any leftover buffers
are cleared in the catch/finally path so u/block cannot remain in memory after
deriveAesKey returns.
- Around line 532-536: The function readSizedBytes is defined but unused; either
remove it or document its intended future use and keep it. If removing, delete
the readSizedBytes(...) definition from ChuchuBackupCodec.kt and run a
project-wide search to ensure no hidden references; if keeping, add a KDoc above
readSizedBytes explaining its purpose and expected call sites (e.g., for
fixed-length fields), and mark it with `@Suppress`("unused") if you want to avoid
linter warnings until it’s referenced.
In
`@android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalScreen.kt`:
- Around line 874-901: The empty-state UI block that checks "tabMode ==
TerminalTabMode.Strip && tabs.isEmpty()" is being rendered inside the
Connected/Reconnecting branches and can flash due to
TerminalSessionRepository.sessionState being derived from activeTab (which
becomes null when _tabs is empty); move this empty-state UI out of the
Connected/Reconnecting rendering path into the disconnected/no-active-tab branch
(where sessionState.status indicates not connected) or add an explicit guard
using sessionState.status (e.g., only show when sessionState.status is
Disconnected/NoActiveTab) so the "+ new connection" strip state is only rendered
when there truly is no active session, referencing TerminalTabMode.Strip,
tabs.isEmpty(), sessionState and activeTab to locate and fix the logic.
In
`@android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalTabManager.kt`:
- Around line 190-195: The LazyColumn currently uses items(entries.size) which
lacks stable keys; update the call to use items(items = entries, key = { it.id
}) (or itemsIndexed(entries, key = { _, item -> item.id } if you need the index
for focus logic) so each TabSession uses its TabSession.id as the stable key;
change the block around LazyColumn/entries/items to pass entries directly and
supply the key lambda to avoid unnecessary recompositions and animation
glitches.
- Around line 141-149: Currently Key.PageUp/Key.PageDown are handled exactly
like arrow keys; change their branch in TerminalTabManager.kt so that when
event.type == KeyEventType.KeyUp and entries.isNotEmpty() you adjust
focusedTabIndex by a page jump rather than ±1: compute a pageSize (suggest using
a constant like 5 or derive from visible item count) then call
onFocusedTabIndexChange((focusedTabIndex - pageSize).mod(entries.size)) for
PageUp and onFocusedTabIndexChange((focusedTabIndex +
pageSize).mod(entries.size)) for PageDown; preserve the Key.DirectionUp/Down
behavior and the modulo wrapping using entries.size.
- Around line 405-414: statusDotColor in TerminalTabManager duplicates the
SessionStatus→Color mapping used in TerminalTabStrip; extract the shared mapping
into a single helper (e.g., mapSessionStatusToColor or
SessionStatus.colorForPalette) and have both TerminalTabManager.statusDotColor
and TerminalTabStrip call that helper so the
Connected/Connecting/Reconnecting/Disconnected/Error cases are defined in one
place to prevent drift.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: fc7cab2c-7985-4c66-a2fd-75eb2a3c9614
📒 Files selected for processing (18)
android/app/src/main/java/com/jossephus/chuchu/data/repository/SettingsRepository.ktandroid/app/src/main/java/com/jossephus/chuchu/service/backup/ChuchuBackupCodec.ktandroid/app/src/main/java/com/jossephus/chuchu/service/ssh/RsaKeyGenerator.ktandroid/app/src/main/java/com/jossephus/chuchu/service/terminal/TerminalSessionEngine.ktandroid/app/src/main/java/com/jossephus/chuchu/service/terminal/TerminalSessionRepository.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/ApplicationNavController.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Settings/SettingsScreen.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Settings/TerminalAccessorySettings.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/CommandPalette.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/PaletteHint.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalScreen.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalServerPicker.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalTabManager.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalTabMode.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalTabStrip.ktandroid/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalViewModel.ktandroid/app/src/test/java/com/jossephus/chuchu/data/backup/ChuchuBackupCoreTest.ktzig-src/src/bridge/chuchu_backup.zig
💤 Files with no reviewable changes (1)
- android/app/src/main/java/com/jossephus/chuchu/service/terminal/TerminalSessionRepository.kt
🚧 Files skipped from review as they are similar to previous changes (12)
- android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalViewModel.kt
- zig-src/src/bridge/chuchu_backup.zig
- android/app/src/main/java/com/jossephus/chuchu/ui/screens/Settings/TerminalAccessorySettings.kt
- android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalTabMode.kt
- android/app/src/main/java/com/jossephus/chuchu/service/ssh/RsaKeyGenerator.kt
- android/app/src/main/java/com/jossephus/chuchu/service/terminal/TerminalSessionEngine.kt
- android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalTabStrip.kt
- android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/CommandPalette.kt
- android/app/src/main/java/com/jossephus/chuchu/data/repository/SettingsRepository.kt
- android/app/src/main/java/com/jossephus/chuchu/ui/screens/Settings/SettingsScreen.kt
- android/app/src/main/java/com/jossephus/chuchu/ui/screens/Terminal/TerminalServerPicker.kt
- android/app/src/test/java/com/jossephus/chuchu/data/backup/ChuchuBackupCoreTest.kt
33ea210 to
83a3f31
Compare
Summary
Adds an opt-in discoverable terminal tab strip while keeping the existing classic terminal tab behavior as the default.
What changed:
Implementation notes:
Testing
Validation performed locally:
git diff --checkPATH=/home/salem/.cache/chuchu-tools/zig-x86_64-linux-0.15.2:$PATH ANDROID_NDK_HOME=/home/salem/Android/Sdk/ndk/27.1.12297006 ANDROID_NDK_ROOT=/home/salem/Android/Sdk/ndk/27.1.12297006 make buildcd android && ./gradlew app:compileDebugKotlin app:testDebugUnitTest app:assembleDebug app:lintDebuglib/arm64-v8a/libchuchu_jni.so.User testing:
libchuchu_jni.so;AI disclosure
This PR was implemented by AI at the user's request. The user tested debug APKs on-device and requested that AI involvement be disclosed.
Summary by CodeRabbit
New Features
Bug Fixes / Reliability
Tests