feat(studio+render): wire theme picker → saved_shared_colors → FlexStory render

Closes the theme→render gap: the studio theme picker now actually drives a
FlexStory render's colours. GetFlexStoryProps reads saved_shared_colors by
element_key (accentColor/secondaryColor/backgroundColor/textColor), but the studio
only wrote the theme into scene_data — so the picker never reached the MP4.

- studio-svc: UpdateSharedColorsAsync upserts saved_shared_colors by (project,
  element_key) + PATCH /v1/saved-projects/{id}/shared-colors endpoint +
  UpdateColorsRequest/UpdateColorItem. Mirrors UpdateSceneContentsAsync. (dotnet
  build: 0 errors.)
- gateway already wildcard-routes /v1/saved-projects/*path → studio-svc (no change).
- Next: /api/projects/[id]/colors route → gateway; project-api patchProjectColors
  + themeColorsFromSceneData (maps scene_data sceneAccentColor… → the colorSchema
  keys); performSave best-effort pushes the 4 colours alongside contents.

Chain: theme picker → store → scene_data → performSave → patchProjectColors →
gateway → studio-svc upsert → saved_shared_colors → GetFlexStoryProps → render.
Verified: Next build + dotnet build both clean; theme presets render cohesively
across all 6 (incl. dark Midnight). End-to-end studio→render needs the live stack.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-23 17:04:47 +03:30
parent c1747167f3
commit c0d04fa855
6 changed files with 143 additions and 0 deletions
@@ -60,6 +60,12 @@ public class StudioController(StudioService svc) : ControllerBase
public async Task<IActionResult> UpdateContents(Guid id, [FromBody] UpdateContentsRequest req) =>
Ok(new { updated = await svc.UpdateSceneContentsAsync(id, UserId, req.Items ?? new List<UpdateContentItem>()) });
/// <summary>Update the project's theme colours (accentColor/secondaryColor/
/// backgroundColor/textColor) so the FlexStory render reads the theme picker.</summary>
[HttpPatch("{id:guid}/shared-colors")]
public async Task<IActionResult> UpdateColors(Guid id, [FromBody] UpdateColorsRequest req) =>
Ok(new { updated = await svc.UpdateSharedColorsAsync(id, UserId, req.Items ?? new List<UpdateColorItem>()) });
/// <summary>Internal endpoint: get project for render service (no user-ownership check).</summary>
[HttpGet("{id:guid}/render-payload")]
[Authorize(Roles = "Service")]