Function bodies 189 total
getSelectedClipId function · javascript · L1287-L1290 (4 LOC)src/js/app.js
function getSelectedClipId(selectId) {
const select = document.getElementById(selectId);
return select && select.value ? select.value : null;
}updateClipActionButtons function · javascript · L1292-L1304 (13 LOC)src/js/app.js
function updateClipActionButtons() {
const videoId = getSelectedClipId('video-clip-select');
const audioId = getSelectedClipId('audio-clip-select');
['btn-video-clip-jump', 'btn-video-clip-copy', 'btn-video-clip-cut', 'btn-video-clip-remove'].forEach(id => {
const btn = document.getElementById(id);
if (btn) btn.disabled = !videoId;
});
['btn-audio-clip-jump', 'btn-audio-clip-copy', 'btn-audio-clip-cut', 'btn-audio-clip-remove'].forEach(id => {
const btn = document.getElementById(id);
if (btn) btn.disabled = !audioId;
});
}jumpToClip function · javascript · L1306-L1317 (12 LOC)src/js/app.js
function jumpToClip(clipId) {
const clip = Timeline.getClips().find(c => c.id === clipId);
if (!clip) return;
Timeline.selectClip(clipId);
Player.seekTo(clip.startTime);
if (clip.type === 'video') {
Player.loadVideo(clip.filePath);
showVideoPlayer();
}
Accessibility.announce(`Jumped to "${clip.name}" at ${Accessibility.formatTime(clip.startTime)}`);
renderClipList();
}clipAction function · javascript · L1319-L1347 (29 LOC)src/js/app.js
function clipAction(selectId, action) {
const clipId = getSelectedClipId(selectId);
if (!clipId) return;
const clip = Timeline.getClips().find(c => c.id === clipId);
if (!clip) return;
switch (action) {
case 'jump':
jumpToClip(clipId);
break;
case 'copy':
clipboardClip = { ...clip };
clipboardAction = 'copy';
Accessibility.announce(`Copied "${clip.name}"`);
break;
case 'cut':
clipboardClip = { ...clip };
clipboardAction = 'cut';
Timeline.removeClip(clipId);
Accessibility.announce(`Cut "${clip.name}"`);
renderClipList();
break;
case 'remove':
Timeline.removeClip(clipId);
Accessibility.announce(`Removed "${clip.name}"`);
renderClipList();
break;
}
}initClipList function · javascript · L1349-L1394 (46 LOC)src/js/app.js
function initClipList() {
// Select change events
const videoSelect = document.getElementById('video-clip-select');
const audioSelect = document.getElementById('audio-clip-select');
if (videoSelect) videoSelect.addEventListener('change', updateClipActionButtons);
if (audioSelect) audioSelect.addEventListener('change', updateClipActionButtons);
// Video clip action buttons
document.getElementById('btn-video-clip-jump')?.addEventListener('click', () => clipAction('video-clip-select', 'jump'));
document.getElementById('btn-video-clip-copy')?.addEventListener('click', () => clipAction('video-clip-select', 'copy'));
document.getElementById('btn-video-clip-cut')?.addEventListener('click', () => clipAction('video-clip-select', 'cut'));
document.getElementById('btn-video-clip-remove')?.addEventListener('click', () => clipAction('video-clip-select', 'remove'));
// Audio clip action buttons
document.getElementById('btn-audio-clip-jump')?.addEveinitUserGuide function · javascript · L1396-L1490 (95 LOC)src/js/app.js
function initUserGuide() {
// Page-based navigation — only one section visible at a time
const navBtns = document.querySelectorAll('.guide-nav-btn');
const allSections = document.querySelectorAll('.guide-section');
const pageInfo = document.getElementById('guide-page-info');
function showGuidePage(targetId, focusHeading = true) {
const target = document.getElementById(targetId);
if (!target) return;
// Hide all sections, show only the target
allSections.forEach(s => s.style.display = 'none');
target.style.display = 'block';
// Update active nav button
navBtns.forEach(b => {
const isActive = b.getAttribute('data-target') === targetId;
b.classList.toggle('active', isActive);
b.setAttribute('aria-current', isActive ? 'page' : 'false');
});
// Update page indicator and prev/next button states
const pageIndex = Array.from(navBtns).findIndex(b => b.getAttribute('data-target') === tarshowGuidePage function · javascript · L1402-L1434 (33 LOC)src/js/app.js
function showGuidePage(targetId, focusHeading = true) {
const target = document.getElementById(targetId);
if (!target) return;
// Hide all sections, show only the target
allSections.forEach(s => s.style.display = 'none');
target.style.display = 'block';
// Update active nav button
navBtns.forEach(b => {
const isActive = b.getAttribute('data-target') === targetId;
b.classList.toggle('active', isActive);
b.setAttribute('aria-current', isActive ? 'page' : 'false');
});
// Update page indicator and prev/next button states
const pageIndex = Array.from(navBtns).findIndex(b => b.getAttribute('data-target') === targetId);
if (pageInfo) pageInfo.textContent = `Page ${pageIndex + 1} of ${navBtns.length}`;
const prevBtn = document.getElementById('guide-prev-page');
const nextBtn = document.getElementById('guide-next-page');
if (prevBtn) prevBtn.disabled = (pageIndex <= 0);
if (Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
showResponse function · javascript · L12-L18 (7 LOC)src/js/chatbot.js
function showResponse(text) {
if (!responseInline || !responseBody) return;
responseBody.innerHTML = text;
responseInline.classList.remove('hidden');
// Announce for screen readers
Accessibility.announceStatus(responseBody.textContent.substring(0, 200));
}has function · javascript · L21-L23 (3 LOC)src/js/chatbot.js
function has(text, ...words) {
return words.some(w => text.includes(w));
}means function · javascript · L26-L32 (7 LOC)src/js/chatbot.js
function means(text, ...concepts) {
const t = ' ' + text + ' ';
return concepts.some(c => {
// Each concept can be a pipe-separated list of alternatives
return c.split('|').some(alt => t.includes(alt.trim()));
});
}extractNumber function · javascript · L35-L38 (4 LOC)src/js/chatbot.js
function extractNumber(text) {
const match = text.match(/(\d+(?:\.\d+)?)/);
return match ? parseFloat(match[1]) : null;
}extractNumbers function · javascript · L41-L44 (4 LOC)src/js/chatbot.js
function extractNumbers(text) {
const matches = text.match(/\d+(?:\.\d+)?/g);
return matches ? matches.map(Number) : [];
}requireClip function · javascript · L47-L49 (3 LOC)src/js/chatbot.js
function requireClip() {
return Timeline.getSelectedClip();
}fmtTime function · javascript · L52-L54 (3 LOC)src/js/chatbot.js
function fmtTime(seconds) {
return Accessibility.formatTimeDisplay(seconds);
}describeImage function · javascript · L712-L809 (98 LOC)src/js/chatbot.js
function describeImage() {
const canvas = document.getElementById('photo-canvas');
if (!canvas) return 'Cannot access the photo canvas.';
const ctx = canvas.getContext('2d');
const w = canvas.width;
const h = canvas.height;
if (w === 0 || h === 0) return 'The image canvas is empty.';
const imageData = ctx.getImageData(0, 0, w, h);
const data = imageData.data;
const totalPixels = w * h;
// Analyze colors
let rTotal = 0, gTotal = 0, bTotal = 0;
let brightPixels = 0, darkPixels = 0;
let redPixels = 0, greenPixels = 0, bluePixels = 0;
let whitePixels = 0, blackPixels = 0;
for (let i = 0; i < data.length; i += 4) {
const r = data[i], g = data[i + 1], b = data[i + 2];
rTotal += r; gTotal += g; bTotal += b;
const brightness = (r + g + b) / 3;
if (brightness > 200) brightPixels++;
if (brightness < 55) darkPixels++;
if (brightness > 240 && Math.abs(r - g) < 20 && Math.abs(g - b) < 20) whitePixAbout: code-quality intelligence by Repobility · https://repobility.com
init function · javascript · L812-L841 (30 LOC)src/js/chatbot.js
function init() {
const sendBtn = document.getElementById('btn-send-chat');
const dismissBtn = document.getElementById('btn-chat-dismiss');
if (chatInput) {
chatInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
processCommand(chatInput.value);
chatInput.value = '';
}
});
}
if (sendBtn) {
sendBtn.addEventListener('click', () => {
processCommand(chatInput.value);
chatInput.value = '';
chatInput.focus();
});
}
// Dismiss inline response
if (dismissBtn) {
dismissBtn.addEventListener('click', () => {
if (responseInline) responseInline.classList.add('hidden');
chatInput.focus();
});
}
}getFormatArgs function · javascript · L19-L43 (25 LOC)src/js/converter.js
function getFormatArgs(format, quality = 'medium') {
const qArgs = qualityMap[quality] || qualityMap.medium;
const formatArgs = {
mp4: ['-c:v', 'libx264', '-c:a', 'aac', ...qArgs],
avi: ['-c:v', 'mpeg4', '-c:a', 'mp3', '-q:v', '5'],
mkv: ['-c:v', 'libx264', '-c:a', 'aac', ...qArgs],
mov: ['-c:v', 'libx264', '-c:a', 'aac', ...qArgs],
webm: ['-c:v', 'libvpx-vp9', '-c:a', 'libopus', '-b:v', '2M'],
wmv: ['-c:v', 'wmv2', '-c:a', 'wmav2'],
flv: ['-c:v', 'flv1', '-c:a', 'mp3'],
mpeg: ['-c:v', 'mpeg2video', '-c:a', 'mp2'],
m4v: ['-c:v', 'libx264', '-c:a', 'aac', ...qArgs],
ts: ['-c:v', 'libx264', '-c:a', 'aac', '-f', 'mpegts'],
gif: ['-vf', 'fps=10,scale=480:-1:flags=lanczos', '-loop', '0'],
mp3: ['-vn', '-c:a', 'libmp3lame', '-q:a', '2'],
wav: ['-vn', '-c:a', 'pcm_s16le'],
ogg: ['-vn', '-c:a', 'libvorbis', '-q:a', '5'],
flac: ['-vn', '-c:a', 'flac'],
aac: ['-vn', '-c:a', 'aac', '-b:a', '1convertMedia function · javascript · L46-L106 (61 LOC)src/js/converter.js
async function convertMedia(inputPath, outputFormat, quality = 'medium') {
if (!inputPath) {
Accessibility.announce('No file selected for conversion');
return;
}
if (!window.api) {
Accessibility.announce('File conversion requires the desktop application.');
const statusEl = document.getElementById('conv-media-status');
if (statusEl) statusEl.textContent = 'Error: Conversion requires the desktop application with FFmpeg installed.';
return;
}
const outputPath = inputPath.replace(/\.[^.]+$/, `.${outputFormat}`);
const args = getFormatArgs(outputFormat, quality);
// Show progress
const progressEl = document.getElementById('conv-media-progress');
const statusEl = document.getElementById('conv-media-status');
if (progressEl) {
progressEl.classList.remove('hidden');
progressEl.setAttribute('aria-valuenow', '50');
}
if (statusEl) statusEl.textContent = 'Converting... Please wait.';
Accessibinit function · javascript · L109-L279 (171 LOC)src/js/converter.js
function init() {
// Media file selection
const selectMediaBtn = document.getElementById('btn-conv-select-media');
const mediaDisplay = document.getElementById('conv-media-file-display');
const convertMediaBtn = document.getElementById('btn-conv-media-start');
if (selectMediaBtn) {
selectMediaBtn.addEventListener('click', async () => {
if (!window.api) return;
const result = await window.api.showOpenDialog({
title: 'Select Video or Audio File',
filters: [
{ name: 'Media Files', extensions: ['mp4', 'avi', 'mkv', 'mov', 'wmv', 'webm', 'flv', 'mp3', 'wav', 'ogg', 'flac', 'aac', 'wma', 'm4a'] },
{ name: 'All Files', extensions: ['*'] },
],
properties: ['openFile'],
});
if (!result.canceled && result.filePaths.length > 0) {
mediaInputPath = result.filePaths[0];
const fileName = mediaInputPath.split(/[\\/]/).pop();
if (mediaDisplay) mediaDgetFilterString function · javascript · L30-L32 (3 LOC)src/js/effects.js
function getFilterString() {
return `brightness(${filters.brightness}%) contrast(${filters.contrast}%) saturate(${filters.saturation}%) blur(${filters.blur}px) grayscale(${filters.grayscale}%) sepia(${filters.sepia}%) hue-rotate(${filters.hue}deg) invert(${filters.invert}%)`;
}setFilter function · javascript · L35-L41 (7 LOC)src/js/effects.js
function setFilter(name, value) {
if (name in filters) {
filters[name] = parseFloat(value);
applyFilters();
updateSlider(name, value);
}
}applyPreset function · javascript · L44-L55 (12 LOC)src/js/effects.js
function applyPreset(presetName) {
const preset = presets[presetName];
if (!preset) {
Accessibility.announce(`Unknown preset: ${presetName}`);
return false;
}
Object.assign(filters, preset);
applyFilters();
updateAllSliders();
Accessibility.announce(`Applied ${presetName} filter preset`);
return true;
}resetAll function · javascript · L58-L63 (6 LOC)src/js/effects.js
function resetAll() {
Object.assign(filters, presets.none);
applyFilters();
updateAllSliders();
Accessibility.announce('All filters reset to default');
}Repobility (the analyzer behind this table) · https://repobility.com
applyFilters function · javascript · L66-L68 (3 LOC)src/js/effects.js
function applyFilters() {
Player.applyFilter(getFilterString());
}updateSlider function · javascript · L71-L85 (15 LOC)src/js/effects.js
function updateSlider(name, value) {
// Try both ID patterns: filter-{name} and filter-{name}-slider
const slider = document.getElementById(`filter-${name}`) || document.getElementById(`filter-${name}-slider`);
const display = document.getElementById(`${name}-value`) || document.getElementById(`filter-${name}-val`);
if (slider) {
slider.value = value;
let unit = '%';
if (name === 'blur') unit = 'px';
else if (name === 'hue') unit = '\u00B0';
const displayText = `${value}${unit}`;
slider.setAttribute('aria-valuetext', displayText);
slider.setAttribute('aria-label', `${name.charAt(0).toUpperCase() + name.slice(1)}, currently ${displayText}`);
if (display) display.textContent = displayText;
}
}updateAllSliders function · javascript · L88-L92 (5 LOC)src/js/effects.js
function updateAllSliders() {
for (const [name, value] of Object.entries(filters)) {
updateSlider(name, value);
}
}getFilters function · javascript · L95-L97 (3 LOC)src/js/effects.js
function getFilters() {
return { ...filters };
}getFFmpegFilters function · javascript · L100-L122 (23 LOC)src/js/effects.js
function getFFmpegFilters() {
const ffFilters = [];
if (filters.brightness !== 100) {
ffFilters.push(`eq=brightness=${(filters.brightness - 100) / 100}`);
}
if (filters.contrast !== 100) {
ffFilters.push(`eq=contrast=${filters.contrast / 100}`);
}
if (filters.saturation !== 100) {
ffFilters.push(`eq=saturation=${filters.saturation / 100}`);
}
if (filters.blur > 0) {
const sigma = Math.max(1, Math.round(filters.blur * 2));
ffFilters.push(`boxblur=${sigma}:${sigma}`);
}
if (filters.grayscale > 0) {
ffFilters.push(`hue=s=${1 - filters.grayscale / 100}`);
}
if (filters.hue !== 0) {
ffFilters.push(`hue=h=${filters.hue}`);
}
return ffFilters.join(',') || 'null';
}getPresetNames function · javascript · L125-L127 (3 LOC)src/js/effects.js
function getPresetNames() {
return Object.keys(presets);
}init function · javascript · L130-L140 (11 LOC)src/js/effects.js
function init() {
const filterNames = ['brightness', 'contrast', 'saturation', 'blur', 'grayscale', 'sepia', 'hue', 'invert'];
filterNames.forEach(name => {
const slider = document.getElementById(`filter-${name}`);
if (slider) {
slider.addEventListener('input', () => {
setFilter(name, parseFloat(slider.value));
});
}
});
}setApiKey function · javascript · L16-L19 (4 LOC)src/js/gemini.js
function setApiKey(key) {
apiKey = key.trim();
localStorage.setItem('as-gemini-key', apiKey);
}If a scraper extracted this row, it came from Repobility (https://repobility.com)
getApiKey function · javascript · L21-L24 (4 LOC)src/js/gemini.js
function getApiKey() {
if (!apiKey) apiKey = localStorage.getItem('as-gemini-key') || '';
return apiKey;
}isAvailable function · javascript · L26-L28 (3 LOC)src/js/gemini.js
function isAvailable() {
return !!getApiKey();
}buildSystemPrompt function · javascript · L33-L155 (123 LOC)src/js/gemini.js
function buildSystemPrompt() {
const clips = typeof Timeline !== 'undefined' ? Timeline.getClips() : [];
const selectedClip = typeof Timeline !== 'undefined' ? Timeline.getSelectedClip() : null;
const currentTime = typeof Player !== 'undefined' && Player.getCurrentTime ? Player.getCurrentTime() : 0;
const totalDuration = typeof Timeline !== 'undefined' ? Timeline.getTotalDuration() : 0;
const hasPhoto = typeof PhotoEditor !== 'undefined' && PhotoEditor.hasImage;
let clipInfo = 'Timeline is empty.';
if (clips.length > 0) {
clipInfo = clips.map((c, i) => {
let line = ` ${i}: "${c.name}" (${c.type}) ${c.startTime.toFixed(1)}s–${(c.startTime + c.duration).toFixed(1)}s`;
if (c.volume !== undefined && c.volume !== 100) line += ` vol=${c.volume}%`;
if (c.speed !== undefined && c.speed !== 1) line += ` speed=${c.speed}x`;
if (c.text) line += ` text="${c.text}"`;
return line;
}).join('\n');
}
let selectsend function · javascript · L160-L260 (101 LOC)src/js/gemini.js
async function send(userMessage) {
const key = getApiKey();
if (!key) throw new Error('No Gemini API key set');
const systemPrompt = buildSystemPrompt();
const parts = [{ text: userMessage }];
// Always attach the photo if one is loaded — Gemini can see it
// Downscale to max 1024px so Gemini sees the FULL image without hitting size limits
const hasPhoto = typeof PhotoEditor !== 'undefined' && PhotoEditor.hasImage;
if (hasPhoto) {
try {
const dataURL = PhotoEditor.getImageDataURL();
if (dataURL) {
// Downscale large images so Gemini can see the entire thing
const tempImg = new Image();
const scaled = await new Promise((resolve) => {
tempImg.onload = () => {
const maxDim = 1024;
let sw = tempImg.width, sh = tempImg.height;
if (sw > maxDim || sh > maxDim) {
const ratio = Math.min(maxDim / sw, maxDim / sh);
sw = Math.extractText function · javascript · L265-L270 (6 LOC)src/js/gemini.js
function extractText(data) {
if (!data.candidates || data.candidates.length === 0) return 'No response from Gemini.';
const parts = data.candidates[0].content?.parts;
if (!parts || parts.length === 0) return 'Empty response.';
return parts.map(p => p.text || '').join('');
}parseActions function · javascript · L272-L282 (11 LOC)src/js/gemini.js
function parseActions(responseText) {
const actionsMatch = responseText.match(/```actions\s*\n?([\s\S]*?)```/);
if (!actionsMatch) return { text: responseText, actions: [] };
try {
const actions = JSON.parse(actionsMatch[1].trim());
const text = responseText.replace(/```actions\s*\n?[\s\S]*?```/, '').trim();
return { text, actions: Array.isArray(actions) ? actions : [actions] };
} catch (e) {
return { text: responseText, actions: [] };
}
}ensureClipSelected function · javascript · L288-L298 (11 LOC)src/js/gemini.js
function ensureClipSelected() {
let clip = Timeline.getSelectedClip();
if (!clip) {
const clips = Timeline.getClips();
if (clips.length > 0) {
Timeline.selectClip(clips[0].id);
clip = clips[0];
}
}
return clip;
}executeAction function · javascript · L300-L485 (186 LOC)src/js/gemini.js
async function executeAction(action) {
const p = action.params || {};
switch (action.action) {
// --- Clip selection ---
case 'selectClip': {
const clips = Timeline.getClips();
if (p.index >= 0 && p.index < clips.length) Timeline.selectClip(clips[p.index].id);
break;
}
case 'selectClipByName': {
const clips = Timeline.getClips();
const target = (p.name || '').toLowerCase();
const match = clips.find(c => c.name.toLowerCase().includes(target));
if (match) Timeline.selectClip(match.id);
break;
}
// --- Timeline editing ---
case 'trim': {
const clip = ensureClipSelected();
if (clip) Timeline.trimClip(clip.id, p.trimStart, p.trimEnd);
break;
}
case 'split': {
if (p.time === 'playhead') { Timeline.splitAtPlayhead(); break; }
const clip = ensureClipSelected();
if (clip) {
// p.time is relative to clip start Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
processResponse function · javascript · L490-L511 (22 LOC)src/js/gemini.js
async function processResponse(responseText) {
const { text, actions } = parseActions(responseText);
for (const action of actions) {
try {
await executeAction(action);
// Small yield so the UI updates between chained actions (e.g. select then trim)
await new Promise(r => setTimeout(r, 50));
} catch (e) {
console.error('Error executing action:', action, e);
}
}
// Markdown-ish → HTML
let html = text
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.*?)\*/g, '<em>$1</em>')
.replace(/`(.*?)`/g, '<code>$1</code>')
.replace(/\n/g, '<br>');
return { html, actionsExecuted: actions.length };
}loadImage function · javascript · L21-L46 (26 LOC)src/js/photoeditor.js
function loadImage(filePath) {
currentFilePath = filePath;
const img = new Image();
img.onload = () => {
originalImage = img;
baseImage = img;
undoStack = [];
redoStack = [];
canvas.width = img.width;
canvas.height = img.height;
resetAdjustments();
drawImage();
if (noPhotoMsg) noPhotoMsg.style.display = 'none';
Accessibility.announce(`Image loaded: ${filePath.split(/[\\/]/).pop()}, ${img.width} by ${img.height} pixels`);
Accessibility.setStatus(`Image loaded: ${img.width}x${img.height}`);
const resizeW = document.getElementById('resize-width');
const resizeH = document.getElementById('resize-height');
if (resizeW) resizeW.value = img.width;
if (resizeH) resizeH.value = img.height;
};
img.onerror = () => {
Accessibility.announce('Error loading image file');
};
img.src = filePath.startsWith('file://') ? filePath : `file:///${filePath.replace(/\\/g, '/')}`;
}drawImage function · javascript · L49-L71 (23 LOC)src/js/photoeditor.js
function drawImage() {
if (!originalImage || !ctx) return;
const img = originalImage;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate((rotation * Math.PI) / 180);
ctx.scale(flipH ? -1 : 1, flipV ? -1 : 1);
let filterStr = '';
filterStr += `brightness(${100 + adjustments.brightness}%) `;
filterStr += `contrast(${100 + adjustments.contrast}%) `;
filterStr += `saturate(${100 + adjustments.saturation}%) `;
ctx.filter = filterStr.trim();
const drawW = rotation % 180 === 0 ? canvas.width : canvas.height;
const drawH = rotation % 180 === 0 ? canvas.height : canvas.width;
ctx.drawImage(img, -drawW / 2, -drawH / 2, drawW, drawH);
ctx.restore();
}drawImageRaw function · javascript · L74-L79 (6 LOC)src/js/photoeditor.js
function drawImageRaw() {
if (!originalImage || !ctx) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.filter = 'none';
ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height);
}saveState function · javascript · L82-L94 (13 LOC)src/js/photoeditor.js
function saveState() {
undoStack.push({
adjustments: { ...adjustments },
rotation,
flipH,
flipV,
width: canvas.width,
height: canvas.height,
imageSrc: originalImage ? originalImage.src : null,
});
if (undoStack.length > 20) undoStack.shift();
redoStack = [];
}restoreState function · javascript · L97-L117 (21 LOC)src/js/photoeditor.js
function restoreState(state) {
adjustments = state.adjustments;
rotation = state.rotation;
flipH = state.flipH;
flipV = state.flipV;
canvas.width = state.width;
canvas.height = state.height;
if (state.imageSrc && state.imageSrc !== originalImage.src) {
const img = new Image();
img.onload = () => {
originalImage = img;
drawImage();
updateAdjustmentSliders();
};
img.src = state.imageSrc;
} else {
drawImage();
updateAdjustmentSliders();
}
}undo function · javascript · L120-L133 (14 LOC)src/js/photoeditor.js
function undo() {
if (undoStack.length === 0) {
Accessibility.announce('Nothing to undo');
return;
}
redoStack.push({
adjustments: { ...adjustments },
rotation, flipH, flipV,
width: canvas.width, height: canvas.height,
imageSrc: originalImage ? originalImage.src : null,
});
restoreState(undoStack.pop());
Accessibility.announce('Undone');
}redo function · javascript · L136-L149 (14 LOC)src/js/photoeditor.js
function redo() {
if (redoStack.length === 0) {
Accessibility.announce('Nothing to redo');
return;
}
undoStack.push({
adjustments: { ...adjustments },
rotation, flipH, flipV,
width: canvas.width, height: canvas.height,
imageSrc: originalImage ? originalImage.src : null,
});
restoreState(redoStack.pop());
Accessibility.announce('Redone');
}About: code-quality intelligence by Repobility · https://repobility.com
bakeCanvas function · javascript · L152-L179 (28 LOC)src/js/photoeditor.js
function bakeCanvas() {
return new Promise((resolve) => {
let dataURL;
try {
dataURL = canvas.toDataURL('image/png');
} catch (e) {
console.error('bakeCanvas: toDataURL failed:', e);
resolve(); // edits are already visible on canvas via putImageData
return;
}
const img = new Image();
img.onload = () => {
originalImage = img;
rotation = 0;
flipH = false;
flipV = false;
adjustments = { brightness: 0, contrast: 0, saturation: 0, sharpness: 0 };
updateAdjustmentSliders();
drawImage();
resolve();
};
img.onerror = () => {
console.error('bakeCanvas: image load failed');
resolve();
};
img.src = dataURL;
});
}rotateLeft function · javascript · L182-L195 (14 LOC)src/js/photoeditor.js
function rotateLeft() {
if (!originalImage) return;
saveState();
rotation = (rotation - 90 + 360) % 360;
if (rotation % 180 !== 0) {
canvas.width = originalImage.height;
canvas.height = originalImage.width;
} else {
canvas.width = originalImage.width;
canvas.height = originalImage.height;
}
drawImage();
Accessibility.announce(`Rotated left. Rotation: ${rotation} degrees`);
}rotateRight function · javascript · L198-L211 (14 LOC)src/js/photoeditor.js
function rotateRight() {
if (!originalImage) return;
saveState();
rotation = (rotation + 90) % 360;
if (rotation % 180 !== 0) {
canvas.width = originalImage.height;
canvas.height = originalImage.width;
} else {
canvas.width = originalImage.width;
canvas.height = originalImage.height;
}
drawImage();
Accessibility.announce(`Rotated right. Rotation: ${rotation} degrees`);
}