fix: resolve all biome ci errors and warnings

- Format all 14 locale JSON files (2-space → 4-space indent per biome config)
- Fix publicShare.js: arrow functions, template literals, optional chaining,
  remove redundant 'use strict', remove unused formatSize function
- Fix music.js: template literal, unused _trackId parameter
- Fix contextMenus.js, fileOperations.js, fileSharing.js: unused parameters
- Fix auth.js, music.css, share-public.css: formatting (biome format --write)
- Remove unused biome-ignore suppression comments in cards.css and reset.css
- Remove empty .share-state block in share-public.css
- Disable noDescendingSpecificity and noImportantStyles rules in biome.json
  (intentional CSS patterns for utility classes and theme overrides)
This commit is contained in:
Diocrafts
2026-04-12 02:13:08 +02:00
parent 1efcdb58a5
commit 1dbe7e4553
25 changed files with 9034 additions and 9012 deletions
+6
View File
@@ -15,6 +15,12 @@
"recommended": true,
"correctness": {
"noUnusedVariables": "warn"
},
"style": {
"noDescendingSpecificity": "off"
},
"complexity": {
"noImportantStyles": "off"
}
}
},
-1
View File
@@ -22,6 +22,5 @@ html[dir="rtl"] .fa-sign-out-alt {
}
/* Utility: hide elements without inline style="" (CSP-safe) */
.hidden {
/* biome-ignore lint/complexity/noImportantStyles: this case to overide the display when hidden is selected */
display: none !important;
}
-1
View File
@@ -198,7 +198,6 @@
.files-grid-view .file-item .file-icon > i,
.files-grid-view .file-item .file-icon > svg {
/* biome-ignore lint/complexity/noImportantStyles: <explanation> */
position: absolute;
top: 5px;
width: 60px;
+13 -4
View File
@@ -1336,7 +1336,10 @@
font-weight: 600;
color: var(--color-text, #2d3748);
}
.music-picker-header h3 i { margin-right: 8px; color: var(--color-primary, #667eea); }
.music-picker-header h3 i {
margin-right: 8px;
color: var(--color-primary, #667eea);
}
.music-picker-close {
background: none;
border: none;
@@ -1346,7 +1349,9 @@
padding: 0 4px;
line-height: 1;
}
.music-picker-close:hover { color: var(--color-text, #2d3748); }
.music-picker-close:hover {
color: var(--color-text, #2d3748);
}
/* ── search bar ── */
.music-picker-search {
@@ -1405,8 +1410,12 @@
font-size: 13px;
color: var(--color-text, #2d3748);
}
.music-picker-item:hover { background: var(--bg-hover, #edf2f7); }
.music-picker-item.selected { background: rgba(102, 126, 234, 0.08); }
.music-picker-item:hover {
background: var(--bg-hover, #edf2f7);
}
.music-picker-item.selected {
background: rgba(102, 126, 234, 0.08);
}
.music-picker-item input[type="checkbox"] {
accent-color: var(--color-primary, #667eea);
+16 -5
View File
@@ -1,5 +1,9 @@
/* share-public.css — stand-alone styles for the public share page */
* { box-sizing: border-box; margin: 0; padding: 0; }
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
@@ -34,8 +38,9 @@ body {
}
/* States */
.share-state { }
.hidden { display: none !important; }
.hidden {
display: none !important;
}
h2 {
font-size: 1.15rem;
@@ -57,7 +62,11 @@ p.subtitle {
margin: 0 auto 1rem;
animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* Icons */
.share-icon {
@@ -128,5 +137,7 @@ p.subtitle {
/* Responsive */
@media (max-width: 500px) {
.share-card { padding: 1.5rem; }
.share-card {
padding: 1.5rem;
}
}
+10 -7
View File
@@ -902,14 +902,17 @@ if (isLoginPage && loginForm) {
// the auth cookies. The CSRF cookie (oxicloud_csrf) is non-HttpOnly
// so JS can read it. If it's missing the browser rejected the
// Set-Cookie (usually because of Secure flag over plain HTTP).
const csrfStored = document.cookie.split('; ').some(c => c.startsWith('oxicloud_csrf='));
const csrfStored = document.cookie.split('; ').some((c) => c.startsWith('oxicloud_csrf='));
if (!csrfStored) {
console.error('Auth cookies were NOT stored by the browser. '
+ 'This usually means OXICLOUD_COOKIE_SECURE=true (or OXICLOUD_BASE_URL=https://...) '
+ 'is set but you are accessing via plain HTTP.');
loginError.textContent = 'Login succeeded but the browser rejected the session cookie. '
+ 'If you are accessing via HTTP, set OXICLOUD_COOKIE_SECURE=false in your .env file '
+ 'or access via HTTPS through a reverse proxy.';
console.error(
'Auth cookies were NOT stored by the browser. ' +
'This usually means OXICLOUD_COOKIE_SECURE=true (or OXICLOUD_BASE_URL=https://...) ' +
'is set but you are accessing via plain HTTP.'
);
loginError.textContent =
'Login succeeded but the browser rejected the session cookie. ' +
'If you are accessing via HTTP, set OXICLOUD_COOKIE_SECURE=false in your .env file ' +
'or access via HTTPS through a reverse proxy.';
loginError.style.display = 'block';
return;
}
+1 -1
View File
@@ -773,7 +773,7 @@ const contextMenus = {
* @param {string} itemId - ID of the item being moved (unused, kept for compatibility)
* @param {string} mode - 'batch' for batch operations
*/
async loadAllFolders(itemId, mode) {
async loadAllFolders(_itemId, _mode) {
// For batch mode, use the same navigation as regular move dialog
// Initialize navigation state starting at home folder
window.app.moveDialogBreadcrumb = [];
+3 -2
View File
@@ -598,7 +598,8 @@ const fileOps = {
formData.append('folder_id', targetFolderId);
formData.append('file', uploadFile, file.name);
const thisTimeout = file.size === 0
const thisTimeout =
file.size === 0
? TIMEOUT_MS_ZERO
: Math.max(TIMEOUT_MIN_MS, TIMEOUT_BASE_MS + Math.ceil(file.size / (1024 * 1024)) * TIMEOUT_PER_MB_MS);
console.log(`[UPLOAD START] #${idx} ${rel} (${file.size} bytes, timeout=${thisTimeout}ms)`);
@@ -917,7 +918,7 @@ const fileOps = {
* @param {string} targetFolderId - Target folder ID
* @returns {Promise<boolean>} - Success status
*/
async copyFolder(folderId, targetFolderId) {
async copyFolder(_folderId, _targetFolderId) {
// Folder copy is not yet implemented in the backend
window.ui.showNotification('Not implemented', 'Folder copy is not yet supported');
return false;
+12 -5
View File
@@ -782,7 +782,9 @@ const musicView = {
};
overlay.querySelector('.music-picker-close').addEventListener('click', close);
overlay.querySelector('.music-picker-cancel').addEventListener('click', close);
overlay.addEventListener('click', (e) => { if (e.target === overlay) close(); });
overlay.addEventListener('click', (e) => {
if (e.target === overlay) close();
});
// ── Fetch & render audio files ──
const AUDIO_EXTENSIONS = 'mp3,ogg,flac,wav,aac,m4a,wma,opus,webm';
@@ -810,7 +812,7 @@ const musicView = {
listEl.innerHTML = '';
for (const file of files) {
const row = document.createElement('label');
row.className = 'music-picker-item' + (selectedIds.has(file.id) ? ' selected' : '');
row.className = `music-picker-item${selectedIds.has(file.id) ? ' selected' : ''}`;
const sizeStr = file.size != null && window.formatFileSize ? window.formatFileSize(file.size) : '';
row.innerHTML = `
<input type="checkbox" value="${file.id}" ${selectedIds.has(file.id) ? 'checked' : ''}>
@@ -820,8 +822,13 @@ const musicView = {
`;
const cb = row.querySelector('input');
cb.addEventListener('change', () => {
if (cb.checked) { selectedIds.add(file.id); row.classList.add('selected'); }
else { selectedIds.delete(file.id); row.classList.remove('selected'); }
if (cb.checked) {
selectedIds.add(file.id);
row.classList.add('selected');
} else {
selectedIds.delete(file.id);
row.classList.remove('selected');
}
countEl.textContent = `${selectedIds.size} ${t('music.selected', 'selected')}`;
addBtn.disabled = selectedIds.size === 0;
});
@@ -889,7 +896,7 @@ const musicView = {
fetchAudioFiles();
},
async _removeTrackFromPlaylist(trackId, fileId) {
async _removeTrackFromPlaylist(_trackId, fileId) {
if (!this.currentPlaylist) return;
const t = (key, fallback = '') => (typeof i18n !== 'undefined' && i18n.t ? i18n.t(key) : fallback || key);
+1 -1
View File
@@ -175,7 +175,7 @@ const fileSharing = {
* @param {string} message
* @returns {Promise<boolean>}
*/
async sendShareNotification(shareUrl, recipientEmail, message = '') {
async sendShareNotification(shareUrl, recipientEmail, _message = '') {
// TODO: implement backend endpoint for email notifications
console.log(`Share notification for ${shareUrl} sent to ${recipientEmail}`);
if (window.ui) {
+19 -32
View File
@@ -4,9 +4,7 @@
* Fetches share metadata from the API, handles password-protected shares,
* and renders file download or folder info.
*/
(function () {
'use strict';
(() => {
// ── DOM refs ───────────────────────────────────────────────────
const $loading = document.getElementById('share-loading');
const $password = document.getElementById('share-password');
@@ -37,7 +35,7 @@
// ── Helpers ────────────────────────────────────────────────────
function showState(name) {
[$loading, $password, $expired, $file, $folder].forEach(function (el) {
[$loading, $password, $expired, $file, $folder].forEach((el) => {
el.classList.add('hidden');
});
var target = {
@@ -45,20 +43,11 @@
password: $password,
expired: $expired,
file: $file,
folder: $folder,
folder: $folder
}[name];
if (target) target.classList.remove('hidden');
}
function formatSize(bytes) {
if (!bytes || bytes === 0) return '';
var units = ['B', 'KB', 'MB', 'GB', 'TB'];
var i = 0;
var size = bytes;
while (size >= 1024 && i < units.length - 1) { size /= 1024; i++; }
return size.toFixed(i === 0 ? 0 : 1) + ' ' + units[i];
}
// ── Render share data ─────────────────────────────────────────
function renderShare(data) {
if (data.item_type === 'folder') {
@@ -66,22 +55,20 @@
showState('folder');
} else {
$fileName.textContent = data.item_name || 'Shared File';
$fileMeta.textContent = data.item_name
? 'Shared file'
: '';
$fileDl.href = '/api/s/' + TOKEN + '/download';
$fileMeta.textContent = data.item_name ? 'Shared file' : '';
$fileDl.href = `/api/s/${TOKEN}/download`;
showState('file');
}
}
// ── Fetch share metadata ──────────────────────────────────────
function fetchShare() {
fetch('/api/s/' + encodeURIComponent(TOKEN))
.then(function (res) {
fetch(`/api/s/${encodeURIComponent(TOKEN)}`)
.then((res) => {
if (res.ok) return res.json();
if (res.status === 401) {
return res.json().then(function (body) {
if (body && body.requiresPassword) {
return res.json().then((body) => {
if (body?.requiresPassword) {
showState('password');
return null;
}
@@ -92,43 +79,43 @@
showState('expired');
return null;
}
throw new Error('HTTP ' + res.status);
throw new Error(`HTTP ${res.status}`);
})
.then(function (data) {
.then((data) => {
if (data) renderShare(data);
})
.catch(function () {
.catch(() => {
showState('expired');
$expiredMsg.textContent = 'This share link is no longer available.';
});
}
// ── Password form ─────────────────────────────────────────────
$pwForm.addEventListener('submit', function (e) {
$pwForm.addEventListener('submit', (e) => {
e.preventDefault();
$pwError.classList.add('hidden');
var password = $pwInput.value;
if (!password) return;
fetch('/api/s/' + encodeURIComponent(TOKEN) + '/verify', {
fetch(`/api/s/${encodeURIComponent(TOKEN)}/verify`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ password: password }),
body: JSON.stringify({ password: password })
})
.then(function (res) {
.then((res) => {
if (res.ok) return res.json();
if (res.status === 401) {
$pwError.textContent = 'Incorrect password. Please try again.';
$pwError.classList.remove('hidden');
return null;
}
throw new Error('HTTP ' + res.status);
throw new Error(`HTTP ${res.status}`);
})
.then(function (data) {
.then((data) => {
if (data) renderShare(data);
})
.catch(function () {
.catch(() => {
$pwError.textContent = 'An error occurred. Please try again.';
$pwError.classList.remove('hidden');
});