diff --git a/src/pages/editor/components/canvas/ObjectsLayer.tsx b/src/pages/editor/components/canvas/ObjectsLayer.tsx
index 8657f75..e8ef668 100644
--- a/src/pages/editor/components/canvas/ObjectsLayer.tsx
+++ b/src/pages/editor/components/canvas/ObjectsLayer.tsx
@@ -97,25 +97,7 @@ const ObjectsLayer = ({
.filter((obj) => obj.groupId && obj.type !== "group")
.map((obj) => {
const groupObject = objects.find((o) => o.id === obj.groupId);
- // داده قدیمی/ناقص: groupId بدون شیء type:group در صفحه — مثل preview همه را نشان بده
- if (!groupObject) {
- return (
-
- );
- }
+ if (!groupObject) return null;
// Render with absolute position but wrapped in non-listening group
return (
diff --git a/src/pages/editor/store/editorStore.helpers.ts b/src/pages/editor/store/editorStore.helpers.ts
index 38e6db9..f812821 100644
--- a/src/pages/editor/store/editorStore.helpers.ts
+++ b/src/pages/editor/store/editorStore.helpers.ts
@@ -262,3 +262,70 @@ export const ungroupById = (objects: EditorObject[], groupId: string) =>
objects
.map((obj) => (obj.groupId === groupId ? { ...obj, groupId: undefined } : obj))
.filter((obj) => obj.id !== groupId);
+
+/**
+ * اگر اعضا groupId دارند ولی شیء type:group با همان id در آرایه نیست (ذخیره/سنک ناقص)،
+ * همانند buildGroupResult یک wrapper اضافه میکنیم تا ادیتور گروه را یکپارچه جابهجا کند.
+ * اگر فقط یک عضو با آن groupId باشد، groupId حذف میشود (گروه نامعتبر).
+ */
+export const ensureMissingGroupObjects = (objects: EditorObject[]): EditorObject[] => {
+ let next = [...objects];
+
+ const referencedIds = new Set();
+ for (const o of next) {
+ if (o.type !== "group" && o.groupId) {
+ referencedIds.add(o.groupId);
+ }
+ }
+
+ for (const gid of referencedIds) {
+ const members = next.filter((o) => o.groupId === gid && o.type !== "group");
+ if (members.length < 2) {
+ next = next.map((o) =>
+ o.groupId === gid && o.type !== "group" ? { ...o, groupId: undefined } : o,
+ );
+ }
+ }
+
+ const stillReferenced = new Set();
+ for (const o of next) {
+ if (o.type !== "group" && o.groupId) {
+ stillReferenced.add(o.groupId);
+ }
+ }
+
+ const existingGroupIds = new Set(
+ next.filter((o) => o.type === "group").map((o) => o.id),
+ );
+
+ const toAppend: EditorObject[] = [];
+ for (const gid of stillReferenced) {
+ if (existingGroupIds.has(gid)) continue;
+
+ const members = next.filter((o) => o.groupId === gid && o.type !== "group");
+ if (members.length < 2) continue;
+
+ let minX = Infinity;
+ let minY = Infinity;
+ let maxX = -Infinity;
+ let maxY = -Infinity;
+ for (const m of members) {
+ const b = getObjectBounds(m);
+ minX = Math.min(minX, b.minX);
+ minY = Math.min(minY, b.minY);
+ maxX = Math.max(maxX, b.maxX);
+ maxY = Math.max(maxY, b.maxY);
+ }
+
+ toAppend.push({
+ id: gid,
+ type: "group",
+ x: minX,
+ y: minY,
+ width: maxX - minX,
+ height: maxY - minY,
+ });
+ }
+
+ return toAppend.length > 0 ? [...next, ...toAppend] : next;
+};
diff --git a/src/pages/editor/store/editorStore.ts b/src/pages/editor/store/editorStore.ts
index bc67e96..9c46931 100644
--- a/src/pages/editor/store/editorStore.ts
+++ b/src/pages/editor/store/editorStore.ts
@@ -9,6 +9,7 @@ import {
isInvalidPageId,
reorderByIds,
ungroupById,
+ ensureMissingGroupObjects,
} from "./editorStore.helpers";
import type {
EditorObject,
@@ -792,6 +793,7 @@ export const useEditorStore = create((set, get) => {
...page,
id: isInvalidPageId(page.id) ? createScopedId("page") : page.id,
guides: page.guides || [],
+ objects: ensureMissingGroupObjects(page.objects || []),
}));
const firstPage = normalizedPages[0];
set({