feat(studio): copy repeaters, characters/controllers, color presets into editable project
Build backend images / build content-svc (push) Failing after 1m46s
Build backend images / build file-svc (push) Failing after 2m32s
Build backend images / build gateway (push) Failing after 1m18s
Build backend images / build identity-svc (push) Failing after 1m2s
Build backend images / build notification-svc (push) Failing after 2m59s
Build backend images / build render-svc (push) Failing after 6m12s
Build backend images / build studio-svc (push) Failing after 4m14s

Extends CopyTemplateGraphAsync: repeater children flatten into saved_scene_contents
(repeater_item_key/index via repeater_items); scene characters+controllers and color
presets+items copied, correlated by (new scene, original-id/sort) since studio tables
lack original-id columns. studio character.key is a uuid → store original char id.
No regression on templates without these (copy 0 rows). All enum cols cast ::text.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-06 22:14:28 +03:30
parent f8631fbbc4
commit 6ee211fb35
@@ -122,22 +122,23 @@ public class StudioService(StudioDbContext db)
SELECT new_id, old_id FROM ins;",
savedProjectId, originalProjectId);
// 2. content elements (skip repeater children for now)
// 2. content elements — incl. repeater children flattened via repeater_item_key
await db.Database.ExecuteSqlRawAsync(@"
INSERT INTO studio.saved_scene_contents
(saved_scene_id, key, title, localized_title, hint, type, value,
font_face, font_face_name, font_size, default_font_size, default_font_face,
justify, position_in_container, direction_layer_value, is_text_box,
ai_input_type, mapped_list, thumbnail, sort, status, created_at, updated_at)
ai_input_type, mapped_list, thumbnail, sort, repeater_item_key,
repeater_index, status, created_at, updated_at)
SELECT sm.new_id, ce.key, ce.title, ce.localized_title, ce.hint, ce.type::text,
ce.default_value, ce.font_face, ce.font_face_name, ce.font_size,
ce.default_font_size, ce.default_font_face, ce.justify::text,
ce.position_in_container, ce.direction_layer_value, ce.is_text_box,
ce.ai_input_type::text, ce.mapped_list, ce.thumbnail, ce.sort, 'default',
now(), now()
ce.ai_input_type::text, ce.mapped_list, ce.thumbnail, ce.sort,
ri.repeat_item_key, ri.sort, 'default', now(), now()
FROM content.scene_content_elements ce
JOIN _scene_map sm ON sm.old_id = ce.scene_id
WHERE ce.repeater_item_id IS NULL;");
LEFT JOIN content.repeater_items ri ON ri.id = ce.repeater_item_id;");
// 3. colour elements
await db.Database.ExecuteSqlRawAsync(@"
@@ -148,6 +149,41 @@ public class StudioService(StudioDbContext db)
FROM content.scene_color_elements ce
JOIN _scene_map sm ON sm.old_id = ce.scene_id;");
// 3b. characters — studio's `key` is a uuid; store the original character id
await db.Database.ExecuteSqlRawAsync(@"
INSERT INTO studio.saved_scene_characters (saved_scene_id, key, name, icon)
SELECT sm.new_id, ch.id, ch.name, ch.icon
FROM content.scene_characters ch
JOIN _scene_map sm ON sm.old_id = ch.scene_id;");
// 3c. character controllers (correlate the new character by scene + original id)
await db.Database.ExecuteSqlRawAsync(@"
INSERT INTO studio.saved_scene_character_controllers
(saved_scene_character_id, name, key, value, sort)
SELECT sc.id, cc.name, cc.key, cc.default_value, cc.sort
FROM content.scene_character_controllers cc
JOIN content.scene_characters cch ON cch.id = cc.scene_character_id
JOIN _scene_map sm ON sm.old_id = cch.scene_id
JOIN studio.saved_scene_characters sc
ON sc.saved_scene_id = sm.new_id AND sc.key = cch.id;");
// 3d. colour presets
await db.Database.ExecuteSqlRawAsync(@"
INSERT INTO studio.saved_scene_color_presets (saved_scene_id, is_selected, sort)
SELECT sm.new_id, false, cp.sort
FROM content.scene_color_presets cp
JOIN _scene_map sm ON sm.old_id = cp.scene_id;");
// 3e. colour preset items (correlate the new preset by scene + sort)
await db.Database.ExecuteSqlRawAsync(@"
INSERT INTO studio.saved_scene_color_preset_items (preset_id, element_key, value, sort)
SELECT sp.id, ci.element_key, ci.value, ci.sort
FROM content.scene_color_preset_items ci
JOIN content.scene_color_presets cp ON cp.id = ci.preset_id
JOIN _scene_map sm ON sm.old_id = cp.scene_id
JOIN studio.saved_scene_color_presets sp
ON sp.saved_scene_id = sm.new_id AND sp.sort = cp.sort;");
// 4. shared (project-level) colours — frshare frd_* controls
await db.Database.ExecuteSqlRawAsync(@"
INSERT INTO studio.saved_shared_colors