diff --git a/src/main/core/projects.js b/src/main/core/projects.js
index 4408663..2ff7dc9 100644
--- a/src/main/core/projects.js
+++ b/src/main/core/projects.js
@@ -122,7 +122,6 @@ const createImageFile = async (projectPath, scene, ext, data) => {
}
await writeFile(filePath, data);
return {
- id,
filename: `${id}.${ext}`,
scene,
path: filePath,
@@ -134,7 +133,6 @@ export const savePicture = async (projectPath, track, ext, buffer) => {
const trackId = Number(track);
const file = await createImageFile(projectPath, trackId, ext, buffer);
return {
- id: file.id,
filename: file.filename,
deleted: false,
length: 1,
diff --git a/src/renderer/actions/index.js b/src/renderer/actions/index.js
index 651e6a0..e06a200 100644
--- a/src/renderer/actions/index.js
+++ b/src/renderer/actions/index.js
@@ -31,7 +31,7 @@ const getDefaultPreview = async (data) => {
for (let i = 0; i < (data?.project?.scenes?.length || 0); i++) {
for (const picture of data?.project?.scenes?.[i]?.pictures || []) {
if (!picture.deleted) {
- return getFrameBlobUrl(picture.id);
+ return getFrameBlobUrl(picture.filename?.split('.')?.[0]);
}
}
}
@@ -48,7 +48,7 @@ const computeProject = async (data, bindPictureLink = true) => {
pictures: await Promise.all(
scene.pictures.map(async (picture) => ({
...picture,
- link: bindPictureLink ? await getFrameBlobUrl(picture.id) : null,
+ link: bindPictureLink ? await getFrameBlobUrl(picture.filename?.split('.')?.[0]) : null,
}))
),
};
@@ -150,7 +150,6 @@ export const Actions = {
SAVE_PICTURE: async (evt, { buffer, extension = 'jpg' }) => {
const frameId = await createFrame(buffer, extension);
return {
- id: `${frameId}`,
filename: `${frameId}.${extension || 'dat'}`,
deleted: false,
length: 1,
diff --git a/src/renderer/components/ShortcutsList/index.jsx b/src/renderer/components/ShortcutsList/index.jsx
index 7e254a0..52936e7 100644
--- a/src/renderer/components/ShortcutsList/index.jsx
+++ b/src/renderer/components/ShortcutsList/index.jsx
@@ -56,6 +56,7 @@ const ShortcutsList = ({ t, shortcuts }) => {
ONION_MORE: t('Increase onion skin'),
ONION_LESS: t('Decrease onion skin'),
MUTE: t('Mute / Unmute sounds'),
+ CLONE: t('Clone current frame'),
DUPLICATE: t('Duplicate current frame'),
DEDUPLICATE: t('Deduplicate current frame'),
GRID: t('Show / Hide grid'),
@@ -80,7 +81,7 @@ const ShortcutsList = ({ t, shortcuts }) => {
'ONION_MORE',
'GRID',
],
- ACTIONS: ['DELETE_FRAME', 'DUPLICATE', 'DEDUPLICATE', 'HIDE_FRAME'],
+ ACTIONS: ['DELETE_FRAME', 'CLONE', 'DUPLICATE', 'DEDUPLICATE', 'HIDE_FRAME'],
NAVIGATION: ['FRAME_LEFT', 'FRAME_RIGHT', 'FRAME_LIVE', 'FRAME_FIRST'],
OTHER: ['HOME'],
};
diff --git a/src/renderer/core/shortcuts.js b/src/renderer/core/shortcuts.js
index 5d11c6f..dd9c959 100644
--- a/src/renderer/core/shortcuts.js
+++ b/src/renderer/core/shortcuts.js
@@ -26,6 +26,7 @@ const SHORTCUTS = {
ONION_MORE: ['+'],
ONION_LESS: ['-'],
MUTE: ['m', '/', 'ctrl+m'],
+ CLONE: ['c'],
DUPLICATE: ['pageup'],
DEDUPLICATE: ['pagedown'],
GRID: ['g'],
diff --git a/src/renderer/hooks/useProject.js b/src/renderer/hooks/useProject.js
index b393242..03e9b75 100644
--- a/src/renderer/hooks/useProject.js
+++ b/src/renderer/hooks/useProject.js
@@ -94,6 +94,25 @@ function useProject(options) {
});
});
+ // Action clone frame
+ const actionCloneFrame = useCallback(async (trackId, frameId) => {
+ const sceneId = Number(trackId);
+ setProjectData((oldData) => {
+ let d = structuredClone(oldData);
+ if (d.project.scenes[sceneId]) {
+ const newId = Math.max(0, ...d.project.scenes[sceneId].pictures.map((e) => e.id)) + 1;
+ d.project.scenes[sceneId].pictures = d.project.scenes[sceneId].pictures.reduce((acc, p) => {
+ if (`${p.id}` !== `${frameId}`) {
+ return [...acc, p];
+ } else {
+ return [...acc, p, { ...p, id: newId }];
+ }
+ }, []);
+ }
+ return d;
+ });
+ });
+
// Action delete frame
const actionDeleteFrame = useCallback(async (trackId, frameId) => {
const sceneId = Number(trackId);
@@ -152,11 +171,12 @@ function useProject(options) {
setProjectData((oldData) => {
let d = structuredClone(oldData);
if (d.project.scenes[sceneId]) {
+ const newId = Math.max(0, ...d.project.scenes[sceneId].pictures.map((e) => e.id)) + 1;
const index = beforeFrameId === false ? -1 : d.project.scenes[sceneId].pictures.findIndex((f) => `${f.id}` === `${beforeFrameId}`);
if (index >= 0) {
- d.project.scenes[sceneId].pictures = [...d.project.scenes[sceneId].pictures.slice(0, index), addedPicture, ...d.project.scenes[sceneId].pictures.slice(index)];
+ d.project.scenes[sceneId].pictures = [...d.project.scenes[sceneId].pictures.slice(0, index), { ...addedPicture, id: newId }, ...d.project.scenes[sceneId].pictures.slice(index)];
} else {
- d.project.scenes[sceneId].pictures = [...d.project.scenes[sceneId].pictures, addedPicture];
+ d.project.scenes[sceneId].pictures = [...d.project.scenes[sceneId].pictures, { ...addedPicture, id: newId }];
}
}
return d;
@@ -211,7 +231,8 @@ function useProject(options) {
changeFPS: actionChangeFPS,
changeRatio: actionChangeRatio,
applyHiddenFrameStatus: actionApplyHiddenFrameStatus,
- actionApplyDuplicateFrameOffset: actionApplyDuplicateFrameOffset,
+ applyDuplicateFrameOffset: actionApplyDuplicateFrameOffset,
+ cloneFrame: actionCloneFrame,
deleteFrame: actionDeleteFrame,
rename: actionRename,
moveFrame: actionMoveFrame,
diff --git a/src/renderer/views/Animator.jsx b/src/renderer/views/Animator.jsx
index fa6756b..7a87080 100644
--- a/src/renderer/views/Animator.jsx
+++ b/src/renderer/views/Animator.jsx
@@ -306,10 +306,13 @@ const Animator = ({ t }) => {
projectActions.applyHiddenFrameStatus(track, currentFrameId, !currentFrame?.hidden);
},
DUPLICATE: async () => {
- projectActions.actionApplyDuplicateFrameOffset(track, currentFrameId, 1);
+ projectActions.applyDuplicateFrameOffset(track, currentFrameId, 1);
+ },
+ CLONE: async () => {
+ projectActions.cloneFrame(track, currentFrameId);
},
DEDUPLICATE: async () => {
- projectActions.actionApplyDuplicateFrameOffset(track, currentFrameId, -1);
+ projectActions.applyDuplicateFrameOffset(track, currentFrameId, -1);
},
MUTE: () => {
setIsMuted(!isMuted);
@@ -368,8 +371,8 @@ const Animator = ({ t }) => {
showGrid={gridStatus}
blendMode={differenceStatus}
shortPlayStatus={shortPlayStatus}
- loopStatus={loopStatus}
shortPlayFrames={Number(settings.SHORT_PLAY) || 1}
+ loopStatus={loopStatus}
cameraId={currentCameraId}
cameraCapabilities={currentCameraCapabilities}
fps={fps}
@@ -411,7 +414,15 @@ const Animator = ({ t }) => {
frameQuantity={pictures.length}
isCurrentFrameHidden={!!currentFrame.hidden}
/>
-
+
{!showCameraSettings && !showProjectSettings && }
setShowCameraSettings(false)}>