Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@
"@ai-sdk/svelte": "^1.1.24",
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@f21fc7f",
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@865e2fc",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@957a779",
"@appwrite.io/pink-legacy": "^1.0.3",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@865e2fc",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@957a779",
"@faker-js/faker": "^9.9.0",
"@neodrag/svelte": "^2.3.3",
"@popperjs/core": "^2.11.8",
"@sentry/sveltekit": "^8.38.0",
"@stripe/stripe-js": "^3.5.0",
Expand Down
32 changes: 22 additions & 10 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions src/lib/helpers/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,32 @@ export enum InvalidFileType {
EXTENSION = 'invalid_extension'
}

/**
* Check if a file is an image based on its MIME type
*/
export function isImageFile(mimeType: string | null | undefined): boolean {
if (!mimeType) return false;
return mimeType.startsWith('image/');
}

/**
* Check if a file is larger than the specified size threshold (in bytes)
*/
export function isLargeFile(fileSize: number, thresholdBytes: number = 1024 * 1024): boolean {
return fileSize > thresholdBytes;
}

/**
* Check if a file is a large image
*/
export function isLargeImage(
mimeType: string | null | undefined,
fileSize: number,
thresholdBytes: number = 1024 * 1024
): boolean {
return isImageFile(mimeType) && isLargeFile(fileSize, thresholdBytes);
}
Comment on lines +100 to +124
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if the new helper functions are used anywhere in the codebase
echo "Searching for usages of isImageFile, isLargeFile, isLargeImage..."
rg -n "isImageFile|isLargeFile|isLargeImage" --type=ts --type=svelte -g '!src/lib/helpers/files.ts'

Repository: appwrite/console

Length of output: 173


🏁 Script executed:

#!/bin/bash
# Check if the new helper functions are used anywhere in the codebase
echo "Searching for usages of isImageFile, isLargeFile, isLargeImage..."
rg -n "isImageFile|isLargeFile|isLargeImage" -g '!src/lib/helpers/files.ts' -g '*.ts' -g '*.tsx' -g '*.js' -g '*.jsx' -g '*.svelte'

Repository: appwrite/console

Length of output: 517


Remove unused helper functions or document their intended purpose.

Verification confirms these functions are exported but have zero external usages in the codebase—only internal usage within isLargeImage. While the implementation follows guidelines (pure functions, camelCase naming), exporting unused code creates maintenance burden.

Either remove isImageFile and isLargeFile if not needed, or add a TODO comment documenting their intended use in future features.

🤖 Prompt for AI Agents
In src/lib/helpers/files.ts around lines 100 to 124, the helper functions
isImageFile and isLargeFile are exported but have no external usages (only used
by isLargeImage); either remove the two unused exports and inline their logic
into isLargeImage (replace calls with direct checks and remove their export
declarations), or keep them and add a single-line TODO comment above each
function documenting intended future use (e.g., "TODO: exported for potential
reuse in X feature — remove if unused by Y date"); ensure exports/exports list
and imports elsewhere are updated accordingly so there are no dangling exports.


export const defaultIgnore = `
### Node ###
# Logs
Expand Down
123 changes: 123 additions & 0 deletions src/lib/helpers/imageTransformations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { ImageFormat, type Models } from '@appwrite.io/console';

export type TransformationState = {
width?: number;
height?: number;
gravity?: string; // focal point: 'top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right'
borderWidth?: number;
borderColor?: string; // hex without #
borderOpacity?: number; // 0-100
borderRadius?: number;
background?: string; // hex without #
quality?: number; // 1-100
output?: ImageFormat;
rotation?: number; // 0-360
};

export function generateTransformationParams(
state: TransformationState
): Record<string, string | number> {
const params: Record<string, string | number> = {};

if (state.width) params.width = state.width;
if (state.height) params.height = state.height;
if (state.gravity && state.gravity !== 'center') {
params.gravity = state.gravity;
}
if (state.borderWidth && state.borderWidth > 0) {
params.borderWidth = state.borderWidth;
if (state.borderColor) {
params.borderColor = state.borderColor.replace('#', '');
}
}
if (state.borderRadius && state.borderRadius > 0) {
params.borderRadius = state.borderRadius;
}
if (state.background) {
params.background = state.background.replace('#', '');
}
if (state.quality && state.quality < 100) {
params.quality = state.quality;
}
if (state.output) {
params.output = state.output;
}
if (state.rotation && state.rotation !== 0) {
params.rotation = state.rotation;
}

return params;
}

export function generateSDKCode(
state: TransformationState,
bucketId: string,
fileId: string,
sdk: 'js' | 'python' | 'flutter' | 'swift' | 'kotlin'
): string {
const params = generateTransformationParams(state);
const paramStrings: string[] = [];

// Build parameter object/string
Object.entries(params).forEach(([key, value]) => {
if (sdk === 'js' || sdk === 'python') {
paramStrings.push(` ${key}: ${typeof value === 'string' ? `'${value}'` : value}`);
} else if (sdk === 'flutter' || sdk === 'swift' || sdk === 'kotlin') {
paramStrings.push(` ${key}: ${typeof value === 'string' ? `"${value}"` : value}`);
}
});

const paramsBlock = paramStrings.length > 0 ? `,\n${paramStrings.join(',\n')}` : '';

switch (sdk) {
case 'js':
return `storage.getFilePreview({
bucketId: '${bucketId}',
fileId: '${fileId}'${paramsBlock}
});`;

case 'python':
return `storage.get_file_preview(
bucket_id='${bucketId}',
file_id='${fileId}'${paramsBlock.replace(/(\w+):/g, '$1=')}
);`;

case 'flutter':
return `Storage.getFilePreview(
bucketId: '${bucketId}',
fileId: '${fileId}'${paramsBlock}
);`;

case 'swift':
return `storage.getFilePreview(
bucketId: "${bucketId}",
fileId: "${fileId}"${paramsBlock}
)`;

case 'kotlin':
return `storage.getFilePreview(
bucketId = "${bucketId}",
fileId = "${fileId}"${paramsBlock.replace(/(\w+):/g, '$1 =')}
)`;

default:
return '';
}
}

export function getFormatLabel(format: ImageFormat): string {
switch (format) {
case ImageFormat.Jpg:
return 'JPG';
case ImageFormat.Png:
return 'PNG';
case ImageFormat.Gif:
return 'GIF';
case ImageFormat.Webp:
return 'WEBP';
case ImageFormat.Avif:
return 'AVIF';
default:
return 'JPG';
}
}
Loading