Redesign playground AI workflow#84
Open
leoafarias wants to merge 35 commits into
Open
Conversation
Framework changes that let SuperDeck slides be hosted inside a custom app (e.g. an external editor) instead of going through the CLI build pipeline. superdeck: - SlideParts.header/footer are now nullable so embedded hosts can omit chrome - SlideRenderView uses MixScope.inherit and SlideCaptureService re-applies SD color tokens so captures resolve theme tokens identically to live slides - DeckPresentationState/ThumbnailService expose deleteAllThumbnails for editor flows that need to invalidate the cache - Hero shuttle now re-applies the source slide's DefaultTextStyle so text doesn't resize at handoff builder: re-export comment/markdown/section parsers for downstream apps. demo/superdeck: bump google_fonts to ^8.1.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Keep `header`/`footer` nullable so embedded hosts can omit chrome by passing `null`, but default them back to `HeaderPart()`/`FooterPart()` so existing decks relying on default parts keep their chrome instead of silently losing it.
Squash-merge of #77 into feat/playground-framework-support (Option A). Does not touch main.
Foundation (Phase 0) for migrating superdeck_ai into the playground. - Bump ack 1.0.0-beta.9 -> 1.0.0-beta.12 (core pubspec + melos bootstrap) - Fix ack breaking changes in core: strictParsing() removed (primitives now strict by default); AckSchema is now AckSchema<Boundary, Runtime> - Raise Flutter floor to >=3.41.0 across the workspace - Add genui / Gemini / ack-satellite deps + build_runner to playground - Bundle AI prompt + few-shot example assets under assets/ai_* - Gitignore .env; drop unnecessary material import in customization_sidebar
Phase 1: the inverse of the parse pipeline (MarkdownParser / SectionParser / BlockParser / CommentParser). Renders SlideOptions frontmatter, @section / @block / @widget directives with block-style YAML options, escapes @-leading content lines, and avoids duplicating embedded comments. Round-trips demo/slides.md and all three AI example decks (15 tests). This is what lets AI-generated slides land in the markdown editor as editable text.
Phase 2: mechanical port of the superdeck_ai library (96 files) into packages/playground/lib/features/ai, preserving the source tree. - Rewrite package:superdeck_ai/* imports to package:playground/features/ai/* - package:superdeck_core/* and package:superdeck/* now resolve to the monorepo path deps (no API divergence found against ack beta.12 core) - Add go_router + path + path_provider direct deps so the ported code compiles - Regenerate ack_generator codegen (11 *.g.dart outputs) The engine compiles cleanly (analyze green, existing tests pass) but is not yet wired into the app. dart:io stays for now (analyze-safe; macOS works); the in-memory / web-safe refactor + generator rewrite land in Phase 3.
Phase 3: refactor the ported engine to run fully in-memory and integrate it.
Generator:
- DeckGenerationResult now carries List<Slide> + Map<String,Uint8List> images
+ style instead of a disk path; drop kIsWeb guard so generation runs on web
- _finalizeDeck returns parsed slides (no DeckDocumentStore / disk write)
- Image phase keeps bytes in-memory keyed by bare asset filename (no File I/O)
- regenerateFromLastPrompt uses in-memory last-request state (no disk reads)
Web-safety:
- debug_logger -> debugPrint/logging (no dart:io)
- deck_style_service -> in-memory signal holder
- chat_viewmodel -> in-memory last-params (no metadata file)
- path_service -> conditional io/web stubs
- Fix asset path constants to assets/ai_{prompts,examples}
Integration:
- New AiStore orchestrates generate -> cache images -> serialize slides ->
editor + MemoryDeckLoader -> applyFromAiStyle
- New TextEditorController + editor document injection
- DeckCustomizationStore.applyFromAiStyle (DeckStyleType -> signals)
- Lift MemoryAssetCacheStore to a shared provider; register AiStore +
TextEditorController in main.dart
flutter analyze green, tests pass, `flutter build web` succeeds.
Phase 4 (core): a sparkles button in the customization toolbar opens a modal prompt panel wired to AiStore. Typing a prompt and pressing Generate runs the in-memory pipeline; generated slides serialize into the markdown editor + live preview, with reactive progress and error states. Verified: analyze green, tests pass, `flutter build web` succeeds (the now- reachable AI path is web-safe). The full GenUI 8-step wizard + Remix builder screens are wired next.
Phase 4 (full): make the 8-step GenUI wizard and the Remix component builder reachable and route their generation through AiStore. - Add /ai/wizard (ChatScreen) and /ai/remix (RemixScreen) routes in main.dart - Toolbar: sparkles -> quick prompt panel, wand-stars -> full wizard, cube-box -> Remix builder - Rewire summary_card (final wizard step): build prompt via buildPromptFromWizardContext, read AiStore from the provider tree, and call generate(prompt, imageStyleId, backgroundColor); show AiProgressScreen - Replace go_router navigation in the ported screens with Navigator - New in-memory AiProgressScreen backed by AiStore signals analyze green, tests pass, `flutter build web` succeeds (wizard path web-safe).
Phase 5: make the AI slide-editing tools web-safe and in-memory. - Add DeckStore interface + DeckDocument value (deck_store.dart) - Add InMemoryDeckStore: reads DeckController slides, writes by serializing to markdown -> MemoryDeckLoader (no disk) - DeckToolsService depends on DeckStore (not the disk store); no reachable dart:io - creating screen reads in-memory loadedSlides instead of disk - DeckToolsAdapter scaffold maps tool ops to genui DynamicAiTool (Ack schemas); wiring into the conversation is left as a TODO(phase5) analyze green, tests pass, `flutter build web` succeeds.
Phase 6: bring over the superdeck_ai tests, adapted to the in-memory APIs. - 37 test files under test/features/ai (catalog/schemas/prompts/services/ tools/utils + chat/remix/presentation viewmodels) - Adapt DeckGenerationResult assertions (slides/slideCount/images, not path) - Inject DeckStore/InMemoryDeckStore instead of the disk store; drop disk-only PathService setup - Preserve loadForTest / RetryPolicy(delayFn) / GenUiConversationBuilder seams - Drop deck_document_store (disk) + two widget tests needing dotenv injection 453 tests pass (448 ported + 5 existing editor); analyze green.
Phase 7: remove dead code and tighten the integration glue. - Remove the unreachable presentation island (router.dart, app_navigator_key, creating_presentation_screen, presentation_deck_host, presentation_viewmodel + its test) — superseded by direct routes + AiProgressScreen - Drop the go_router dependency (no longer referenced) - Relative imports for the new glue files; tighten applyFromAiStyle, the editor markdown injection, in_memory_deck_store, and remove a redundant cancel check in the generator Remaining dart:io is isolated to the disk-backed DeckStore impl (deck_document_store), an alternative to InMemoryDeckStore, web-isolated. analyze green, 414 tests pass, `flutter build web` succeeds.
Address bugs found in the post-migration review: - Images: inline the data: URIs returned by MemoryAssetCacheStore.write into the serialized markdown (the markdown image renderer doesn't consult the asset cache), so AI-generated images actually render instead of failing to a missing FileImage/AssetImage. - AiStore: ignore re-entrant generate() calls (double-tap) to prevent interleaved writes; bail out with a friendly error on empty slide results. - TextEditorController: switch to ChangeNotifier so a repeated generation with identical markdown still refreshes the editor (ValueNotifier deduped it). - Editor injection: insert-before-delete and never go below one node, so the document never transiently violates SuperEditor's non-empty invariant. analyze green, 414 tests pass.
Apply the duplication + dead-code findings from the post-migration review. Duplication: - Delete copied hash_utils (use core's generateValueHash) - GenerationPhase.label extension replaces the _phaseLabel switch duplicated in the AI panel + progress screen - Unify the unique-slide-key loop in slide_key_utils (was duplicated across deck_tools_service + deck_generator_workflow) - Share AI-style color extraction between style_builder + applyFromAiStyle Dead code / web-safety: - Remove the dead disk layer (deck_document_store, path_service[_io/_web], superdeck_workspace) and the runtime path getters in paths.dart; rewire the deck-tools test onto InMemoryDeckStore. lib/ is now free of dart:io. - Drop unused members: InMemoryDeckStore.updateStyle/currentStyle, DeckStyleService no-op aliases, dead ChatViewModel/DeckGeneratorService regenerate methods, DebugLogger no-op init/dispose - main.dart: _Providers -> StatelessWidget; chat_screen: signal.value = setter analyze green, 406 tests pass, no dart:io in lib, `flutter build web` succeeds.
AI-generated images are cached in-memory under a bare key (`slide-x-illustration.png`) and referenced as ``, but the markdown renderer never consulted the cache (and a prior data-URI workaround was rejected by UriValidator). Now bare-key image refs resolve through the ambient AssetCacheStore, keeping editor markdown clean and working on web + macOS. - New ResolvedAssetImage widget: resolves a bare key via AssetCacheStore (data:/file: URI -> getImageProvider), falls back to the original ref on miss - SlideRenderView: optional assetCacheStore param + InheritedData<AssetCacheStore> wrap (ambient fallback); SuperDeckApp wraps its subtree the same way - ImageElementBuilder + @image widget: resolve bare keys when a store is present, otherwise unchanged (paths with slashes / real URLs keep prior behavior) - Playground passes the shared MemoryAssetCacheStore at the SlideRenderView call sites; AiStore drops the data-URI markdown rewrite (keeps clean bare refs) - New widget test covers cache hit (data: URI), miss (fallback), and no-store superdeck 560 tests, playground 406 tests, analyze clean, web build succeeds.
A `live`-tagged integration test that exercises the real Gemini pipeline (image model + full deck) against GOOGLE_AI_API_KEY. Unlike `flutter test`, the integration binding permits real network. It asserts generated images cache and resolve to data: URIs and that the serialized markdown keeps bare-key refs. Quota / rate-limit / overload responses are treated as inconclusive (markTestSkipped) rather than failures, so it stays a clean diagnostic on a free-tier key. Skips entirely when no key is set; excluded from the default suite (integration_test/ is not run by `flutter test`). Run: fvm flutter test integration_test/live_image_generation_test.dart -d macos Verified live: macOS app builds, network entitlement OK, real calls execute and errors surface correctly. The current key is free-tier (gemini-2.5-pro limit 0; image model rate-limited), so generation needs a billing-enabled key.
gemini-2.5-pro has no free-tier quota (limit 0), so deck generation failed on free-tier keys. Default the final-deck model to gemini-2.5-flash so text generation works without billing; pass GeminiModelNames.gemini25Pro for higher-quality decks on a billing-enabled key. Verified live on macOS with a free-tier key: full pipeline succeeds (outline -> deck), 2 slides + style generated. Images still require a paid key (the image model is rate-limited on free tier) and fail gracefully.
Replace the parallel InheritedData<AssetCacheStore> channel with the existing per-slide render-context object. The store is now carried on SlideConfiguration (next to widgets/style), bound at build time by SlideConfigurationBuilder, and read at render time by the image widgets via InheritedData.maybeOf<SlideConfiguration>(context)?.assetCacheStore. - SlideConfiguration: optional assetCacheStore field (+ ctor/==/hashCode, mapper regen); copyWith preserves it; it's never serialized so no codec concern - DeckController: holds the resolved store, reuses it for ThumbnailService, and passes it into buildConfigurations - image_element_builder + image_widget read the store from SlideConfiguration - Remove the parallel plumbing: SlideRenderView assetCacheStore param + wrap, SuperDeckApp ambient wrap, and the playground call-site passing - Fixes thumbnails for free: the offscreen capture already provides InheritedData<SlideConfiguration>, so cached images now resolve there too - Test injects the store via SlideConfiguration instead of the InheritedData wrap ResolvedAssetImage / isBareAssetKey unchanged. superdeck 560 + playground 406 tests pass, analyze clean, web build succeeds, no InheritedData<AssetCacheStore> remains.
The AI deck-CRUD tools (create/read/update/delete/move slide + update style + readSlide-with-screenshot) were ported and refactored to in-memory during the migration but never wired into the running app — reachable only from tests. Remove the dead subsystem to keep the migration free of dead code; it will be reimplemented in a dedicated PR. Removed (recoverable from git history): - lib/features/ai/core/tools/** (service, adapter, store, in-memory store, mutation helpers, schemas + .g, errors) - lib/features/ai/core/utils/deck_style_service.dart (global signal, only written by the dead service; read by nobody) - lib/features/ai/presentation/thumbnail_preview_service.dart and lib/features/ai/core/superdeck_slide_configurations.dart (only reached via the dead service; the live editor thumbnails use DeckController.presentation.generateThumbnails) - the corresponding tests Added .planning/tool_sub_system.md: full architecture + requirements spec (tool contract, GenUI additionalTools seam, wiring requirements, and the known gaps to fix — store consistency, style plumbing, missing readSlide tool) to drive the future implementation PR. analyze green, 353 tests pass, flutter build web succeeds.
…o-op Post-migration review cleanup: - Remove the unused path_provider dependency (no imports under lib/ after the disk layer was removed) - Pin json_schema_builder dev-dep `any` -> ^0.1.5 (used by the schema-equivalence test; avoid silent major drift) - preview_sidebar: drop the no-op copyWith(style: configuration.style) identity copy -> SlideRenderView(configuration) analyze green, 353 tests pass.
Enable `prefer_relative_imports` in the playground analysis options and convert all intra-package `package:playground/...` imports to relative (per the repo convention). 128 imports across 33 files auto-converted via `dart fix`; the two `export` self-references hand-converted (the lint only fixes imports). Test files keep `package:` imports (the conventional way to reference the package under test). No behavior change. analyze green, 353 tests pass, flutter build web succeeds.
… lifecycle Add catalog data normalizer to coerce AI payload types, strip injected component fields from schemas, and make conversation startup async with request queuing and session epoch guards. Update deck tools, prompts, and tests with shared fake agent client helper.
# Conflicts: # demo/macos/Flutter/GeneratedPluginRegistrant.swift # demo/macos/Podfile.lock # packages/builder/lib/superdeck_builder.dart # packages/playground/analysis_options.yaml # packages/playground/lib/features/editor/customization_sidebar.dart # packages/playground/lib/features/editor/text_editor.dart # packages/playground/lib/features/editor/thumbnail_refresher.dart # packages/playground/lib/main.dart # packages/playground/lib/stores/deck_customization_store.dart # packages/playground/macos/Flutter/GeneratedPluginRegistrant.swift # packages/playground/pubspec.yaml # packages/playground/test/features/editor/thumbnail_refresher_test.dart
|
To preview the documentation for this pull request, visit the following URL:
|
|
Visit the preview URL for this PR (updated for commit 8384826): https://superdeck-dev--pr84-feat-superdeck-wizar-i7pjd4f2.web.app (expires Thu, 30 Jul 2026 14:58:27 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: bd68fc230762285849207e7e120aaf87cd4ca2f9 |
Remove window manager initialization and dependency usage from superdeck, then update the PDF saver to the new file_saver API. Also bump playground super_editor and refresh generated plugin/project files for the affected desktop targets.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Testing
melos run analyzemelos run test --no-selectfvm flutter cleaninpackages/superdeckto clear stale shader test artifacts