fix(ci): resolve all lint errors for GitHub Actions

- Remove unused SupportedLocale import in results.tsx
- Remove unused webroot variable in public/i18n.js
- Configure knip.json to ignore i18n public API exports
- Add language-selector and language-option to ESLint ignore list
- Auto-fix Prettier formatting across all files
- Fix better-tailwindcss line wrapping issues
This commit is contained in:
Your Name 2026-01-20 12:16:13 +08:00
parent 8089fa0853
commit 569c572e62
16 changed files with 621 additions and 508 deletions

View file

@ -9,233 +9,245 @@ import { userService } from "./user";
import { EyeIcon } from "../icons/eye";
import { DeleteIcon } from "../icons/delete";
export const history = new Elysia().use(userService).use(localeService).get(
"/history",
async ({ redirect, user, locale, t }) => {
if (HIDE_HISTORY) {
return redirect(`${WEBROOT}/`, 302);
}
export const history = new Elysia()
.use(userService)
.use(localeService)
.get(
"/history",
async ({ redirect, user, locale, t }) => {
if (HIDE_HISTORY) {
return redirect(`${WEBROOT}/`, 302);
}
if (!user) {
return redirect(`${WEBROOT}/login`, 302);
}
if (!user) {
return redirect(`${WEBROOT}/login`, 302);
}
let userJobs = db.query("SELECT * FROM jobs WHERE user_id = ?").as(Jobs).all(user.id).reverse();
let userJobs = db
.query("SELECT * FROM jobs WHERE user_id = ?")
.as(Jobs)
.all(user.id)
.reverse();
for (const job of userJobs) {
const files = db.query("SELECT * FROM file_names WHERE job_id = ?").as(Filename).all(job.id);
for (const job of userJobs) {
const files = db
.query("SELECT * FROM file_names WHERE job_id = ?")
.as(Filename)
.all(job.id);
job.finished_files = files.length;
job.files_detailed = files;
}
job.finished_files = files.length;
job.files_detailed = files;
}
// Filter out jobs with no files
userJobs = userJobs.filter((job) => job.num_files > 0);
// Filter out jobs with no files
userJobs = userJobs.filter((job) => job.num_files > 0);
return (
<BaseHtml webroot={WEBROOT} title="ConvertX-CN | Results" locale={locale}>
<>
<Header
webroot={WEBROOT}
allowUnauthenticated={ALLOW_UNAUTHENTICATED}
hideHistory={HIDE_HISTORY}
loggedIn
locale={locale}
t={t}
/>
<main
class={`
w-full flex-1 px-2
sm:px-4
`}
>
<article class="article">
<div class="mb-4 flex items-center justify-between">
<h1 class="text-xl">{t("history", "title")}</h1>
<div id="delete-selected-container">
<button
id="delete-selected-btn"
class={`
flex btn-secondary flex-row gap-2 text-contrast
disabled:cursor-not-allowed disabled:opacity-50
`}
disabled
>
<DeleteIcon />{" "}
<span>
{t("history", "deleteSelected")} (<span id="selected-count">0</span>)
</span>
</button>
return (
<BaseHtml webroot={WEBROOT} title="ConvertX-CN | Results" locale={locale}>
<>
<Header
webroot={WEBROOT}
allowUnauthenticated={ALLOW_UNAUTHENTICATED}
hideHistory={HIDE_HISTORY}
loggedIn
locale={locale}
t={t}
/>
<main
class={`
w-full flex-1 px-2
sm:px-4
`}
>
<article class="article">
<div class="mb-4 flex items-center justify-between">
<h1 class="text-xl">{t("history", "title")}</h1>
<div id="delete-selected-container">
<button
id="delete-selected-btn"
class={`
flex btn-secondary flex-row gap-2 text-contrast
disabled:cursor-not-allowed disabled:opacity-50
`}
disabled
>
<DeleteIcon />{" "}
<span>
{t("history", "deleteSelected")} (<span id="selected-count">0</span>)
</span>
</button>
</div>
</div>
</div>
<table
class={`
w-full table-auto overflow-y-auto rounded bg-neutral-900 text-left
[&_td]:p-4
[&_tr]:rounded-sm [&_tr]:border-b [&_tr]:border-neutral-800
`}
>
<thead>
<tr>
<th
class={`
px-2 py-2
sm:px-4
`}
>
<input
type="checkbox"
id="select-all"
class="h-4 w-4 cursor-pointer"
title={t("history", "selectAll")}
/>
</th>
<th
class={`
px-2 py-2
sm:px-4
`}
>
<span class="sr-only">{t("history", "expandDetails")}</span>
</th>
<th
class={`
px-2 py-2
sm:px-4
`}
>
{t("history", "time")}
</th>
<th
class={`
px-2 py-2
sm:px-4
`}
>
{t("history", "files")}
</th>
<th
class={`
px-2 py-2
max-sm:hidden
sm:px-4
`}
>
{t("history", "filesDone")}
</th>
<th
class={`
px-2 py-2
sm:px-4
`}
>
{t("history", "status")}
</th>
<th
class={`
px-2 py-2
sm:px-4
`}
>
{t("history", "actions")}
</th>
</tr>
</thead>
<tbody>
{userJobs.map((job) => (
<>
<tr id={`job-row-${job.id}`}>
<td>
<input
type="checkbox"
class="h-4 w-4 cursor-pointer"
data-checkbox-type="job"
data-job-id={job.id}
/>
</td>
<td class="job-details-toggle cursor-pointer" data-job-id={job.id}>
<svg
id={`arrow-${job.id}`}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="inline-block h-4 w-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M8.25 4.5l7.5 7.5-7.5 7.5"
<table
class={`
w-full table-auto overflow-y-auto rounded bg-neutral-900 text-left
[&_td]:p-4
[&_tr]:rounded-sm [&_tr]:border-b [&_tr]:border-neutral-800
`}
>
<thead>
<tr>
<th
class={`
px-2 py-2
sm:px-4
`}
>
<input
type="checkbox"
id="select-all"
class="h-4 w-4 cursor-pointer"
title={t("history", "selectAll")}
/>
</th>
<th
class={`
px-2 py-2
sm:px-4
`}
>
<span class="sr-only">{t("history", "expandDetails")}</span>
</th>
<th
class={`
px-2 py-2
sm:px-4
`}
>
{t("history", "time")}
</th>
<th
class={`
px-2 py-2
sm:px-4
`}
>
{t("history", "files")}
</th>
<th
class={`
px-2 py-2
max-sm:hidden
sm:px-4
`}
>
{t("history", "filesDone")}
</th>
<th
class={`
px-2 py-2
sm:px-4
`}
>
{t("history", "status")}
</th>
<th
class={`
px-2 py-2
sm:px-4
`}
>
{t("history", "actions")}
</th>
</tr>
</thead>
<tbody>
{userJobs.map((job) => (
<>
<tr id={`job-row-${job.id}`}>
<td>
<input
type="checkbox"
class="h-4 w-4 cursor-pointer"
data-checkbox-type="job"
data-job-id={job.id}
/>
</svg>
</td>
<td safe>
{new Date(job.date_created).toLocaleTimeString(LANGUAGE, {
timeZone: TIMEZONE,
})}
</td>
<td>{job.num_files}</td>
<td class="max-sm:hidden">{job.finished_files}</td>
<td safe>{job.status}</td>
<td class="flex flex-row gap-4">
<a
class={`
text-accent-500 underline
hover:text-accent-400
`}
href={`${WEBROOT}/results/${job.id}`}
>
<EyeIcon />
</a>
<a
class={`
text-accent-500 underline
hover:text-accent-400
`}
href={`${WEBROOT}/delete/${job.id}`}
>
<DeleteIcon />
</a>
</td>
</tr>
<tr id={`details-${job.id}`} class="hidden">
<td colspan="7">
<div class="p-2 text-sm text-neutral-500">
<div class="mb-1 font-semibold">{t("history", "detailedFileInfo")}</div>
{job.files_detailed.map((file: Filename) => (
<div class="flex items-center">
<span class="w-5/12 truncate" title={file.file_name} safe>
{file.file_name}
</span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class={`mx-2 inline-block h-4 w-4 text-neutral-500`}
>
<path
fill-rule="evenodd"
d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z"
clip-rule="evenodd"
/>
</svg>
<span class="w-5/12 truncate" title={file.output_file_name} safe>
{file.output_file_name}
</span>
</td>
<td class="job-details-toggle cursor-pointer" data-job-id={job.id}>
<svg
id={`arrow-${job.id}`}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="inline-block h-4 w-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M8.25 4.5l7.5 7.5-7.5 7.5"
/>
</svg>
</td>
<td safe>
{new Date(job.date_created).toLocaleTimeString(LANGUAGE, {
timeZone: TIMEZONE,
})}
</td>
<td>{job.num_files}</td>
<td class="max-sm:hidden">{job.finished_files}</td>
<td safe>{job.status}</td>
<td class="flex flex-row gap-4">
<a
class={`
text-accent-500 underline
hover:text-accent-400
`}
href={`${WEBROOT}/results/${job.id}`}
>
<EyeIcon />
</a>
<a
class={`
text-accent-500 underline
hover:text-accent-400
`}
href={`${WEBROOT}/delete/${job.id}`}
>
<DeleteIcon />
</a>
</td>
</tr>
<tr id={`details-${job.id}`} class="hidden">
<td colspan="7">
<div class="p-2 text-sm text-neutral-500">
<div class="mb-1 font-semibold">
{t("history", "detailedFileInfo")}
</div>
))}
</div>
</td>
</tr>
</>
))}
</tbody>
</table>
</article>
</main>
<script>
{`
{job.files_detailed.map((file: Filename) => (
<div class="flex items-center">
<span class="w-5/12 truncate" title={file.file_name} safe>
{file.file_name}
</span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class={`mx-2 inline-block h-4 w-4 text-neutral-500`}
>
<path
fill-rule="evenodd"
d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z"
clip-rule="evenodd"
/>
</svg>
<span class="w-5/12 truncate" title={file.output_file_name} safe>
{file.output_file_name}
</span>
</div>
))}
</div>
</td>
</tr>
</>
))}
</tbody>
</table>
</article>
</main>
<script>
{`
document.addEventListener('DOMContentLoaded', () => {
// Expand/collapse job details
const toggles = document.querySelectorAll('.job-details-toggle');
@ -330,12 +342,12 @@ export const history = new Elysia().use(userService).use(localeService).get(
});
});
`}
</script>
</>
</BaseHtml>
);
},
{
auth: true,
},
);
</script>
</>
</BaseHtml>
);
},
{
auth: true,
},
);

View file

@ -7,7 +7,7 @@ import { ALLOW_UNAUTHENTICATED, WEBROOT } from "../helpers/env";
import { DownloadIcon } from "../icons/download";
import { DeleteIcon } from "../icons/delete";
import { EyeIcon } from "../icons/eye";
import { type SupportedLocale, type Translator, createTranslator, defaultLocale } from "../i18n/index";
import { type Translator, createTranslator, defaultLocale } from "../i18n/index";
import { localeService } from "../i18n/service";
import { userService } from "./user";
@ -165,7 +165,13 @@ export const results = new Elysia()
return (
<BaseHtml webroot={WEBROOT} title="ConvertX-CN | Result" locale={locale}>
<>
<Header webroot={WEBROOT} allowUnauthenticated={ALLOW_UNAUTHENTICATED} loggedIn locale={locale} t={t} />
<Header
webroot={WEBROOT}
allowUnauthenticated={ALLOW_UNAUTHENTICATED}
loggedIn
locale={locale}
t={t}
/>
<main
class={`
w-full flex-1 px-2

View file

@ -17,237 +17,245 @@ import {
import { localeService } from "../i18n/service";
import { FIRST_RUN, userService } from "./user";
export const root = new Elysia().use(userService).use(localeService).get(
"/",
async ({ jwt, redirect, cookie: { auth, jobId }, locale, t }) => {
if (!ALLOW_UNAUTHENTICATED) {
if (FIRST_RUN) {
return redirect(`${WEBROOT}/setup`, 302);
export const root = new Elysia()
.use(userService)
.use(localeService)
.get(
"/",
async ({ jwt, redirect, cookie: { auth, jobId }, locale, t }) => {
if (!ALLOW_UNAUTHENTICATED) {
if (FIRST_RUN) {
return redirect(`${WEBROOT}/setup`, 302);
}
if (!auth?.value) {
return redirect(`${WEBROOT}/login`, 302);
}
}
if (!auth?.value) {
// validate jwt
let user: ({ id: string } & JWTPayloadSpec) | false = false;
if (ALLOW_UNAUTHENTICATED) {
const newUserId = String(
UNAUTHENTICATED_USER_SHARING
? 0
: randomInt(2 ** 24, Math.min(2 ** 48 + 2 ** 24 - 1, Number.MAX_SAFE_INTEGER)),
);
const accessToken = await jwt.sign({
id: newUserId,
});
user = { id: newUserId };
if (!auth) {
return {
message: t("auth", "noCookies"),
};
}
// set cookie
auth.set({
value: accessToken,
httpOnly: true,
secure: !HTTP_ALLOWED,
maxAge: 24 * 60 * 60,
sameSite: "strict",
});
} else if (auth?.value) {
user = await jwt.verify(auth.value);
if (
user !== false &&
user.id &&
(Number.parseInt(user.id) < 2 ** 24 || !ALLOW_UNAUTHENTICATED)
) {
// Make sure user exists in db
const existingUser = db.query("SELECT * FROM users WHERE id = ?").as(User).get(user.id);
if (!existingUser) {
if (auth?.value) {
auth.remove();
}
return redirect(`${WEBROOT}/login`, 302);
}
}
}
if (!user) {
return redirect(`${WEBROOT}/login`, 302);
}
}
// validate jwt
let user: ({ id: string } & JWTPayloadSpec) | false = false;
if (ALLOW_UNAUTHENTICATED) {
const newUserId = String(
UNAUTHENTICATED_USER_SHARING
? 0
: randomInt(2 ** 24, Math.min(2 ** 48 + 2 ** 24 - 1, Number.MAX_SAFE_INTEGER)),
// create a new job
db.query("INSERT INTO jobs (user_id, date_created) VALUES (?, ?)").run(
user.id,
new Date().toISOString(),
);
const accessToken = await jwt.sign({
id: newUserId,
});
user = { id: newUserId };
if (!auth) {
return {
message: t("auth", "noCookies"),
};
const { id } = db
.query("SELECT id FROM jobs WHERE user_id = ? ORDER BY id DESC")
.get(user.id) as { id: number };
if (!jobId) {
return { message: t("auth", "cookiesRequired") };
}
// set cookie
auth.set({
value: accessToken,
jobId.set({
value: id,
httpOnly: true,
secure: !HTTP_ALLOWED,
maxAge: 24 * 60 * 60,
sameSite: "strict",
});
} else if (auth?.value) {
user = await jwt.verify(auth.value);
if (
user !== false &&
user.id &&
(Number.parseInt(user.id) < 2 ** 24 || !ALLOW_UNAUTHENTICATED)
) {
// Make sure user exists in db
const existingUser = db.query("SELECT * FROM users WHERE id = ?").as(User).get(user.id);
console.log("jobId set to:", id);
if (!existingUser) {
if (auth?.value) {
auth.remove();
}
return redirect(`${WEBROOT}/login`, 302);
}
}
}
if (!user) {
return redirect(`${WEBROOT}/login`, 302);
}
// create a new job
db.query("INSERT INTO jobs (user_id, date_created) VALUES (?, ?)").run(
user.id,
new Date().toISOString(),
);
const { id } = db
.query("SELECT id FROM jobs WHERE user_id = ? ORDER BY id DESC")
.get(user.id) as { id: number };
if (!jobId) {
return { message: t("auth", "cookiesRequired") };
}
jobId.set({
value: id,
httpOnly: true,
secure: !HTTP_ALLOWED,
maxAge: 24 * 60 * 60,
sameSite: "strict",
});
console.log("jobId set to:", id);
return (
<BaseHtml webroot={WEBROOT} locale={locale}>
<>
<Header
webroot={WEBROOT}
accountRegistration={ACCOUNT_REGISTRATION}
allowUnauthenticated={ALLOW_UNAUTHENTICATED}
hideHistory={HIDE_HISTORY}
loggedIn
locale={locale}
t={t}
/>
<main
class={`
w-full flex-1 px-2
sm:px-4
`}
>
<article class="article">
<h1 class="mb-4 text-xl">{t("convert", "title")}</h1>
<div class="mb-4 scrollbar-thin max-h-[50vh] overflow-y-auto">
<table
id="file-list"
class={`
w-full table-auto rounded bg-neutral-900
[&_td]:p-4 [&_td]:first:max-w-[30vw] [&_td]:first:truncate
[&_tr]:rounded-sm [&_tr]:border-b [&_tr]:border-neutral-800
`}
/>
</div>
<div
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
`}
>
<span>
<b>{t("convert", "chooseFile")}</b> {t("convert", "orDragHere")}
</span>
<input
type="file"
name="file"
multiple
class="absolute inset-0 size-full cursor-pointer opacity-0"
/>
</div>
</article>
<form
method="post"
action={`${WEBROOT}/convert`}
class="relative mx-auto mb-[35vh] w-full max-w-4xl"
return (
<BaseHtml webroot={WEBROOT} locale={locale}>
<>
<Header
webroot={WEBROOT}
accountRegistration={ACCOUNT_REGISTRATION}
allowUnauthenticated={ALLOW_UNAUTHENTICATED}
hideHistory={HIDE_HISTORY}
loggedIn
locale={locale}
t={t}
/>
<main
class={`
w-full flex-1 px-2
sm:px-4
`}
>
<input type="hidden" name="file_names" id="file_names" />
<article class="article w-full">
<input
type="search"
name="convert_to_search"
placeholder={t("convert", "searchConversions")}
autocomplete="off"
class="w-full rounded-sm bg-neutral-800 p-4"
/>
<div class="select_container relative">
<article
<article class="article">
<h1 class="mb-4 text-xl">{t("convert", "title")}</h1>
<div class="mb-4 scrollbar-thin max-h-[50vh] overflow-y-auto">
<table
id="file-list"
class={`
convert_to_popup absolute z-2 m-0 hidden h-[30vh] max-h-[50vh] w-full flex-col
overflow-x-hidden overflow-y-auto rounded bg-neutral-800
sm:h-[30vh]
w-full table-auto rounded bg-neutral-900
[&_td]:p-4 [&_td]:first:max-w-[30vw] [&_td]:first:truncate
[&_tr]:rounded-sm [&_tr]:border-b [&_tr]:border-neutral-800
`}
>
{Object.entries(getAllTargets()).map(([converter, targets]) => (
<article
class={`
convert_to_group flex w-full flex-col border-b border-neutral-700 p-4
`}
data-converter={converter}
>
<header class="mb-2 w-full text-xl font-bold" safe>
{converter}
</header>
<ul class={`convert_to_target flex flex-row flex-wrap gap-1`}>
{targets.map((target) => (
<button
// https://stackoverflow.com/questions/121499/when-a-blur-event-occurs-how-can-i-find-out-which-element-focus-went-to#comment82388679_33325953
tabindex={0}
class={`
target rounded bg-neutral-700 p-1 text-base
hover:bg-neutral-600
`}
data-value={`${target},${converter}`}
data-target={target}
data-converter={converter}
type="button"
safe
>
{target}
</button>
))}
</ul>
</article>
))}
</article>
{/* Hidden element which determines the format to convert the file too and the converter to use */}
<select name="convert_to" aria-label={t("convert", "convertTo")} required hidden>
<option selected disabled value="">
{t("convert", "convertTo")}
</option>
{Object.entries(getAllTargets()).map(([converter, targets]) => (
<optgroup label={converter}>
{targets.map((target) => (
<option value={`${target},${converter}`} safe>
{target}
</option>
))}
</optgroup>
))}
</select>
/>
</div>
<div
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
`}
>
<span>
<b>{t("convert", "chooseFile")}</b> {t("convert", "orDragHere")}
</span>
<input
type="file"
name="file"
multiple
class="absolute inset-0 size-full cursor-pointer opacity-0"
/>
</div>
</article>
<input
class={`
w-full btn-primary opacity-100
disabled:cursor-not-allowed disabled:opacity-50
`}
type="submit"
value={t("convert", "convertButton")}
disabled
/>
</form>
</main>
<script src="script.js" defer />
</>
</BaseHtml>
);
},
{
cookie: t.Cookie({
auth: t.Optional(t.String()),
jobId: t.Optional(t.String()),
locale: t.Optional(t.String()),
}),
},
);
<form
method="post"
action={`${WEBROOT}/convert`}
class="relative mx-auto mb-[35vh] w-full max-w-4xl"
>
<input type="hidden" name="file_names" id="file_names" />
<article class="article w-full">
<input
type="search"
name="convert_to_search"
placeholder={t("convert", "searchConversions")}
autocomplete="off"
class="w-full rounded-sm bg-neutral-800 p-4"
/>
<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-x-hidden overflow-y-auto rounded bg-neutral-800
sm:h-[30vh]
`}
>
{Object.entries(getAllTargets()).map(([converter, targets]) => (
<article
class={`
convert_to_group flex w-full flex-col border-b border-neutral-700 p-4
`}
data-converter={converter}
>
<header class="mb-2 w-full text-xl font-bold" safe>
{converter}
</header>
<ul class={`convert_to_target flex flex-row flex-wrap gap-1`}>
{targets.map((target) => (
<button
// https://stackoverflow.com/questions/121499/when-a-blur-event-occurs-how-can-i-find-out-which-element-focus-went-to#comment82388679_33325953
tabindex={0}
class={`
target rounded bg-neutral-700 p-1 text-base
hover:bg-neutral-600
`}
data-value={`${target},${converter}`}
data-target={target}
data-converter={converter}
type="button"
safe
>
{target}
</button>
))}
</ul>
</article>
))}
</article>
{/* Hidden element which determines the format to convert the file too and the converter to use */}
<select
name="convert_to"
aria-label={t("convert", "convertTo")}
required
hidden
>
<option selected disabled value="">
{t("convert", "convertTo")}
</option>
{Object.entries(getAllTargets()).map(([converter, targets]) => (
<optgroup label={converter}>
{targets.map((target) => (
<option value={`${target},${converter}`} safe>
{target}
</option>
))}
</optgroup>
))}
</select>
</div>
</article>
<input
class={`
w-full btn-primary opacity-100
disabled:cursor-not-allowed disabled:opacity-50
`}
type="submit"
value={t("convert", "convertButton")}
disabled
/>
</form>
</main>
<script src="script.js" defer />
</>
</BaseHtml>
);
},
{
cookie: t.Cookie({
auth: t.Optional(t.String()),
jobId: t.Optional(t.String()),
locale: t.Optional(t.String()),
}),
},
);

View file

@ -173,7 +173,11 @@ export const user = new Elysia()
/>
</label>
</fieldset>
<input type="submit" value={t("auth", "registerButton")} class="w-full btn-primary" />
<input
type="submit"
value={t("auth", "registerButton")}
class="w-full btn-primary"
/>
</form>
</article>
</main>
@ -302,7 +306,11 @@ export const user = new Elysia()
>
{t("nav", "register")}
</a>
<input type="submit" value={t("auth", "loginButton")} class="w-full btn-primary" />
<input
type="submit"
value={t("auth", "loginButton")}
class="w-full btn-primary"
/>
</div>
</form>
</article>
@ -441,7 +449,11 @@ export const user = new Elysia()
</label>
</fieldset>
<div role="group">
<input type="submit" value={t("auth", "updateButton")} class="w-full btn-primary" />
<input
type="submit"
value={t("auth", "updateButton")}
class={`w-full btn-primary`}
/>
</div>
</form>
</article>