Repository navigation
Show a Placeholder While the Assistant Generates an Image - #91
Conversation
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 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 was a problem hiding this comment.
🟡 Changes recommended
There are confirmed build-blocking issues (an extension placed outside its #if canImport(GroveChat) guard and missing UIKit import) plus missing .png.license sidecars for newly referenced DocC screenshots.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR improves the chat and LLM user experience around assistant-generated images by introducing an in-chat “generating image” placeholder that preserves scroll position, enhancing image/file viewing interactions (including keyboard dismissal), and updating DocC documentation screenshots plus supporting tests.
Changes:
- Add a generating-image placeholder flow end-to-end (LLMContext placeholder storage/encoding → GroveChat placeholder UI → completion/removal APIs).
- Improve image viewing and zoom fitting behavior (fade-in reveal, viewer filtering of placeholders, refit-on-bounds-change).
- Refresh documentation + screenshot capture UI tests for chat and LLM onboarding, including a seeded documentation conversation.
File summaries
| File | Description |
|---|---|
| Tests/GroveLLMTests/UITests/TestAppUITests/DocumentationScreenshots.swift | Adds UI-walk to capture OpenAI onboarding screenshots for DocC. |
| Tests/GroveLLMTests/UITests/TestApp/LLMLocal/Onboarding/LLMLocalOnboardingWelcomeView.swift | Switches onboarding welcome to PageView. |
| Tests/GroveLLMTests/LLMContextImageTests.swift | Adds unit tests for announced/completed/removed generated images. |
| Tests/GroveChatTests/ZoomableImageViewTests.swift | Adds test ensuring refit on bounds change without image inflation. |
| Tests/GroveChatTests/UITests/TestAppUITests/DocumentationScreenshots.swift | Adds UI-walk to capture chat documentation screenshots. |
| Tests/GroveChatTests/UITests/TestApp/DocumentationConversation.swift | Adds seeded “Health Assistant” conversation and rendered chart images for docs. |
| Tests/GroveChatTests/UITests/TestApp/ChatTestView.swift | Adds documentation-mode responses + placeholder-to-image replacement behavior. |
| Tests/GroveChatTests/ChatEntityImageCodingTests.swift | Adds Codable round-trip tests for .generating and URL images. |
| Sources/GroveLLM/Models/LLMContextEntity.swift | Introduces _ImageContent.generating and isGenerating helper. |
| Sources/GroveLLM/Models/LLMContext.swift | Adds complete(assistantImage:) and removeGeneratingImages(for:). |
| Sources/GroveLLM/Helpers/LLMContext+Chat.swift | Maps generating images into GroveChat as .generating placeholders. |
| Sources/GroveLLM/GroveLLM.docc/GroveLLM.md | Documents generated-image placeholder behavior and adds screenshot. |
| Sources/GroveChat/Resources/Localizable.xcstrings | Updates follow-up string and adds “Generating image” localization key. |
| Sources/GroveChat/Models/PlatformImage.swift | Ensures .generating has no PlatformImage representation. |
| Sources/GroveChat/Models/ChatEntity.swift | Adds ChatEntity.Content.Image.generating + Codable support. |
| Sources/GroveChat/Message View/ZoomableImageView.swift | Prevents re-entrant fitting and avoids zoom/frame inflation issues. |
| Sources/GroveChat/Message View/SelectableMessageText.swift | Renames follow-up action and adjusts action prominence (Textual). |
| Sources/GroveChat/Message View/PlainMessageView.swift | Adds placeholder-to-image reveal transitions and blocks tapping placeholders. |
| Sources/GroveChat/Message View/GeneratingImageView.swift | Adds animated dot placeholder view for generating images. |
| Sources/GroveChat/Message View/ChatImageViewer.swift | Filters out generating placeholders and improves share-sheet behavior. |
| Sources/GroveChat/Message View/AttachedFilesView.swift | Dismisses keyboard before presenting Quick Look. |
| Sources/GroveChat/Helpers/ProgressiveBlur.swift | Removes progressive blur helper implementation. |
| Sources/GroveChat/Helpers/Keyboard.swift | Adds reusable keyboard dismissal helper for viewer presentation. |
| Sources/GroveChat/GroveChat.docc/Resources/ChatView~dark.png.license | Removes legacy DocC image license sidecar. |
| Sources/GroveChat/GroveChat.docc/Resources/ChatView+VoiceInput~dark.png.license | Removes legacy DocC image license sidecar. |
| Sources/GroveChat/GroveChat.docc/Resources/ChatView+VoiceInput.png.license | Removes legacy DocC image license sidecar. |
| Sources/GroveChat/GroveChat.docc/Resources/ChatView+TextInput~dark.png.license | Removes legacy DocC image license sidecar. |
| Sources/GroveChat/GroveChat.docc/Resources/ChatView+TextInput.png.license | Removes legacy DocC image license sidecar. |
| Sources/GroveChat/GroveChat.docc/Resources/ChatView.png.license | Removes legacy DocC image license sidecar. |
| Sources/GroveChat/GroveChat.docc/GroveChat.md | Updates overview to new screenshots and documents generated pictures + follow-up. |
| Sources/GroveChat/ChatView+Export.swift | Treats .generating like an image in text export description. |
| Sources/GroveChat/ChatView.swift | Adds DocC inline image to ChatView header docs. |
Review details
- Files reviewed: 32/52 changed files
- Comments generated: 4
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
ffeba82 to
07dfa5b
Compare
07dfa5b to
dad1538
Compare
dad1538 to
993472a
Compare
4910529 to
b4ac639
Compare
b4ac639 to
9342bcc
Compare
9342bcc to
17e0c16
Compare
lukaskollmer
left a comment
There was a problem hiding this comment.
looks good. one small issue (and i'm not sure if this is an issue in Chat, or in LLM, or in the owning app) is that if you have a chat open that is long enough to fill the full available vertical screen space, and it starts generating an image and switches from the regular 3 dot typing indicator to the new image generation thing, it doesnt adjust the scroll position, and the user only sees the top part of the image generation indicator (as much as what the height of the regular typing indicator was, which does properly scroll the chat all the way down...
|
Good idea to adjust the auto-scrolling for image generation @lukaskollmer; feel free to push an update on top of the stack if you have a good idea how to implement this. We have been trying to disable auto-scroll when the messages come as one and don't work with streaming. The Stanford AI API Hub had some issues with that. |
### ♻️ Current situation & Problem Onboarding, account setup, consent and the questionnaire each built their own page: their own title, their own footer, their own idea of what a missing answer looks like. Validation drew a red box inside the grey card it sat on, the questionnaire and consent tinted their cards each in their own way, and a page that had to be read to the end looked nothing like a page with a button. The documentation showed screenshots from different eras, and the walks that regenerate them ran inside the CI suites. ### ⚙️ Release Notes <table><tr><td align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/SchmiedmayerLab/Grove/page-scaffold/Sources/GroveViews/GroveViews.docc/Resources/Welcome~dark.png"><img src="https://raw.githubusercontent.com/SchmiedmayerLab/Grove/page-scaffold/Sources/GroveViews/GroveViews.docc/Resources/Welcome.png" width="200" alt="Page with floating actions"></picture></td><td align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/SchmiedmayerLab/Grove/page-scaffold/Sources/GroveViews/GroveViews.docc/Resources/ImageHeader~dark.png"><img src="https://raw.githubusercontent.com/SchmiedmayerLab/Grove/page-scaffold/Sources/GroveViews/GroveViews.docc/Resources/ImageHeader.png" width="200" alt="Header with a symbol"></picture></td><td align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/SchmiedmayerLab/Grove/page-scaffold/Sources/GroveOnboarding/GroveOnboarding.docc/Resources/ScrolledTitle~dark.png"><img src="https://raw.githubusercontent.com/SchmiedmayerLab/Grove/page-scaffold/Sources/GroveOnboarding/GroveOnboarding.docc/Resources/ScrolledTitle.png" width="200" alt="Title risen into the bar"></picture></td><td align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/SchmiedmayerLab/Grove/page-scaffold/Sources/GroveViews/GroveViews.docc/Resources/Validation~dark.png"><img src="https://raw.githubusercontent.com/SchmiedmayerLab/Grove/page-scaffold/Sources/GroveViews/GroveViews.docc/Resources/Validation.png" width="200" alt="Blocking feedback"></picture></td><td align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/SchmiedmayerLab/Grove/page-scaffold/Sources/GroveLLMOpenAI/GroveLLMOpenAI.docc/Resources/APITokenStep~dark.png"><img src="https://raw.githubusercontent.com/SchmiedmayerLab/Grove/page-scaffold/Sources/GroveLLMOpenAI/GroveLLMOpenAI.docc/Resources/APITokenStep.png" width="200" alt="LLM setup step"></picture></td><td align="center"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/SchmiedmayerLab/Grove/page-scaffold/Sources/GroveViews/GroveViews.docc/Resources/Tiles~dark.png"><img src="https://raw.githubusercontent.com/SchmiedmayerLab/Grove/page-scaffold/Sources/GroveViews/GroveViews.docc/Resources/Tiles.png" width="200" alt="Tiles"></picture></td></tr><tr><td align="center"><sub>Page with floating actions</sub></td><td align="center"><sub>Header with a symbol</sub></td><td align="center"><sub>Title risen into the bar</sub></td><td align="center"><sub>Blocking feedback</sub></td><td align="center"><sub>LLM setup step</sub></td><td align="center"><sub>Tiles</sub></td></tr></table> - GroveViews gets the page scaffold every full-page step uses: `PageView` with a header, scrolling content and a footer that floats over it, `PageHeader` with an optional symbol and a `spacing` option for a header under a navigation bar, and `PageActions` with `ActionButtonRole` and `actionButtonStyle(_:)`. The pieces are modifiers too: `floatingActions`, `fadesIntoBottomEdge`, `risesIntoNavigationBar` with `acceptsRisingTitle` (measured in global coordinates, so it also works on a form that recycles its rows), and `softScrollEdge` for lists under a bar. GroveOnboarding keeps `OnboardingInformationView` and `SequentialOnboardingView`; `OnboardingView` is an alias of `PageView` with the areas initialiser. The LLM setup steps move over here; account, consent and the questionnaire follow in #88, #90 and #89. - One language for something that still needs the participant: `BlockingMessage`, and `reportsBlocking` on the control with `highlightsBlockingContent` on its row or card, so the red is painted in the container's own shape. `VerifiableTextField` uses it here; the questionnaire and consent cards adopt it in #89 and #90. - `Scripts/documentation-screenshots.sh` captures through RocketSim and a `snapshot` directive installs a snapshot-test reference for views only a snapshot renders. The walks skip themselves unless the script runs them. Every catalogue except chat, LLM, account, consent and the questionnaire is regenerated here, with inline images on the public views. - Textual 0.6.4, `ProgressiveBlur` moves from the chat to GroveViews, and lists under a bar get the soft edge. - The questionnaire navigator taps a row only once it is reachable below the bar, and the consent test helper drags a picker back out from under it: with a floating footer both used to tap the bar instead. - `ListHeader` stays until #88 removes its last users, the consent view only picks up the new names, the chat keeps its own blur until #91, and three images the README still shows leave with #90. ### 📚 Documentation GroveViews' page gets a Pages and a Blocking Feedback topic, GroveOnboarding describes onboarding on top of the scaffold, and the Documentation Guide covers the script's directives. ### ✅ Testing UI suites for Views, Onboarding, Chat, LLM, Scheduler, Contact, License, AccessGuard, Devices and HealthKit on an iPhone 17 Pro with iOS 26.5, the iOS and macOS unit legs, the floor build and the documentation build. The chat, LLM, account, consent and questionnaire suites were run on the full stack. ### Code of Conduct & Contributing Guidelines By creating and submitting this pull request, you agree to follow our [Code of Conduct](https://github.com/SchmiedmayerLab/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/SchmiedmayerLab/.github/blob/main/CONTRIBUTING.md): - [x] I agree to follow the [Code of Conduct](https://github.com/SchmiedmayerLab/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/SchmiedmayerLab/.github/blob/main/CONTRIBUTING.md).
♻️ Current situation & Problem
While the assistant drew a picture the chat showed nothing until the image arrived, and the conversation lost its place when it did. Opening a picture or a file viewer left the keyboard standing on iOS, the image viewer did not build on macOS, and the chat and LLM pages had screenshots of an older chat.
⚙️ Release Notes
LLMContext.complete(assistantImage:)fills it in andremoveGeneratingImages(for:)drops what an interaction never delivered. The context encodes the placeholder, so a restored conversation shows it too.📚 Documentation
The chat page gets its images and a walk through the composer; the LLM page documents the placeholder and points at the chat.
✅ Testing
Unit tests for the placeholder's coding and the context's completion, the zoomable view's fitting, and the chat and LLM UI suites on an iPhone 17 Pro with iOS 26.5 plus the macOS unit legs.
Code of Conduct & Contributing Guidelines
By creating and submitting this pull request, you agree to follow our Code of Conduct and Contributing Guidelines: