Skip to content

Redesign playground AI workflow#84

Open
leoafarias wants to merge 35 commits into
mainfrom
feat/superdeck-wizard
Open

Redesign playground AI workflow#84
leoafarias wants to merge 35 commits into
mainfrom
feat/superdeck-wizard

Conversation

@leoafarias

@leoafarias leoafarias commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Redesigns the playground AI workflows around composed conversation profiles, sessions, and viewmodels instead of inherited GenUI viewmodels.
  • Adds the shared chat/remix conversation shell, deck-edit coordinator, typed catalog item boundary, and Remix preview schema/normalizer split.
  • Includes the supporting AI prompts, example assets, deck-tool plumbing, slide serializer, and SuperDeck asset-cache image resolution changes needed by the branch.

Testing

  • melos run analyze
  • melos run test --no-select
  • Focused playground AI/editor regression tests
  • Clean rerun after fvm flutter clean in packages/superdeck to clear stale shader test artifacts

tilucasoli and others added 30 commits June 1, 2026 23:21
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 `![](slide-x-illustration.png)`,
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
@docs-page

docs-page Bot commented Jun 29, 2026

Copy link
Copy Markdown

To preview the documentation for this pull request, visit the following URL:

docs.page/btwld/superdeck~84

Documentation is deployed and generated using docs.page

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown

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

leoafarias and others added 3 commits June 29, 2026 15:37
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.
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.

2 participants