Redesign top bar to support screen-specific dynamic content (move ModelSettingsSelector & tool config to top bar) #135

Closed
opened 2025-12-20 17:19:32 +00:00 by rwachters · 0 comments
rwachters commented 2025-12-20 17:19:32 +00:00 (Migrated from github.com)

Summary

We should make the top app bar in MainApplicationFlow capable of showing screen-specific controls. For example, when the Chat screen is active the top bar should host the ModelSettingsSelector (currently in ChatArea) and the tool configuration button (currently in InputArea). This will make the UI more consistent and keep frequently used session-level controls always visible.

Why

  • Flexibility: allow each screen (Chat, Settings, Admin, etc.) to provide its own top-bar actions without editing the scaffold every time.
  • Visibility: important session controls (model selector, tool config) are easier to reach when visible in the top bar.
  • Future-proofing: new screens can add top-bar content (filters, search, actions) via a small API.

Proposed approach (high level)

  1. Add a top-bar slot in MainApplicationFlow for screen-provided composable content. Two primary options:

    • Mutable state/composable lambda: e.g. MutableState<@Composable RowScope.() -> Unit> stored in MainApplicationFlow and updated by screens.
    • CompositionLocal: provide a CompositionLocal for a @Composable RowScope.() -> Unit that screens can set via CompositionLocalProvider.
  2. Implement a small API for screens to populate the top-bar content. Example:

    • val setTopBarContent = remember { mutableStateOf<(@Composable RowScope.() -> Unit)?>(null) }
    • Pass a setter to each screen (or provide a CompositionLocal) so ChatScreen can set content when active and clear it when leaving.
  3. Create a ChatTopBarContent composable (or similar) that wraps ModelSettingsSelector and the tool config UI (extracted from InputArea).

  4. Remove ModelSettingsSelector from ChatArea.SuccessStateDisplay and the tool config button from InputArea; rely on the top-bar content instead.

  5. Ensure proper lifecycle: set the content when the screen becomes active and clear it when the screen is removed.

Files likely to touch

  • app/src/commonMain/kotlin/eu/torvian/chatbot/app/compose/MainApplicationFlow.kt (add slot and render it in the top bar)
  • app/src/commonMain/kotlin/eu/torvian/chatbot/app/compose/ChatScreen.kt (set/clear top bar content)
  • app/src/commonMain/kotlin/eu/torvian/chatbot/app/compose/chatarea/ChatArea.kt (remove ModelSettingsSelector from SuccessStateDisplay)
  • app/src/commonMain/kotlin/eu/torvian/chatbot/app/compose/chatarea/InputArea.kt (remove tool config button)
  • app/src/commonMain/kotlin/eu/torvian/chatbot/app/compose/chatarea/ModelSettingsSelector.kt (no change to the component—just reuse it in top bar)
  • Optionally add a new file ChatTopBarContent.kt under chatarea to host the extracted top-bar composable.

Implementation notes and choices

  • I recommend using the CompositionLocal approach (CompositionLocalProvider) because it avoids threading a setter through many composables and feels idiomatic for multi-module Compose apps. However, the mutable-state setter is simpler and explicit. Either is fine; include a brief comment explaining the choice.

  • Ensure the top-bar content is a RowScope.() -> Unit to allow the app bar actions to host it naturally. If the content is wider than available space, consider horizontal scrolling or an overflow menu.

Acceptance criteria

  • When Chat route is active, the top app bar displays the ModelSettingsSelector and the tool configuration button.
  • ModelSettingsSelector is removed from the body ChatArea so it isn't duplicated.
  • Tool config button is removed from InputArea and now appears in the top bar.
  • No regressions: app builds and Chat screen functions (selecting model/settings, opening tool config) work the same as before.

Testing steps

  • Build/run the desktop app (quick target):

    • ./gradlew app:desktopMainClasses to compile the desktop app UI code.
    • Optionally run ./gradlew app:desktopTest to run desktop tests.
  • Manual smoke test:

    1. Launch desktop app.
    2. Navigate to Chat screen.
    3. Verify model selector and tool config appear in the top bar and behave identically to previous in-body controls.
    4. Verify other screens (Settings, Admin) show no chat-specific controls.

Suggested labels and assignees

  • labels: enhancement, ui, refactor
  • assignee: rwachters (optional)

Notes / follow-ups

  • Consider adding a small unit/integration test or snapshot test for the top bar content rendering per screen (where applicable).
  • If we prefer a centralized pattern, document the TopBarContent API in docs/.

If you want, I can open a PR that implements the CompositionLocal approach and updates the relevant files.

Summary We should make the top app bar in `MainApplicationFlow` capable of showing screen-specific controls. For example, when the Chat screen is active the top bar should host the `ModelSettingsSelector` (currently in `ChatArea`) and the tool configuration button (currently in `InputArea`). This will make the UI more consistent and keep frequently used session-level controls always visible. Why - Flexibility: allow each screen (Chat, Settings, Admin, etc.) to provide its own top-bar actions without editing the scaffold every time. - Visibility: important session controls (model selector, tool config) are easier to reach when visible in the top bar. - Future-proofing: new screens can add top-bar content (filters, search, actions) via a small API. Proposed approach (high level) 1. Add a top-bar slot in `MainApplicationFlow` for screen-provided composable content. Two primary options: - Mutable state/composable lambda: e.g. MutableState<@Composable RowScope.() -> Unit> stored in `MainApplicationFlow` and updated by screens. - CompositionLocal: provide a `CompositionLocal` for a `@Composable RowScope.() -> Unit` that screens can set via `CompositionLocalProvider`. 2. Implement a small API for screens to populate the top-bar content. Example: - `val setTopBarContent = remember { mutableStateOf<(@Composable RowScope.() -> Unit)?>(null) }` - Pass a setter to each screen (or provide a CompositionLocal) so `ChatScreen` can set content when active and clear it when leaving. 3. Create a `ChatTopBarContent` composable (or similar) that wraps `ModelSettingsSelector` and the tool config UI (extracted from `InputArea`). 4. Remove `ModelSettingsSelector` from `ChatArea.SuccessStateDisplay` and the tool config button from `InputArea`; rely on the top-bar content instead. 5. Ensure proper lifecycle: set the content when the screen becomes active and clear it when the screen is removed. Files likely to touch - app/src/commonMain/kotlin/eu/torvian/chatbot/app/compose/MainApplicationFlow.kt (add slot and render it in the top bar) - app/src/commonMain/kotlin/eu/torvian/chatbot/app/compose/ChatScreen.kt (set/clear top bar content) - app/src/commonMain/kotlin/eu/torvian/chatbot/app/compose/chatarea/ChatArea.kt (remove `ModelSettingsSelector` from SuccessStateDisplay) - app/src/commonMain/kotlin/eu/torvian/chatbot/app/compose/chatarea/InputArea.kt (remove tool config button) - app/src/commonMain/kotlin/eu/torvian/chatbot/app/compose/chatarea/ModelSettingsSelector.kt (no change to the component—just reuse it in top bar) - Optionally add a new file `ChatTopBarContent.kt` under `chatarea` to host the extracted top-bar composable. Implementation notes and choices - I recommend using the CompositionLocal approach (`CompositionLocalProvider`) because it avoids threading a setter through many composables and feels idiomatic for multi-module Compose apps. However, the mutable-state setter is simpler and explicit. Either is fine; include a brief comment explaining the choice. - Ensure the top-bar content is a `RowScope.() -> Unit` to allow the app bar `actions` to host it naturally. If the content is wider than available space, consider horizontal scrolling or an overflow menu. Acceptance criteria - When Chat route is active, the top app bar displays the `ModelSettingsSelector` and the tool configuration button. - `ModelSettingsSelector` is removed from the body `ChatArea` so it isn't duplicated. - Tool config button is removed from `InputArea` and now appears in the top bar. - No regressions: app builds and Chat screen functions (selecting model/settings, opening tool config) work the same as before. Testing steps - Build/run the desktop app (quick target): - `./gradlew app:desktopMainClasses` to compile the desktop app UI code. - Optionally run `./gradlew app:desktopTest` to run desktop tests. - Manual smoke test: 1. Launch desktop app. 2. Navigate to Chat screen. 3. Verify model selector and tool config appear in the top bar and behave identically to previous in-body controls. 4. Verify other screens (Settings, Admin) show no chat-specific controls. Suggested labels and assignees - labels: `enhancement`, `ui`, `refactor` - assignee: `rwachters` (optional) Notes / follow-ups - Consider adding a small unit/integration test or snapshot test for the top bar content rendering per screen (where applicable). - If we prefer a centralized pattern, document the `TopBarContent` API in `docs/`. If you want, I can open a PR that implements the CompositionLocal approach and updates the relevant files.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Torvian.eu/chatbot#135
No description provided.