diff --git a/messages/en.json b/messages/en.json index 1eede62..69d1b8c 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1189,6 +1189,14 @@ "replaceImage": "Replace image", "uploadImage": "Upload image" }, + "componentsStudioSidebarBlockFieldForm": { + "panelTitle": "Edit Scene", + "emptyState": "This scene has no editable fields.", + "fieldFallback": "Field {index}", + "textPlaceholder": "Type here…", + "replaceImage": "Replace image", + "uploadImage": "Upload image" + }, "componentsStudioSidebarTransitionsSidebarContent": { "heading": "Transitions", "randomTransition": "Random Transition", diff --git a/messages/fa.json b/messages/fa.json index 0abf193..82f3041 100644 --- a/messages/fa.json +++ b/messages/fa.json @@ -1189,6 +1189,14 @@ "replaceImage": "جایگزینی تصویر", "uploadImage": "بارگذاری تصویر" }, + "componentsStudioSidebarBlockFieldForm": { + "panelTitle": "ویرایش صحنه", + "emptyState": "این صحنه فیلد قابل‌ویرایشی ندارد.", + "fieldFallback": "فیلد {index}", + "textPlaceholder": "اینجا بنویسید…", + "replaceImage": "جایگزینی تصویر", + "uploadImage": "بارگذاری تصویر" + }, "componentsStudioSidebarTransitionsSidebarContent": { "heading": "ترانزیشن‌ها", "randomTransition": "ترانزیشن تصادفی", diff --git a/src/components/studio/sidebar/BlockFieldForm.tsx b/src/components/studio/sidebar/BlockFieldForm.tsx new file mode 100644 index 0000000..2fc90f4 --- /dev/null +++ b/src/components/studio/sidebar/BlockFieldForm.tsx @@ -0,0 +1,165 @@ +"use client"; + +import { useRef } from "react"; +import { ImagePlus, Type } from "lucide-react"; +import { useTranslations } from "next-intl"; + +import { getTextProps, mergeLayerProps } from "@/lib/studio-layer-props"; +import { useStudioStore } from "@/lib/studio-store"; + +/** + * Clean per-field content editor for FlexStory (scene-engine) projects. Each + * scene's editable content is already bridged to `c-`-prefixed text/image layers + * carrying the field's Persian label in `layer.name`; this renders one labelled + * input per field and writes back through the SAME updateLayer path that already + * persists to saved_scene_contents — so it's purely a cleaner presentation of the + * existing, working content path (no Konva geometry, no layer chrome). + */ +export function BlockFieldForm() { + const t = useTranslations("auto.componentsStudioSidebarBlockFieldForm"); + const scenes = useStudioStore((s) => s.scenes); + const activeSceneId = useStudioStore((s) => s.activeSceneId); + const updateLayer = useStudioStore((s) => s.updateLayer); + + const activeScene = scenes.find((s) => s.id === activeSceneId); + const fields = (activeScene?.layers ?? []).filter( + (l) => l.type === "text" || l.type === "image" + ); + + return ( +
+
+

+ {t("panelTitle")} +

+ {activeScene && ( +

{activeScene.name}

+ )} +
+ +
+ {fields.length === 0 ? ( +
+ +

{t("emptyState")}

+
+ ) : ( + fields.map((layer, idx) => + layer.type === "image" ? ( + + updateLayer(layer.id, { props: mergeLayerProps(layer.props, { src }) }) + } + /> + ) : ( + + updateLayer(layer.id, { props: mergeLayerProps(layer.props, { text }) }) + } + /> + ) + ) + )} +
+
+ ); +} + +function TextField({ + label, + value, + onChange, + placeholder, +}: { + label: string; + value: string; + onChange: (v: string) => void; + placeholder: string; +}) { + const MAX = 190; + return ( +
+
+ + MAX + ? "text-[10px] tabular-nums text-red-500" + : "text-[10px] tabular-nums text-gray-400" + } + > + {value.length}/{MAX} + +
+