Skip to content

Export/Import project , Deplicate screenshots , Center Button#15

Open
BrahimChouih wants to merge 5 commits into
YUZU-Hub:mainfrom
BrahimChouih:Export-Import
Open

Export/Import project , Deplicate screenshots , Center Button#15
BrahimChouih wants to merge 5 commits into
YUZU-Hub:mainfrom
BrahimChouih:Export-Import

Conversation

@BrahimChouih
Copy link
Copy Markdown

No description provided.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds project export/import functionality, screenshot duplication, and a center position reset button to the App Store Screenshot Generator. The changes enable users to share projects as files, duplicate screenshots with all their settings, and quickly reset device positioning to centered defaults. The PR also includes extensive CSS formatting improvements (line breaks and spacing) without changing functionality.

Changes:

  • Added export/import functionality for projects as .screenshotproject JSON files
  • Added duplicate screenshot feature that copies all settings (background, device, text, overrides)
  • Added center position reset button to quickly return device to default centered position
  • Updated project dropdown UI with per-project export buttons and import action
  • Applied CSS formatting improvements throughout styles.css for better readability

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 8 comments.

File Description
styles.css Added styles for reset-position-btn, project-export-btn, project-option-info, project-menu-divider, and project-menu-action. Applied formatting improvements to existing animations and selectors.
index.html Added reset-position-btn element in Device tab, added hidden import-project-input file input, updated project menu structure with project-option-info wrapper, and applied HTML formatting improvements.
app.js Implemented exportProjectAsFile(), exportProjectById(), importProjectFromFile(), resetPosition(), and duplicateScreenshot(). Updated updateProjectSelector() to add export buttons per project. Added event handlers for import and reset position features.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app.js
Comment on lines +1711 to +1731
// Get first image as the primary image (for legacy compatibility)
let primaryImage = null;
const firstLang = Object.keys(localizedImages)[0];
if (firstLang) {
primaryImage = localizedImages[firstLang].image;
}

// Create screenshot entry
const screenshot = {
image: primaryImage,
name: screenshotData.name || 'Imported Screenshot',
deviceType: screenshotData.deviceType,
localizedImages: localizedImages,
background: screenshotData.background || JSON.parse(JSON.stringify(state.defaults.background)),
screenshot: screenshotData.screenshot || JSON.parse(JSON.stringify(state.defaults.screenshot)),
text: screenshotData.text || JSON.parse(JSON.stringify(state.defaults.text)),
overrides: screenshotData.overrides || {}
};

state.screenshots.push(screenshot);
}
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import function doesn't handle cases where there are no localized images or all image loading fails. If a screenshot has no localized images, primaryImage will be null (line 1712), which could cause issues when rendering. Consider adding a check to ensure at least one valid image exists before adding the screenshot to the state, or provide a fallback/error message for screenshots that fail to load.

Copilot uses AI. Check for mistakes.
Comment thread app.js
Comment on lines +946 to +952
<button class="project-export-btn" data-project-id="${project.id}" title="Export this project">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
</svg>
</button>
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The project export buttons lack proper ARIA labels for screen readers. While the title attribute provides a tooltip, it's not always read by screen readers. Consider adding aria-label="Export project [project name]" to each export button to improve accessibility for users with screen readers.

Copilot uses AI. Check for mistakes.
Comment thread index.html
</svg>
</button>
<div class="project-menu" id="project-menu">
<!-- Projects will be added here dynamically -->
<!-- Projects and export/import actions are added dynamically by updateProjectSelector() -->
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HTML comment references updateProjectSelector() function for dynamic content generation. While this is helpful, consider also mentioning that the export buttons for individual projects are created within this function so developers know where to look when debugging or modifying this feature.

Suggested change
<!-- Projects and export/import actions are added dynamically by updateProjectSelector() -->
<!-- Projects, per-project export buttons, and export/import actions are added dynamically by updateProjectSelector() -->

Copilot uses AI. Check for mistakes.
Comment thread app.js
Comment on lines +1664 to +1734
// Create the new project
const projectId = 'project_' + Date.now();
projects.push({ id: projectId, name: projectName, screenshotCount: 0 });
saveProjectsMeta();

// Switch to the new project
currentProjectId = projectId;

// Reset state
state.screenshots = [];
state.selectedIndex = 0;
state.outputDevice = importData.state.outputDevice || 'iphone-6.9';
state.customWidth = importData.state.customWidth || 1290;
state.customHeight = importData.state.customHeight || 2796;
state.currentLanguage = importData.state.currentLanguage || 'en';
state.projectLanguages = importData.state.projectLanguages || ['en'];

if (importData.state.defaults) {
state.defaults = importData.state.defaults;
}

// Restore screenshots with their images
const importedScreenshots = importData.state.screenshots || [];
for (const screenshotData of importedScreenshots) {
// Recreate localized images with Image objects
const localizedImages = {};
if (screenshotData.localizedImages) {
for (const lang of Object.keys(screenshotData.localizedImages)) {
const langData = screenshotData.localizedImages[lang];
if (langData?.src) {
// Create Image object from base64
const img = new Image();
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = () => reject(new Error(`Failed to load image for ${lang}`));
img.src = langData.src;
});

localizedImages[lang] = {
image: img,
src: langData.src,
name: langData.name
};
}
}
}

// Get first image as the primary image (for legacy compatibility)
let primaryImage = null;
const firstLang = Object.keys(localizedImages)[0];
if (firstLang) {
primaryImage = localizedImages[firstLang].image;
}

// Create screenshot entry
const screenshot = {
image: primaryImage,
name: screenshotData.name || 'Imported Screenshot',
deviceType: screenshotData.deviceType,
localizedImages: localizedImages,
background: screenshotData.background || JSON.parse(JSON.stringify(state.defaults.background)),
screenshot: screenshotData.screenshot || JSON.parse(JSON.stringify(state.defaults.screenshot)),
text: screenshotData.text || JSON.parse(JSON.stringify(state.defaults.text)),
overrides: screenshotData.overrides || {}
};

state.screenshots.push(screenshot);
}

// Save the imported state
saveState();
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import function switches to a new project and sets currentProjectId before the state is fully loaded and saved. If an error occurs during screenshot loading (lines 1687-1731), the application will be left in an inconsistent state with an empty project. Consider wrapping the entire import process in a transaction-like pattern where the currentProjectId is only updated after all data has been successfully loaded, or implement proper rollback logic in the error handler.

Copilot uses AI. Check for mistakes.
Comment thread app.js
Comment on lines +4291 to +4339
function duplicateScreenshot(index) {
if (index < 0 || index >= state.screenshots.length) return;

const original = state.screenshots[index];

// Deep clone the screenshot
const duplicate = {
// Copy the image reference (same image object)
image: original.image,
name: original.name + ' (copy)',
deviceType: original.deviceType,

// Deep clone localized images
localizedImages: {},

// Deep clone settings
background: JSON.parse(JSON.stringify(original.background)),
screenshot: JSON.parse(JSON.stringify(original.screenshot)),
text: JSON.parse(JSON.stringify(original.text)),
overrides: JSON.parse(JSON.stringify(original.overrides || {}))
};

// Copy localized images (with image object references)
if (original.localizedImages) {
Object.keys(original.localizedImages).forEach(lang => {
const langData = original.localizedImages[lang];
if (langData) {
duplicate.localizedImages[lang] = {
image: langData.image, // Same image object reference
src: langData.src,
name: langData.name
};
}
});
}

// Insert duplicate after the original
state.screenshots.splice(index + 1, 0, duplicate);

// Select the new duplicate
state.selectedIndex = index + 1;

// Update UI
saveState();
updateScreenshotList();
syncUIWithState();
updateGradientStopsUI();
updateCanvas();
}
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate function definition detected. The function duplicateScreenshot is defined twice in this file - once at line 1794 and again here at line 4291. This will cause the second definition to override the first one, which may lead to unexpected behavior. The two implementations differ slightly:

  1. The first implementation (line 1794) handles file name extensions differently and doesn't call saveState().
  2. The second implementation (line 4291) uses a simpler name copy approach and includes saveState().

Consider either:

  • Removing the duplicate definition and keeping only one implementation
  • Renaming one of the functions if both are needed for different purposes
  • Merging the best parts of both implementations into a single function

Copilot uses AI. Check for mistakes.
Comment thread app.js
Comment on lines +1485 to +1487
const exportBtn = document.getElementById('export-project-btn');
if (exportBtn) exportBtn.classList.add('loading');

Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exportBtn element with ID export-project-btn is referenced on line 1485, but this element doesn't exist in the HTML. The export functionality is now triggered through buttons with class project-export-btn in the dropdown menu, which have data-project-id attributes. This will cause exportBtn to be null, and the loading state won't be applied. Either remove these lines or update the selector to match the actual buttons being used.

Suggested change
const exportBtn = document.getElementById('export-project-btn');
if (exportBtn) exportBtn.classList.add('loading');

Copilot uses AI. Check for mistakes.
Comment thread styles.css
Comment on lines +2441 to +2450
@keyframes spin {
from {
transform: rotate(0deg);
}

to {
transform: rotate(360deg);
}
}

Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate @keyframes spin definition. The same animation is defined at line 1331 and again here at line 2441. This creates redundant code. Consider removing one of the duplicate definitions to maintain cleaner code.

Suggested change
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

Copilot uses AI. Check for mistakes.
Comment thread app.js
Comment on lines +1688 to +1709
// Recreate localized images with Image objects
const localizedImages = {};
if (screenshotData.localizedImages) {
for (const lang of Object.keys(screenshotData.localizedImages)) {
const langData = screenshotData.localizedImages[lang];
if (langData?.src) {
// Create Image object from base64
const img = new Image();
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = () => reject(new Error(`Failed to load image for ${lang}`));
img.src = langData.src;
});

localizedImages[lang] = {
image: img,
src: langData.src,
name: langData.name
};
}
}
}
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing validation for image data size when importing projects. The import function loads base64 image data from the imported file without checking the size. Maliciously crafted files with extremely large base64 images could cause memory issues or browser crashes. Consider adding validation to check the size of image data before loading, or implementing a maximum file size check for the imported project file itself.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants