18 Commits

Author SHA1 Message Date
Emrik Östling
663b1d4171 Merge pull request #167 from C4illin/release-please--branches--main--components--convertx-frontend 2024-10-06 23:36:13 +02:00
Emrik Östling
c3067ca12d chore(main): release 0.8.1 2024-10-06 00:49:06 +02:00
Emrik Östling
4561ca3760 Merge pull request #164 from C4illin/fix/#163/add-jfif-support 2024-10-06 00:48:46 +02:00
Emrik Östling
698cce58ce Merge pull request #165 from C4illin/fix/#157/resize-when-converting-to-ico 2024-10-06 00:46:22 +02:00
C4illin
339b79f786 fix: treat jfif as jpeg
issue #163
2024-10-06 00:45:08 +02:00
C4illin
4f98f778f0 chore: add message when resizing image 2024-10-06 00:40:34 +02:00
Emrik Östling
8479b33a47 Merge pull request #166 from C4illin/fix/#151/disable-convert-button 2024-10-05 23:29:39 +02:00
C4illin
78844d7bd5 fix: disable convert button when input is empty
issue #151
2024-10-05 01:20:23 +02:00
Emrik Östling
64e4a271e1 Merge branch 'main' into fix/#157/resize-when-converting-to-ico 2024-10-05 01:02:48 +02:00
C4illin
5fb8c3575b chore: add eslint-plugin-readable-tailwind 2024-10-05 01:01:00 +02:00
C4illin
a6b8bcecae chore: disable dependency dashboard in Renovate configuration 2024-10-05 00:47:34 +02:00
C4illin
bc9c820820 chore: remove DeepSource configuration file 2024-10-05 00:45:34 +02:00
C4illin
ee9207a7f4 chore: fix eslint rules 2024-10-05 00:43:24 +02:00
C4illin
a34e215202 chore: remove biome 2024-10-05 00:01:39 +02:00
C4illin
b4e53dbb8e fix: resize to fit for ico
issue #157
2024-10-04 23:55:39 +02:00
C4illin
b5e8d82bfa chore(eslint): add browser globals to ESLint configuration 2024-10-04 23:44:18 +02:00
Emrik Östling
5d9000bb33 Merge pull request #162 from C4illin/renovate/biomejs-biome-1.x
chore(deps): update dependency @biomejs/biome to v1.9.3
2024-10-01 22:01:56 +02:00
renovate[bot]
ccb065ef0f chore(deps): update dependency @biomejs/biome to v1.9.3 2024-10-01 15:11:07 +00:00
21 changed files with 265 additions and 161 deletions

View File

@@ -1,7 +0,0 @@
version = 1
[[analyzers]]
name = "javascript"
[analyzers.meta]
environment = ["nodejs"]

View File

@@ -1,5 +1,14 @@
# Changelog # Changelog
## [0.8.1](https://github.com/C4illin/ConvertX/compare/v0.8.0...v0.8.1) (2024-10-05)
### Bug Fixes
* disable convert button when input is empty ([78844d7](https://github.com/C4illin/ConvertX/commit/78844d7bd55990789ed07c81e49043e688cbe656)), closes [#151](https://github.com/C4illin/ConvertX/issues/151)
* resize to fit for ico ([b4e53db](https://github.com/C4illin/ConvertX/commit/b4e53dbb8e70b3a95b44e5b756759d16117a87e1)), closes [#157](https://github.com/C4illin/ConvertX/issues/157)
* treat jfif as jpeg ([339b79f](https://github.com/C4illin/ConvertX/commit/339b79f786131deb93f0d5683e03178fdcab1ef5)), closes [#163](https://github.com/C4illin/ConvertX/issues/163)
## [0.8.0](https://github.com/C4illin/ConvertX/compare/v0.7.0...v0.8.0) (2024-09-30) ## [0.8.0](https://github.com/C4illin/ConvertX/compare/v0.7.0...v0.8.0) (2024-09-30)

BIN
bun.lockb

Binary file not shown.

View File

@@ -1,27 +1,23 @@
import comments from "@eslint-community/eslint-plugin-eslint-comments/configs";
import { fixupPluginRules } from "@eslint/compat"; import { fixupPluginRules } from "@eslint/compat";
import js from "@eslint/js"; import eslint from "@eslint/js";
import deprecationPlugin from "eslint-plugin-deprecation"; import deprecationPlugin from "eslint-plugin-deprecation";
import importPlugin from "eslint-plugin-import"; import eslintPluginReadableTailwind from "eslint-plugin-readable-tailwind";
import simpleImportSortPlugin from "eslint-plugin-simple-import-sort"; import simpleImportSortPlugin from "eslint-plugin-simple-import-sort";
import tailwind from "eslint-plugin-tailwindcss"; import tailwind from "eslint-plugin-tailwindcss";
import globals from "globals"; import globals from "globals";
import tseslint from "typescript-eslint"; import tseslint from "typescript-eslint";
export default tseslint.config( export default tseslint.config(
js.configs.recommended, eslint.configs.recommended,
importPlugin.flatConfigs.recommended,
comments.recommended,
...tseslint.configs.recommended, ...tseslint.configs.recommended,
...tailwind.configs["flat/recommended"], ...tailwind.configs["flat/recommended"],
{ {
plugins: { plugins: {
"@typescript-eslint": tseslint.plugin,
deprecation: fixupPluginRules(deprecationPlugin), deprecation: fixupPluginRules(deprecationPlugin),
import: fixupPluginRules(importPlugin),
"simple-import-sort": simpleImportSortPlugin, "simple-import-sort": simpleImportSortPlugin,
"readable-tailwind": eslintPluginReadableTailwind,
}, },
ignores: ["**/node_modules/**", "**/public/**"], ignores: ["**/node_modules/**"],
languageOptions: { languageOptions: {
parserOptions: { parserOptions: {
projectService: true, projectService: true,
@@ -32,14 +28,23 @@ export default tseslint.config(
}, },
globals: { globals: {
...globals.node, ...globals.node,
...globals.browser,
}, },
}, },
files: ["**/*.{js,mjs,cjs}"], files: ["**/*.{js,mjs,cjs,tsx,ts}"],
rules: { rules: {
"tailwindcss/no-custom-classname": [ ...eslintPluginReadableTailwind.configs.warning.rules,
"error", "tailwindcss/classnames-order": "off",
"readable-tailwind/multiline": [
"warn",
{
group: "newLine",
printWidth: 100,
},
],
"tailwindcss/no-custom-classname": [
"warn",
{ {
config: "./tailwind.config.js",
whitelist: [ whitelist: [
"select_container", "select_container",
"convert_to_popup", "convert_to_popup",
@@ -49,7 +54,6 @@ export default tseslint.config(
], ],
}, },
], ],
"import/no-named-as-default": "off",
}, },
}, },
); );

View File

@@ -1,15 +1,15 @@
{ {
"name": "convertx-frontend", "name": "convertx-frontend",
"version": "0.8.0", "version": "0.8.1",
"scripts": { "scripts": {
"dev": "bun run --watch src/index.tsx", "dev": "bun run --watch src/index.tsx",
"hot": "bun run --hot src/index.tsx", "hot": "bun run --hot src/index.tsx",
"format": "biome format --write ./src", "format": "eslint --fix .",
"build": "postcss ./src/main.css -o ./src/public/generated.css", "build": "postcss ./src/main.css -o ./src/public/generated.css",
"lint": "run-p 'lint:*'", "lint": "run-p 'lint:*'",
"lint:tsc": "tsc --noEmit", "lint:tsc": "tsc --noEmit",
"lint:knip": "knip", "lint:knip": "knip",
"lint:biome": "biome lint --error-on-warnings ./src" "lint:eslint": "eslint ."
}, },
"dependencies": { "dependencies": {
"@elysiajs/cookie": "^0.8.0", "@elysiajs/cookie": "^0.8.0",
@@ -24,13 +24,10 @@
"start": "bun run src/index.tsx" "start": "bun run src/index.tsx"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "1.9.2",
"@eslint-community/eslint-plugin-eslint-comments": "^4.4.0",
"@eslint/compat": "^1.1.1", "@eslint/compat": "^1.1.1",
"@eslint/js": "^9.11.1", "@eslint/js": "^9.12.0",
"@ianvs/prettier-plugin-sort-imports": "^4.3.1", "@ianvs/prettier-plugin-sort-imports": "^4.3.1",
"@kitajs/ts-html-plugin": "^4.1.0", "@kitajs/ts-html-plugin": "^4.1.0",
"@picocss/pico": "^2.0.6",
"@total-typescript/ts-reset": "^0.6.1", "@total-typescript/ts-reset": "^0.6.1",
"@types/bun": "^1.1.10", "@types/bun": "^1.1.10",
"@types/eslint": "^9.6.1", "@types/eslint": "^9.6.1",
@@ -41,12 +38,12 @@
"@typescript-eslint/parser": "^8.7.0", "@typescript-eslint/parser": "^8.7.0",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"cssnano": "^7.0.6", "cssnano": "^7.0.6",
"eslint": "^9.11.1", "eslint": "^9.12.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-deprecation": "^3.0.0", "eslint-plugin-deprecation": "^3.0.0",
"eslint-plugin-import": "^2.30.0",
"eslint-plugin-isaacscript": "^4.0.0", "eslint-plugin-isaacscript": "^4.0.0",
"eslint-plugin-prettier": "^5.2.1", "eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-readable-tailwind": "^1.8.1",
"eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-tailwindcss": "^3.17.4", "eslint-plugin-tailwindcss": "^3.17.4",
"globals": "^15.9.0", "globals": "^15.9.0",
@@ -59,9 +56,6 @@
"tailwind-scrollbar": "^3.1.0", "tailwind-scrollbar": "^3.1.0",
"tailwindcss": "^3.4.13", "tailwindcss": "^3.4.13",
"typescript": "^5.6.2", "typescript": "^5.6.2",
"typescript-eslint": "^8.7.0" "typescript-eslint": "^8.8.0"
}, }
"trustedDependencies": [
"@biomejs/biome"
]
} }

View File

@@ -1,9 +1,9 @@
// eslint-disable-next-line no-undef
module.exports = { module.exports = {
plugins: { plugins: {
tailwindcss: {}, tailwindcss: {},
autoprefixer: {}, autoprefixer: {},
// eslint-disable-next-line no-undef
...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}) ...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {})
} }
} }

View File

@@ -1,6 +1,7 @@
{ {
"$schema": "https://docs.renovatebot.com/renovate-schema.json", "$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [ "extends": [
"config:recommended" "config:recommended",
":disableDependencyDashboard"
] ]
} }

View File

@@ -5,17 +5,23 @@ export const Header = ({
let rightNav: JSX.Element; let rightNav: JSX.Element;
if (loggedIn) { if (loggedIn) {
rightNav = ( rightNav = (
<ul class="flex gap-4 "> <ul class="flex gap-4">
<li> <li>
<a <a
class="text-accent-600 transition-all hover:text-accent-500 hover:underline" class={`
text-accent-600 transition-all
hover:text-accent-500 hover:underline
`}
href="/history"> href="/history">
History History
</a> </a>
</li> </li>
<li> <li>
<a <a
class="text-accent-600 transition-all hover:text-accent-500 hover:underline" class={`
text-accent-600 transition-all
hover:text-accent-500 hover:underline
`}
href="/logoff"> href="/logoff">
Logout Logout
</a> </a>
@@ -27,7 +33,10 @@ export const Header = ({
<ul class="flex gap-4"> <ul class="flex gap-4">
<li> <li>
<a <a
class="text-accent-600 transition-all hover:text-accent-500 hover:underline" class={`
text-accent-600 transition-all
hover:text-accent-500 hover:underline
`}
href="/login"> href="/login">
Login Login
</a> </a>
@@ -35,7 +44,10 @@ export const Header = ({
{accountRegistration ? ( {accountRegistration ? (
<li> <li>
<a <a
class="text-accent-600 transition-all hover:text-accent-500 hover:underline" class={`
text-accent-600 transition-all
hover:text-accent-500 hover:underline
`}
href="/register"> href="/register">
Register Register
</a> </a>

View File

@@ -116,8 +116,8 @@ export async function convert(
fileType: string, fileType: string,
convertTo: string, convertTo: string,
targetPath: string, targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation> // eslint-disable-next-line @typescript-eslint/no-unused-vars
options?: any, options?: unknown,
): Promise<string> { ): Promise<string> {
// let command = "ffmpeg"; // let command = "ffmpeg";
@@ -137,7 +137,7 @@ export async function convert(
console.error(`stderr: ${stderr}`); console.error(`stderr: ${stderr}`);
} }
resolve("success"); resolve("Done");
}); });
}); });
} }

View File

@@ -1,6 +1,5 @@
import { exec } from "node:child_process"; import { exec } from "node:child_process";
// This could be done dynamically by running `ffmpeg -formats` and parsing the output // This could be done dynamically by running `ffmpeg -formats` and parsing the output
export const properties = { export const properties = {
from: { from: {
@@ -689,10 +688,19 @@ export async function convert(
fileType: string, fileType: string,
convertTo: string, convertTo: string,
targetPath: string, targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation> // eslint-disable-next-line @typescript-eslint/no-unused-vars
options?: any, options?: unknown,
): Promise<string> { ): Promise<string> {
const command = `ffmpeg -i "${filePath}" "${targetPath}"`; let extra = "";
let message = "Done";
if (convertTo === "ico") {
// make sure image is 256x256 or smaller
extra = `-filter:v "scale='min(256,iw)':min'(256,ih)':force_original_aspect_ratio=decrease"`;
message = "Done: resized to 256x256";
}
const command = `ffmpeg -i "${filePath}" ${extra} "${targetPath}"`;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
exec(command, (error, stdout, stderr) => { exec(command, (error, stdout, stderr) => {
@@ -708,7 +716,7 @@ export async function convert(
console.error(`stderr: ${stderr}`); console.error(`stderr: ${stderr}`);
} }
resolve("success"); resolve(message);
}); });
}); });
} }

View File

@@ -313,8 +313,8 @@ export function convert(
fileType: string, fileType: string,
convertTo: string, convertTo: string,
targetPath: string, targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation> // eslint-disable-next-line @typescript-eslint/no-unused-vars
options?: any, options?: unknown,
): Promise<string> { ): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
exec( exec(
@@ -332,7 +332,7 @@ export function convert(
console.error(`stderr: ${stderr}`); console.error(`stderr: ${stderr}`);
} }
resolve("success"); resolve("Done");
}, },
); );
}); });

View File

@@ -39,8 +39,8 @@ export function convert(
fileType: string, fileType: string,
convertTo: string, convertTo: string,
targetPath: string, targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation> // eslint-disable-next-line @typescript-eslint/no-unused-vars
options?: any, options?: unknown,
): Promise<string> { ): Promise<string> {
let tool = ""; let tool = "";
if (fileType === "jxl") { if (fileType === "jxl") {
@@ -65,7 +65,7 @@ export function convert(
console.error(`stderr: ${stderr}`); console.error(`stderr: ${stderr}`);
} }
resolve("success"); resolve("Done");
}); });
}); });
} }

View File

@@ -1,67 +1,44 @@
import {
convert as convertImage,
properties as propertiesImage
} from "./vips";
import {
convert as convertPandoc,
properties as propertiesPandoc,
} from "./pandoc";
import {
convert as convertFFmpeg,
properties as propertiesFFmpeg,
} from "./ffmpeg";
import {
convert as convertGraphicsmagick,
properties as propertiesGraphicsmagick,
} from "./graphicsmagick";
import {
convert as convertxelatex,
properties as propertiesxelatex,
} from "./xelatex";
import {
convert as convertLibjxl,
properties as propertiesLibjxl,
} from "./libjxl";
import {
convert as convertresvg,
properties as propertiesresvg,
} from "./resvg";
import {
convert as convertassimp,
properties as propertiesassimp,
} from "./assimp";
import { normalizeFiletype } from "../helpers/normalizeFiletype"; import { normalizeFiletype } from "../helpers/normalizeFiletype";
import { convert as convertassimp, properties as propertiesassimp } from "./assimp";
import { convert as convertFFmpeg, properties as propertiesFFmpeg } from "./ffmpeg";
import { convert as convertGraphicsmagick, properties as propertiesGraphicsmagick } from "./graphicsmagick";
import { convert as convertLibjxl, properties as propertiesLibjxl } from "./libjxl";
import { convert as convertPandoc, properties as propertiesPandoc } from "./pandoc";
import { convert as convertresvg, properties as propertiesresvg } from "./resvg";
import { convert as convertImage, properties as propertiesImage } from "./vips";
import { convert as convertxelatex, properties as propertiesxelatex } from "./xelatex";
// This should probably be reconstructed so that the functions are not imported instead the functions hook into this to make the converters more modular // This should probably be reconstructed so that the functions are not imported instead the functions hook into this to make the converters more modular
const properties: Record<string, { const properties: Record<
string,
{
properties: { properties: {
from: Record<string, string[]>; from: Record<string, string[]>;
to: Record<string, string[]>; to: Record<string, string[]>;
options?: Record<string, Record<string, { options?: Record<
string,
Record<
string,
{
description: string; description: string;
type: string; type: string;
default: number; default: number;
}>>; }
>
>;
}; };
converter: ( converter: (
filePath: string, filePath: string,
fileType: string, fileType: string,
convertTo: string, convertTo: string,
targetPath: string, targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
options?: any, options?: unknown,
// biome-ignore lint/suspicious/noExplicitAny: <explanation> ) => unknown;
) => any; }
}> = { > = {
libjxl: { libjxl: {
properties: propertiesLibjxl, properties: propertiesLibjxl,
converter: convertLibjxl, converter: convertLibjxl,
@@ -99,24 +76,19 @@ const properties: Record<string, {
export async function mainConverter( export async function mainConverter(
inputFilePath: string, inputFilePath: string,
fileTypeOriginal: string, fileTypeOriginal: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation> convertTo: string,
convertTo: any,
targetPath: string, targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation> options?: unknown,
options?: any,
converterName?: string, converterName?: string,
) { ) {
const fileType = normalizeFiletype(fileTypeOriginal); const fileType = normalizeFiletype(fileTypeOriginal);
// biome-ignore lint/suspicious/noExplicitAny: <explanation> let converterFunc: typeof properties["libjxl"]["converter"] | undefined;
let converterFunc: any;
// let converterName = converterName;
if (converterName) { if (converterName) {
converterFunc = properties[converterName]?.converter; converterFunc = properties[converterName]?.converter;
} else { } else {
// Iterate over each converter in properties // Iterate over each converter in properties
// biome-ignore lint/style/noParameterAssign: <explanation>
for (converterName in properties) { for (converterName in properties) {
const converterObj = properties[converterName]; const converterObj = properties[converterName];
@@ -144,7 +116,7 @@ export async function mainConverter(
} }
try { try {
await converterFunc( const result = await converterFunc(
inputFilePath, inputFilePath,
fileType, fileType,
convertTo, convertTo,
@@ -154,7 +126,13 @@ export async function mainConverter(
console.log( console.log(
`Converted ${inputFilePath} from ${fileType} to ${convertTo} successfully using ${converterName}.`, `Converted ${inputFilePath} from ${fileType} to ${convertTo} successfully using ${converterName}.`,
result,
); );
if (typeof result === "string") {
return result;
}
return "Done"; return "Done";
} catch (error) { } catch (error) {
console.error( console.error(
@@ -190,9 +168,7 @@ for (const converterName in properties) {
} }
} }
export const getPossibleTargets = ( export const getPossibleTargets = (from: string): Record<string, string[]> => {
from: string,
): Record<string, string[]> => {
const fromClean = normalizeFiletype(from); const fromClean = normalizeFiletype(from);
return possibleTargets[fromClean] || {}; return possibleTargets[fromClean] || {};
@@ -216,6 +192,7 @@ for (const converterName in properties) {
} }
possibleInputs.sort(); possibleInputs.sort();
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const getPossibleInputs = () => { const getPossibleInputs = () => {
return possibleInputs; return possibleInputs;
}; };
@@ -287,4 +264,4 @@ export const getAllInputs = (converter: string) => {
// } // }
// // print the number of unique Inputs and Outputs // // print the number of unique Inputs and Outputs
// console.log(`Unique Formats: ${uniqueFormats.size}`); // console.log(`Unique Formats: ${uniqueFormats.size}`);

View File

@@ -124,8 +124,8 @@ export function convert(
fileType: string, fileType: string,
convertTo: string, convertTo: string,
targetPath: string, targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation> // eslint-disable-next-line @typescript-eslint/no-unused-vars
options?: any, options?: unknown,
): Promise<string> { ): Promise<string> {
// set xelatex here // set xelatex here
const xelatex = ["pdf", "latex"]; const xelatex = ["pdf", "latex"];
@@ -149,7 +149,7 @@ export function convert(
console.error(`stderr: ${stderr}`); console.error(`stderr: ${stderr}`);
} }
resolve("success"); resolve("Done");
}, },
); );
}); });

View File

@@ -14,8 +14,8 @@ export function convert(
fileType: string, fileType: string,
convertTo: string, convertTo: string,
targetPath: string, targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation> // eslint-disable-next-line @typescript-eslint/no-unused-vars
options?: any, options?: unknown,
): Promise<string> { ): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
exec(`resvg "${filePath}" "${targetPath}"`, (error, stdout, stderr) => { exec(`resvg "${filePath}" "${targetPath}"`, (error, stdout, stderr) => {
@@ -31,7 +31,7 @@ export function convert(
console.error(`stderr: ${stderr}`); console.error(`stderr: ${stderr}`);
} }
resolve("success"); resolve("Done");
}); });
}); });
} }

View File

@@ -1,5 +1,6 @@
import { exec } from "node:child_process"; import { exec } from "node:child_process";
// declare possible conversions // declare possible conversions
export const properties = { export const properties = {
from: { from: {
@@ -94,8 +95,8 @@ export function convert(
fileType: string, fileType: string,
convertTo: string, convertTo: string,
targetPath: string, targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation> // eslint-disable-next-line @typescript-eslint/no-unused-vars
options?: any, options?: unknown,
): Promise<string> { ): Promise<string> {
// if (fileType === "svg") { // if (fileType === "svg") {
// const scale = options.scale || 1; // const scale = options.scale || 1;
@@ -134,8 +135,8 @@ export function convert(
console.error(`stderr: ${stderr}`); console.error(`stderr: ${stderr}`);
} }
resolve("success"); resolve("Done");
}, },
); );
}); });
} }

View File

@@ -14,8 +14,8 @@ export function convert(
fileType: string, fileType: string,
convertTo: string, convertTo: string,
targetPath: string, targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation> // eslint-disable-next-line @typescript-eslint/no-unused-vars
options?: any, options?: unknown,
): Promise<string> { ): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// const fileName: string = (targetPath.split("/").pop() as string).replace(".pdf", "") // const fileName: string = (targetPath.split("/").pop() as string).replace(".pdf", "")
@@ -39,7 +39,7 @@ export function convert(
console.error(`stderr: ${stderr}`); console.error(`stderr: ${stderr}`);
} }
resolve("success"); resolve("Done");
}, },
); );
}); });

View File

@@ -2,6 +2,7 @@ export const normalizeFiletype = (filetype: string): string => {
const lowercaseFiletype = filetype.toLowerCase(); const lowercaseFiletype = filetype.toLowerCase();
switch (lowercaseFiletype) { switch (lowercaseFiletype) {
case "jfif":
case "jpg": case "jpg":
return "jpeg"; return "jpeg";
case "htm": case "htm":

View File

@@ -141,7 +141,9 @@ const app = new Elysia({
<main class="mx-auto w-full max-w-4xl px-4"> <main class="mx-auto w-full max-w-4xl px-4">
<h1 class="my-8 text-3xl">Welcome to ConvertX!</h1> <h1 class="my-8 text-3xl">Welcome to ConvertX!</h1>
<article class="article p-0"> <article class="article p-0">
<header class="w-full bg-neutral-800 p-4">Create your account</header> <header class="w-full bg-neutral-800 p-4">
Create your account
</header>
<form method="post" action="/register" class="p-4"> <form method="post" action="/register" class="p-4">
<fieldset class="mb-4 flex flex-col gap-4"> <fieldset class="mb-4 flex flex-col gap-4">
<label class="flex flex-col gap-1"> <label class="flex flex-col gap-1">
@@ -172,7 +174,10 @@ const app = new Elysia({
<footer class="p-4"> <footer class="p-4">
Report any issues on{" "} Report any issues on{" "}
<a <a
class="text-accent-500 underline hover:text-accent-400" class={`
text-accent-500 underline
hover:text-accent-400
`}
href="https://github.com/C4illin/ConvertX" href="https://github.com/C4illin/ConvertX"
> >
GitHub GitHub
@@ -528,12 +533,21 @@ const app = new Elysia({
<div class="mb-4 max-h-[50vh] overflow-y-auto scrollbar-thin"> <div class="mb-4 max-h-[50vh] overflow-y-auto scrollbar-thin">
<table <table
id="file-list" id="file-list"
class="w-full table-auto rounded bg-neutral-900 [&_td]:p-4 [&_tr]:rounded [&_tr]:border-b [&_tr]:border-neutral-800" class={`
w-full table-auto rounded bg-neutral-900
[&_td]:p-4
[&_tr]:rounded [&_tr]:border-b [&_tr]:border-neutral-800
`}
/> />
</div> </div>
<div <div
id="dropzone" id="dropzone"
class="relative flex h-48 w-full items-center justify-center rounded border border-dashed border-neutral-700 transition-all hover:border-neutral-600 [&.dragover]:border-4 [&.dragover]:border-neutral-500" class={`
relative flex h-48 w-full items-center justify-center rounded border border-dashed
border-neutral-700 transition-all
[&.dragover]:border-4 [&.dragover]:border-neutral-500
hover:border-neutral-600
`}
> >
<span> <span>
<b>Choose a file</b> or drag it here <b>Choose a file</b> or drag it here
@@ -561,11 +575,19 @@ const app = new Elysia({
class="w-full rounded bg-neutral-800 p-4" class="w-full rounded bg-neutral-800 p-4"
/> />
<div class="select_container relative"> <div class="select_container relative">
<article class="convert_to_popup absolute z-[2] m-0 hidden h-[30vh] max-h-[50vh] w-full flex-col overflow-y-auto overflow-x-hidden rounded bg-neutral-800 sm:h-[30vh]"> <article
class={`
convert_to_popup absolute z-[2] m-0 hidden h-[30vh] max-h-[50vh] w-full
flex-col overflow-y-auto overflow-x-hidden rounded bg-neutral-800
sm:h-[30vh]
`}
>
{Object.entries(getAllTargets()).map( {Object.entries(getAllTargets()).map(
([converter, targets]) => ( ([converter, targets]) => (
<article <article
class="convert_to_group w-full border-b border-neutral-700 p-4 flex flex-col" class={`
convert_to_group flex w-full flex-col border-b border-neutral-700 p-4
`}
data-converter={converter} data-converter={converter}
> >
<header class="mb-2 w-full text-xl font-bold" safe> <header class="mb-2 w-full text-xl font-bold" safe>
@@ -576,7 +598,10 @@ const app = new Elysia({
<button <button
// https://stackoverflow.com/questions/121499/when-a-blur-event-occurs-how-can-i-find-out-which-element-focus-went-to#comment82388679_33325953 // https://stackoverflow.com/questions/121499/when-a-blur-event-occurs-how-can-i-find-out-which-element-focus-went-to#comment82388679_33325953
tabindex={0} tabindex={0}
class="target rounded bg-neutral-700 p-1 text-base hover:bg-neutral-600" class={`
target rounded bg-neutral-700 p-1 text-base
hover:bg-neutral-600
`}
data-value={`${target},${converter}`} data-value={`${target},${converter}`}
data-target={target} data-target={target}
data-converter={converter} data-converter={converter}
@@ -616,7 +641,15 @@ const app = new Elysia({
</select> </select>
</div> </div>
</article> </article>
<input class="btn-primary w-full" type="submit" value="Convert" /> <input
class={`
btn-primary w-full
disabled:cursor-not-allowed disabled:opacity-50
`}
type="submit"
value="Convert"
disabled
/>
</form> </form>
</main> </main>
<script src="script.js" defer /> <script src="script.js" defer />
@@ -629,11 +662,17 @@ const app = new Elysia({
({ body }) => { ({ body }) => {
return ( return (
<> <>
<article class="convert_to_popup absolute z-[2] m-0 hidden h-[50vh] max-h-[50vh] w-full flex-col overflow-y-auto overflow-x-hidden rounded bg-neutral-800 sm:h-[30vh]"> <article
class={`
convert_to_popup absolute z-[2] m-0 hidden h-[50vh] max-h-[50vh] w-full flex-col
overflow-y-auto overflow-x-hidden rounded bg-neutral-800
sm:h-[30vh]
`}
>
{Object.entries(getPossibleTargets(body.fileType)).map( {Object.entries(getPossibleTargets(body.fileType)).map(
([converter, targets]) => ( ([converter, targets]) => (
<article <article
class="convert_to_group w-full border-b border-neutral-700 p-4 flex flex-col" class="convert_to_group flex w-full flex-col border-b border-neutral-700 p-4"
data-converter={converter} data-converter={converter}
> >
<header class="mb-2 w-full text-xl font-bold" safe> <header class="mb-2 w-full text-xl font-bold" safe>
@@ -644,7 +683,10 @@ const app = new Elysia({
<button <button
// https://stackoverflow.com/questions/121499/when-a-blur-event-occurs-how-can-i-find-out-which-element-focus-went-to#comment82388679_33325953 // https://stackoverflow.com/questions/121499/when-a-blur-event-occurs-how-can-i-find-out-which-element-focus-went-to#comment82388679_33325953
tabindex={0} tabindex={0}
class="target rounded bg-neutral-700 p-1 text-base hover:bg-neutral-600" class={`
target rounded bg-neutral-700 p-1 text-base
hover:bg-neutral-600
`}
data-value={`${target},${converter}`} data-value={`${target},${converter}`}
data-target={target} data-target={target}
data-converter={converter} data-converter={converter}
@@ -713,7 +755,6 @@ const app = new Elysia({
await Bun.write(`${userUploadsDir}${file.name}`, file); await Bun.write(`${userUploadsDir}${file.name}`, file);
} }
} else { } else {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions, @typescript-eslint/dot-notation
await Bun.write(`${userUploadsDir}${body.file["name"]}`, body.file); await Bun.write(`${userUploadsDir}${body.file["name"]}`, body.file);
} }
} }
@@ -893,7 +934,13 @@ const app = new Elysia({
<main class="w-full px-4"> <main class="w-full px-4">
<article class="article"> <article class="article">
<h1 class="mb-4 text-xl">Results</h1> <h1 class="mb-4 text-xl">Results</h1>
<table class="w-full table-auto rounded bg-neutral-900 text-left [&_td]:p-4 [&_tr]:rounded [&_tr]:border-b [&_tr]:border-neutral-800"> <table
class={`
w-full table-auto rounded bg-neutral-900 text-left
[&_td]:p-4
[&_tr]:rounded [&_tr]:border-b [&_tr]:border-neutral-800
`}
>
<thead> <thead>
<tr> <tr>
<th class="px-4 py-2">Time</th> <th class="px-4 py-2">Time</th>
@@ -912,7 +959,10 @@ const app = new Elysia({
<td safe>{job.status}</td> <td safe>{job.status}</td>
<td> <td>
<a <a
class="text-accent-500 underline hover:text-accent-400" class={`
text-accent-500 underline
hover:text-accent-400
`}
href={`/results/${job.id}`} href={`/results/${job.id}`}
> >
View View
@@ -990,9 +1040,22 @@ const app = new Elysia({
<progress <progress
max={job.num_files} max={job.num_files}
value={files.length} value={files.length}
class="mb-4 inline-block h-2 w-full appearance-none overflow-hidden rounded-full border-0 bg-neutral-700 bg-none text-accent-500 accent-accent-500 [&::-moz-progress-bar]:bg-neutral-700 [&::-webkit-progress-value]:rounded-full [&::-webkit-progress-value]:[background:none] [&[value]::-webkit-progress-value]:bg-accent-500 [&[value]::-webkit-progress-value]:transition-[inline-size]" class={`
mb-4 inline-block h-2 w-full appearance-none overflow-hidden rounded-full
border-0 bg-neutral-700 bg-none text-accent-500 accent-accent-500
[&::-moz-progress-bar]:bg-neutral-700 [&::-webkit-progress-value]:rounded-full
[&::-webkit-progress-value]:[background:none]
[&[value]::-webkit-progress-value]:bg-accent-500
[&[value]::-webkit-progress-value]:transition-[inline-size]
`}
/> />
<table class="w-full table-auto rounded bg-neutral-900 text-left [&_td]:p-4 [&_tr]:rounded [&_tr]:border-b [&_tr]:border-neutral-800"> <table
class={`
w-full table-auto rounded bg-neutral-900 text-left
[&_td]:p-4
[&_tr]:rounded [&_tr]:border-b [&_tr]:border-neutral-800
`}
>
<thead> <thead>
<tr> <tr>
<th class="px-4 py-2">Converted File Name</th> <th class="px-4 py-2">Converted File Name</th>
@@ -1008,7 +1071,10 @@ const app = new Elysia({
<td safe>{file.status}</td> <td safe>{file.status}</td>
<td> <td>
<a <a
class="text-accent-500 underline hover:text-accent-400" class={`
text-accent-500 underline
hover:text-accent-400
`}
href={`/download/${outputPath}${file.output_file_name}`} href={`/download/${outputPath}${file.output_file_name}`}
> >
View View
@@ -1016,7 +1082,10 @@ const app = new Elysia({
</td> </td>
<td> <td>
<a <a
class="text-accent-500 underline hover:text-accent-400" class={`
text-accent-500 underline
hover:text-accent-400
`}
href={`/download/${outputPath}${file.output_file_name}`} href={`/download/${outputPath}${file.output_file_name}`}
download={file.output_file_name} download={file.output_file_name}
> >
@@ -1093,9 +1162,22 @@ const app = new Elysia({
<progress <progress
max={job.num_files} max={job.num_files}
value={files.length} value={files.length}
class="mb-4 inline-block h-2 w-full appearance-none overflow-hidden rounded-full border-0 bg-neutral-700 bg-none text-accent-500 accent-accent-500 [&::-moz-progress-bar]:bg-neutral-700 [&::-webkit-progress-value]:rounded-full [&::-webkit-progress-value]:[background:none] [&[value]::-webkit-progress-value]:bg-accent-500 [&[value]::-webkit-progress-value]:transition-[inline-size]" class={`
mb-4 inline-block h-2 w-full appearance-none overflow-hidden rounded-full border-0
bg-neutral-700 bg-none text-accent-500 accent-accent-500
[&::-moz-progress-bar]:bg-neutral-700 [&::-webkit-progress-value]:rounded-full
[&::-webkit-progress-value]:[background:none]
[&[value]::-webkit-progress-value]:bg-accent-500
[&[value]::-webkit-progress-value]:transition-[inline-size]
`}
/> />
<table class="w-full table-auto rounded bg-neutral-900 text-left [&_td]:p-4 [&_tr]:rounded [&_tr]:border-b [&_tr]:border-neutral-800"> <table
class={`
w-full table-auto rounded bg-neutral-900 text-left
[&_td]:p-4
[&_tr]:rounded [&_tr]:border-b [&_tr]:border-neutral-800
`}
>
<thead> <thead>
<tr> <tr>
<th class="px-4 py-2">Converted File Name</th> <th class="px-4 py-2">Converted File Name</th>
@@ -1111,7 +1193,10 @@ const app = new Elysia({
<td safe>{file.status}</td> <td safe>{file.status}</td>
<td> <td>
<a <a
class="text-accent-500 underline hover:text-accent-400" class={`
text-accent-500 underline
hover:text-accent-400
`}
href={`/download/${outputPath}${file.output_file_name}`} href={`/download/${outputPath}${file.output_file_name}`}
> >
View View
@@ -1119,7 +1204,10 @@ const app = new Elysia({
</td> </td>
<td> <td>
<a <a
class="text-accent-500 underline hover:text-accent-400" class={`
text-accent-500 underline
hover:text-accent-400
`}
href={`/download/${outputPath}${file.output_file_name}`} href={`/download/${outputPath}${file.output_file_name}`}
download={file.output_file_name} download={file.output_file_name}
> >
@@ -1179,7 +1267,14 @@ const app = new Elysia({
<main class="w-full px-4"> <main class="w-full px-4">
<article class="article"> <article class="article">
<h1 class="mb-4 text-xl">Converters</h1> <h1 class="mb-4 text-xl">Converters</h1>
<table class="w-full table-auto rounded bg-neutral-900 text-left [&_td]:p-4 [&_tr]:rounded [&_tr]:border-b [&_tr]:border-neutral-800 [&_ul]:list-inside [&_ul]:list-disc"> <table
class={`
w-full table-auto rounded bg-neutral-900 text-left
[&_td]:p-4
[&_tr]:rounded [&_tr]:border-b [&_tr]:border-neutral-800
[&_ul]:list-inside [&_ul]:list-disc
`}
>
<thead> <thead>
<tr> <tr>
<th class="mx-4 my-2">Converter</th> <th class="mx-4 my-2">Converter</th>

View File

@@ -4,11 +4,11 @@ const dropZone = document.getElementById("dropzone");
const fileNames = []; const fileNames = [];
let fileType; let fileType;
dropZone.addEventListener("dragover", (e) => { dropZone.addEventListener("dragover", () => {
dropZone.classList.add("dragover"); dropZone.classList.add("dragover");
}); });
dropZone.addEventListener("dragleave", (e) => { dropZone.addEventListener("dragleave", () => {
dropZone.classList.remove("dragover"); dropZone.classList.remove("dragover");
}); });
@@ -22,6 +22,7 @@ const updateSearchBar = () => {
const convertToGroupElements = document.querySelectorAll(".convert_to_group"); const convertToGroupElements = document.querySelectorAll(".convert_to_group");
const convertToGroups = {}; const convertToGroups = {};
const convertToElement = document.querySelector("select[name='convert_to']"); const convertToElement = document.querySelector("select[name='convert_to']");
const convertButton = document.querySelector("input[type='submit']");
const showMatching = (search) => { const showMatching = (search) => {
for (const [targets, groupElement] of Object.values(convertToGroups)) { for (const [targets, groupElement] of Object.values(convertToGroups)) {
@@ -57,6 +58,7 @@ const updateSearchBar = () => {
target.onmousedown = () => { target.onmousedown = () => {
convertToElement.value = target.dataset.value; convertToElement.value = target.dataset.value;
convertToInput.value = `${target.dataset.target} using ${target.dataset.converter}`; convertToInput.value = `${target.dataset.target} using ${target.dataset.converter}`;
convertButton.disabled = false;
showMatching(""); showMatching("");
}; };
} }
@@ -68,6 +70,11 @@ const updateSearchBar = () => {
showMatching(e.target.value.toLowerCase()); showMatching(e.target.value.toLowerCase());
}); });
convertToInput.addEventListener("search", () => {
// when the user clears the search bar using the 'x' button
convertButton.disabled = true;
});
convertToInput.addEventListener("blur", (e) => { convertToInput.addEventListener("blur", (e) => {
// Keep the popup open even when clicking on a target button // Keep the popup open even when clicking on a target button
// for a split second to allow the click to go through // for a split second to allow the click to go through
@@ -153,6 +160,7 @@ const setTitle = () => {
}; };
// Add a onclick for the delete button // Add a onclick for the delete button
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const deleteRow = (target) => { const deleteRow = (target) => {
const filename = target.parentElement.parentElement.children[0].textContent; const filename = target.parentElement.parentElement.children[0].textContent;
const row = target.parentElement.parentElement; const row = target.parentElement.parentElement;
@@ -203,7 +211,7 @@ const uploadFiles = (files) => {
const formConvert = document.querySelector("form[action='/convert']"); const formConvert = document.querySelector("form[action='/convert']");
formConvert.addEventListener("submit", (e) => { formConvert.addEventListener("submit", () => {
const hiddenInput = document.querySelector("input[name='file_names']"); const hiddenInput = document.querySelector("input[name='file_names']");
hiddenInput.value = JSON.stringify(fileNames); hiddenInput.value = JSON.stringify(fileNames);
}); });

View File

@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-require-imports */
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
// eslint-disable-next-line no-undef
module.exports = { module.exports = {
content: ["./src/**/*.{html,js,tsx,jsx,cjs,mjs}"], content: ["./src/**/*.{html,js,tsx,jsx,cjs,mjs}"],
theme: { theme: {