@tnkrai/tnkr-editor 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -73196,7 +73196,7 @@ function expandGroupsToChildren(parts) {
73196
73196
  return expandedParts;
73197
73197
  }
73198
73198
  // Utility function to sync step parts with latest data from referenced steps
73199
- function syncStepPartsWithLatestData(stepParts, allSteps) {
73199
+ function syncStepPartsWithLatestData(stepParts, allSteps, animationDirections) {
73200
73200
  const updatedParts = [];
73201
73201
  const processedStepIds = new Set(); // Track processed steps to avoid infinite recursion
73202
73202
  console.log('Syncing step parts:', stepParts);
@@ -73229,8 +73229,11 @@ function syncStepPartsWithLatestData(stepParts, allSteps) {
73229
73229
  processPart(currentPart, depth + 1);
73230
73230
  }
73231
73231
  else {
73232
- // Add regular part to the result
73233
- updatedParts.push(currentPart);
73232
+ // Override with animationDirection from MongoDB if provided
73233
+ const cleanedPart = Object.assign(Object.assign({}, currentPart), { metadata: Object.assign(Object.assign({}, currentPart.metadata), {
73234
+ // Use MongoDB animationDirections as single source of truth
73235
+ animationDirection: (animationDirections === null || animationDirections === void 0 ? void 0 : animationDirections[currentPart.id]) || undefined }) });
73236
+ updatedParts.push(cleanedPart);
73234
73237
  }
73235
73238
  });
73236
73239
  }
@@ -73239,8 +73242,11 @@ function syncStepPartsWithLatestData(stepParts, allSteps) {
73239
73242
  }
73240
73243
  }
73241
73244
  else {
73242
- // Keep regular parts as-is
73243
- updatedParts.push(part);
73245
+ // Override with animationDirection from MongoDB if provided
73246
+ const cleanedPart = Object.assign(Object.assign({}, part), { metadata: Object.assign(Object.assign({}, part.metadata), {
73247
+ // Use MongoDB animationDirections as single source of truth
73248
+ animationDirection: (animationDirections === null || animationDirections === void 0 ? void 0 : animationDirections[part.id]) || undefined }) });
73249
+ updatedParts.push(cleanedPart);
73244
73250
  }
73245
73251
  };
73246
73252
  // Process each part in the initial stepParts array
@@ -74120,85 +74126,94 @@ function useModelViewCore(parts = []) {
74120
74126
  return ({
74121
74127
  id: part.id,
74122
74128
  name: part.name,
74129
+ type: part.type,
74123
74130
  direction: (_a = part.metadata) === null || _a === void 0 ? void 0 : _a.animationDirection,
74124
74131
  });
74125
74132
  }),
74126
74133
  });
74127
74134
  }
74128
- // Process each part and handle groups vs transforms differently
74135
+ // Process parts directly - handle groups and transforms
74136
+ const processedTransforms = new Set(); // Track which transforms we've already animated
74129
74137
  partsWithDirections.forEach((part) => {
74130
- var _a, _b;
74138
+ var _a, _b, _c;
74131
74139
  const direction = (_a = part.metadata) === null || _a === void 0 ? void 0 : _a.animationDirection;
74132
- if (part.type === 'transform') {
74133
- // Handle individual transforms
74134
- const transformElement = (_b = containerRef.current) === null || _b === void 0 ? void 0 : _b.querySelector(`Transform[id="${CSS.escape(part.id)}"]`);
74135
- const originalTranslation = originalTransformsRef.current.get(part.id);
74136
- if (transformElement && originalTranslation) {
74137
- let targetPosition;
74138
- if (direction) {
74139
- if (factor === 1) {
74140
- console.log(`📍 Transform ${part.name} using direction: ${direction}`);
74141
- }
74142
- targetPosition = calculateDirectionalExplodedPosition(originalTranslation, direction, factor);
74143
- }
74144
- else {
74145
- if (factor === 1) {
74146
- console.log(`🎲 Transform ${part.name} using fallback explosion`);
74147
- }
74148
- targetPosition = calculateExplodedPosition(originalTranslation, [0, 0, 0], factor, 3, part.id);
74140
+ // Handle groups with all their children moving as a unit
74141
+ if ((part.type === 'group' || ((_b = part.metadata) === null || _b === void 0 ? void 0 : _b.isGroup)) && part.children) {
74142
+ // Calculate ONE offset for the entire group
74143
+ let groupOffset = [0, 0, 0];
74144
+ if (direction) {
74145
+ // Group has a direction - calculate directional offset
74146
+ const tempPos = calculateDirectionalExplodedPosition('0 0 0', direction, factor);
74147
+ groupOffset = tempPos.split(' ').map(Number);
74148
+ console.log(`📦 Group ${part.id} (${part.name}) calculated offset: [${groupOffset.join(', ')}] for direction: ${direction}`);
74149
+ if (factor === 1) {
74150
+ console.log(`📦 Group ${part.id} (${part.name}) moving ${direction} as a unit with offset [${groupOffset.join(', ')}]`);
74149
74151
  }
74150
- transformElement.setAttribute('translation', targetPosition);
74151
74152
  }
74152
74153
  else {
74154
+ // Group without direction - use random explosion but same for all parts
74155
+ const tempPos = calculateExplodedPosition('0 0 0', [0, 0, 0], factor, 3, part.id);
74156
+ groupOffset = tempPos.split(' ').map(Number);
74153
74157
  if (factor === 1) {
74154
- console.warn(`❌ Transform not found: ${part.name} (${part.id})`);
74158
+ console.log(`🎲 Group ${part.id} (${part.name}) exploding randomly as a unit`);
74155
74159
  }
74156
74160
  }
74157
- }
74158
- else if (part.type === 'group' && part.children) {
74159
- // Handle groups - move all child transforms together in the same direction
74160
- if (factor === 1) {
74161
- console.log(`📍 Group ${part.name} with ${part.children.length} children using direction: ${direction || 'fallback'}`);
74162
- }
74163
- // Calculate group offset once using camera-relative movement
74164
- let groupOffset = [0, 0, 0];
74165
- if (direction) {
74166
- // Use the same calculateDirectionalExplodedPosition logic for consistency
74167
- const tempPos = calculateDirectionalExplodedPosition('0 0 0', direction, factor);
74168
- const offset = tempPos.split(' ').map(Number);
74169
- groupOffset = offset;
74170
- }
74171
- // Apply the same offset to all child transforms in the group
74172
- const collectAndAnimateTransforms = (children) => {
74161
+ // Apply THE SAME offset to ALL transforms in the group
74162
+ const applyGroupOffset = (children, depth = 0) => {
74163
+ console.log(`${' '.repeat(depth)}Applying offset to ${children.length} children`);
74173
74164
  children.forEach((child) => {
74174
74165
  var _a;
74175
- if (child.type === 'transform') {
74166
+ console.log(`${' '.repeat(depth)}Child: ${child.name} (${child.id}), type: ${child.type}`);
74167
+ if (child.type === 'transform' && child.id) {
74176
74168
  const transformElement = (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(`Transform[id="${CSS.escape(child.id)}"]`);
74177
74169
  const originalTranslation = originalTransformsRef.current.get(child.id);
74178
74170
  if (transformElement && originalTranslation) {
74179
74171
  const pos = originalTranslation.split(' ').map(Number);
74180
- let targetPosition;
74181
- if (direction) {
74182
- // Apply group offset to maintain relative positions
74183
- targetPosition = [
74184
- pos[0] + groupOffset[0],
74185
- pos[1] + groupOffset[1],
74186
- pos[2] + groupOffset[2],
74187
- ].join(' ');
74188
- }
74189
- else {
74190
- // Fallback to individual explosion
74191
- targetPosition = calculateExplodedPosition(originalTranslation, [0, 0, 0], factor, 3, child.id);
74192
- }
74172
+ // Apply the group offset to maintain relative positions
74173
+ const targetPosition = [
74174
+ pos[0] + groupOffset[0],
74175
+ pos[1] + groupOffset[1],
74176
+ pos[2] + groupOffset[2],
74177
+ ].join(' ');
74178
+ console.log(`${' '.repeat(depth)}✅ Applying offset [${groupOffset.join(', ')}] to transform ${child.name}: ${originalTranslation} -> ${targetPosition}`);
74193
74179
  transformElement.setAttribute('translation', targetPosition);
74180
+ processedTransforms.add(child.id); // Mark as processed
74181
+ }
74182
+ else {
74183
+ console.log(`${' '.repeat(depth)}❌ Could not find element or translation for ${child.id}`);
74194
74184
  }
74195
74185
  }
74196
- else if (child.children) {
74197
- collectAndAnimateTransforms(child.children);
74186
+ // Recursively process nested children
74187
+ if (child.children) {
74188
+ applyGroupOffset(child.children, depth + 1);
74198
74189
  }
74199
74190
  });
74200
74191
  };
74201
- collectAndAnimateTransforms(part.children);
74192
+ applyGroupOffset(part.children, 0);
74193
+ }
74194
+ else if (part.type === 'transform' && part.id && !processedTransforms.has(part.id)) {
74195
+ // Handle individual transforms (not part of a group we already processed)
74196
+ const transformElement = (_c = containerRef.current) === null || _c === void 0 ? void 0 : _c.querySelector(`Transform[id="${CSS.escape(part.id)}"]`);
74197
+ const originalTranslation = originalTransformsRef.current.get(part.id);
74198
+ if (transformElement && originalTranslation) {
74199
+ let targetPosition;
74200
+ if (direction) {
74201
+ // Individual transform with direction
74202
+ targetPosition = calculateDirectionalExplodedPosition(originalTranslation, direction, factor);
74203
+ if (factor === 1) {
74204
+ console.log(`📍 Transform ${part.name} moving ${direction}`);
74205
+ }
74206
+ }
74207
+ else {
74208
+ // Individual transform without direction - use random explosion
74209
+ targetPosition = calculateExplodedPosition(originalTranslation, [0, 0, 0], factor, 3, part.id);
74210
+ if (factor === 1) {
74211
+ console.log(`🎲 Transform ${part.name} using random explosion`);
74212
+ }
74213
+ }
74214
+ transformElement.setAttribute('translation', targetPosition);
74215
+ processedTransforms.add(part.id);
74216
+ }
74202
74217
  }
74203
74218
  });
74204
74219
  }, [calculateDirectionalExplodedPosition, calculateExplodedPosition]);
@@ -75275,7 +75290,7 @@ handleNext: parentHandleNext, handlePrevious: parentHandlePrevious, onCameraSett
75275
75290
  await new Promise((resolve) => setTimeout(resolve, 100));
75276
75291
  // Recenter the model for optimal rotation around step parts
75277
75292
  console.log('🎯 Starting recentering for automatic step transition...');
75278
- const autoTransitionStepParts = expandGroupsToChildren(syncStepPartsWithLatestData(stepData.selectedStep.parts || [], phases.flatMap((phase) => phase.steps || [])));
75293
+ const autoTransitionStepParts = expandGroupsToChildren(syncStepPartsWithLatestData(stepData.selectedStep.parts || [], phases.flatMap((phase) => phase.steps || []), stepData.selectedStep.animationDirections));
75279
75294
  if (partOperations === null || partOperations === void 0 ? void 0 : partOperations.recenterToShapeIds) {
75280
75295
  recenterForAssemblyStep(partOperations.recenterToShapeIds, autoTransitionStepParts);
75281
75296
  }
@@ -75290,7 +75305,7 @@ handleNext: parentHandleNext, handlePrevious: parentHandlePrevious, onCameraSett
75290
75305
  const rawStepParts = stepData.selectedStep.parts || [];
75291
75306
  // Pass ALL steps from ALL phases for complete recursive syncing
75292
75307
  const allStepsFromAllPhases = phases.flatMap((phase) => phase.steps || []);
75293
- const allStepParts = syncStepPartsWithLatestData(rawStepParts, allStepsFromAllPhases);
75308
+ const allStepParts = syncStepPartsWithLatestData(rawStepParts, allStepsFromAllPhases, stepData.selectedStep.animationDirections);
75294
75309
  // Expand groups to individual transforms for animation processing
75295
75310
  const expandedAllStepParts = expandGroupsToChildren(allStepParts);
75296
75311
  // Separate step-specific parts (tagged with current step ID) from inherited parts
@@ -77015,11 +77030,7 @@ const AssemblyViewMode = ({ phases, htmlContent, className = "", productTitle, p
77015
77030
  if (currentPhase === null || currentPhase === void 0 ? void 0 : currentPhase.id) {
77016
77031
  handlePhaseDescriptionChange(currentPhase.id, content);
77017
77032
  }
77018
- }, onUploadImage: onUploadImage, customExtensionSet: getBasicExtensions(), className: "text-white", minHeight: 20, readOnly: !editMode }, `phase-${currentPhase === null || currentPhase === void 0 ? void 0 : currentPhase.id}`) }), jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [jsxRuntimeExports.jsx("h2", { className: "text-xl font-semibold mb-4 text-white", children: "Steps" }), jsxRuntimeExports.jsx("div", { className: "space-y-4", children: (currentPhase === null || currentPhase === void 0 ? void 0 : currentPhase.steps) && currentPhase.steps.length > 0 ? (currentPhase.steps.map((step, index) => (jsxRuntimeExports.jsx("button", { onClick: () => handleStepClick(index), className: "w-full text-left rounded-2xl px-4 py-3 cursor-pointer border border-[#313133] transition-all", onMouseEnter: (e) => {
77019
- e.currentTarget.style.border = '2px solid white';
77020
- }, onMouseLeave: (e) => {
77021
- e.currentTarget.style.border = '1px solid #313133';
77022
- }, children: jsxRuntimeExports.jsxs("span", { className: "text-white/70", children: ["Step ", index + 1, ": ", step.title] }) }, step.id)))) : (
77033
+ }, onUploadImage: onUploadImage, customExtensionSet: getBasicExtensions(), className: "text-white", minHeight: 20, readOnly: !editMode }, `phase-${currentPhase === null || currentPhase === void 0 ? void 0 : currentPhase.id}`) }), jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [jsxRuntimeExports.jsx("h2", { className: "text-xl font-semibold mb-4 text-white", children: "Steps" }), jsxRuntimeExports.jsx("div", { className: "space-y-4", children: (currentPhase === null || currentPhase === void 0 ? void 0 : currentPhase.steps) && currentPhase.steps.length > 0 ? (currentPhase.steps.map((step, index) => (jsxRuntimeExports.jsx("button", { onClick: () => handleStepClick(index), className: "w-full text-left rounded-2xl px-4 py-3 cursor-pointer border border-[#313133] transition-all hover:border-[#AA423A]", children: jsxRuntimeExports.jsxs("span", { className: "text-white/70", children: ["Step ", index + 1, ": ", step.title] }) }, step.id)))) : (
77023
77034
  // Display message when phase has valid title but no steps
77024
77035
  jsxRuntimeExports.jsx("p", { className: "text-white/80 text-md max-w-[31rem]", children: "No steps yet. Add an assembly step and an animation to let your readers know what to do." })) })] }), (currentPhase === null || currentPhase === void 0 ? void 0 : currentPhase.steps) && currentPhase.steps.length > 0 && (jsxRuntimeExports.jsx("div", { className: "mt-8 max-w-[7rem] ml-auto", children: jsxRuntimeExports.jsx(Button, { onClick: handleBeginPhase, className: "bg-[#AA423A] hover:bg-[#8A322E] text-white px-6 py-2 rounded-lg font-medium w-full h-11", children: "Begin Phase" }) }))] }))) : (
77025
77036
  // Step Details
@@ -77575,7 +77586,7 @@ const PreviewCategoryItem = ({ category, level, selectedId, onSelect, onToggleEx
77575
77586
  }, className: "ml-2 p-0.5 hover:bg-gray-700 rounded transition-colors flex-shrink-0", "aria-label": isExpanded ? "Collapse" : "Expand", children: isExpanded ? (jsxRuntimeExports.jsx(lucideReact.ChevronDown, { size: 14, className: "text-gray-400" })) : (jsxRuntimeExports.jsx(lucideReact.ChevronRight, { size: 14, className: "text-gray-400" })) }))] }), isExpanded && hasChildren && (jsxRuntimeExports.jsx("div", { children: category.children.map((child) => (jsxRuntimeExports.jsx(PreviewCategoryItem, { category: child, level: level + 1, selectedId: selectedId, onSelect: onSelect, onToggleExpand: onToggleExpand }, child.id))) }))] }));
77576
77587
  };
77577
77588
  const PreviewSidebar = ({ categories, selectedCategoryId, onCategorySelect, onToggleExpand, className = "", }) => {
77578
- return (jsxRuntimeExports.jsx("div", { className: cn("w-64 flex flex-col px-2", className), children: jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto py-4", children: [categories.map((category, index) => (jsxRuntimeExports.jsx("div", { className: index > 0 ? "mt-8" : "", children: jsxRuntimeExports.jsx(PreviewCategoryItem, { category: category, level: 0, selectedId: selectedCategoryId, onSelect: onCategorySelect, onToggleExpand: onToggleExpand }) }, category.id))), categories.length === 0 && (jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center p-8 text-gray-500", children: jsxRuntimeExports.jsx("p", { children: "No content available" }) }))] }) }));
77589
+ return (jsxRuntimeExports.jsxs("div", { className: cn("w-64 flex flex-col px-2", className), children: [jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto py-4", children: [categories.map((category, index) => (jsxRuntimeExports.jsx("div", { className: index > 0 ? "mt-8" : "", children: jsxRuntimeExports.jsx(PreviewCategoryItem, { category: category, level: 0, selectedId: selectedCategoryId, onSelect: onCategorySelect, onToggleExpand: onToggleExpand }) }, category.id))), categories.length === 0 && (jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center p-8 text-gray-500", children: jsxRuntimeExports.jsx("p", { children: "No content available" }) }))] }), jsxRuntimeExports.jsx("div", { className: "px-4 py-3 border-t border-[#313133]", children: jsxRuntimeExports.jsxs("a", { href: "https://tnkr.ai", target: "_blank", rel: "noopener noreferrer", className: "flex items-center justify-center gap-2 text-xs text-gray-400 hover:text-gray-300 transition-colors", children: [jsxRuntimeExports.jsx("span", { children: "Powered by" }), jsxRuntimeExports.jsx("span", { className: "font-semibold text-[#AA423A]", children: "TnkrAI" })] }) })] }));
77579
77590
  };
77580
77591
 
77581
77592
  const PageContentNavigator = ({ content, className = "" }) => {