Merge pull request #352 from EdouardVanbelle/fix/admin-missing-icons-library
fix(ui): admin panel, profile, etc: restore missing icons library
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
import { formatFileSize, formatQuotaSize } from '../core/formatters.js';
|
import { formatFileSize, formatQuotaSize } from '../core/formatters.js';
|
||||||
import { i18n } from '../core/i18n.js';
|
import { i18n } from '../core/i18n.js';
|
||||||
|
import { oxiIconsInit } from '../core/icons.js';
|
||||||
import { Modal } from '../core/modal.js';
|
import { Modal } from '../core/modal.js';
|
||||||
import { fileOps } from '../features/files/fileOperations.js';
|
import { fileOps } from '../features/files/fileOperations.js';
|
||||||
import { multiSelect } from '../features/files/multiSelect.js';
|
import { multiSelect } from '../features/files/multiSelect.js';
|
||||||
@@ -334,6 +335,8 @@ function switchSectionTo(section) {
|
|||||||
* Initialize the application
|
* Initialize the application
|
||||||
*/
|
*/
|
||||||
function initApp() {
|
function initApp() {
|
||||||
|
oxiIconsInit();
|
||||||
|
|
||||||
// Cache DOM elements
|
// Cache DOM elements
|
||||||
cacheElements();
|
cacheElements();
|
||||||
|
|
||||||
|
|||||||
+14
-14
@@ -15,7 +15,7 @@
|
|||||||
// All icons use viewBox="0 0 {width} 512" and fill="currentColor".
|
// All icons use viewBox="0 0 {width} 512" and fill="currentColor".
|
||||||
// Keys use FA5 class names (without "fa-" prefix) for backward compatibility.
|
// Keys use FA5 class names (without "fa-" prefix) for backward compatibility.
|
||||||
|
|
||||||
const _ICONS = {
|
const OxiIcons = {
|
||||||
'arrow-left': [
|
'arrow-left': [
|
||||||
448,
|
448,
|
||||||
'M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.2 288 416 288c17.7 0 32-14.3 32-32s-14.3-32-32-32l-306.7 0L214.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z'
|
'M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.2 288 416 288c17.7 0 32-14.3 32-32s-14.3-32-32-32l-306.7 0L214.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z'
|
||||||
@@ -430,7 +430,7 @@ const _ICONS = {
|
|||||||
* @returns {string} SVG markup string, or empty string if icon not found
|
* @returns {string} SVG markup string, or empty string if icon not found
|
||||||
*/
|
*/
|
||||||
function oxiIcon(name, extraClass) {
|
function oxiIcon(name, extraClass) {
|
||||||
const entry = _ICONS[name];
|
const entry = OxiIcons[name];
|
||||||
if (!entry) return '';
|
if (!entry) return '';
|
||||||
const [w, d] = entry;
|
const [w, d] = entry;
|
||||||
const cls = extraClass ? `oxi-icon ${extraClass}` : 'oxi-icon';
|
const cls = extraClass ? `oxi-icon ${extraClass}` : 'oxi-icon';
|
||||||
@@ -477,13 +477,13 @@ function replaceIconsInElement(container) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use outline variant if available and element uses "far"
|
// Use outline variant if available and element uses "far"
|
||||||
if (isRegular && _ICONS[`${iconName}-outline`]) {
|
if (isRegular && OxiIcons[`${iconName}-outline`]) {
|
||||||
iconName = `${iconName}-outline`;
|
iconName = `${iconName}-outline`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!iconName || !_ICONS[iconName]) continue;
|
if (!iconName || !OxiIcons[iconName]) continue;
|
||||||
|
|
||||||
const [w, d] = _ICONS[iconName];
|
const [w, d] = OxiIcons[iconName];
|
||||||
|
|
||||||
// Build SVG element
|
// Build SVG element
|
||||||
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||||
@@ -510,14 +510,7 @@ function replaceIconsInElement(container) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Expose globally ────────────────────────────────────────────
|
function oxiIconsInit() {
|
||||||
export { oxiIcon, replaceIconsInElement };
|
|
||||||
export const OxiIcons = _ICONS;
|
|
||||||
|
|
||||||
// ── Auto-replace: MutationObserver bridge ──────────────────────
|
|
||||||
// Watches the DOM for new <i class="fa-..."> elements and converts them
|
|
||||||
// to inline SVGs automatically. Debounced to at most once per frame.
|
|
||||||
(function autoReplace() {
|
|
||||||
let raf = 0;
|
let raf = 0;
|
||||||
const scan = () => {
|
const scan = () => {
|
||||||
raf = 0;
|
raf = 0;
|
||||||
@@ -541,4 +534,11 @@ export const OxiIcons = _ICONS;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).observe(document.documentElement, { childList: true, subtree: true });
|
}).observe(document.documentElement, { childList: true, subtree: true });
|
||||||
})();
|
}
|
||||||
|
|
||||||
|
// ── Expose globally ────────────────────────────────────────────
|
||||||
|
export { OxiIcons, oxiIcon, oxiIconsInit, replaceIconsInElement };
|
||||||
|
|
||||||
|
// ── Auto-replace: MutationObserver bridge ──────────────────────
|
||||||
|
// Watches the DOM for new <i class="fa-..."> elements and converts them
|
||||||
|
// to inline SVGs automatically. Debounced to at most once per frame.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { getCsrfHeaders } from '../../core/csrf.js';
|
import { getCsrfHeaders } from '../../core/csrf.js';
|
||||||
import { escapeHtml } from '../../core/formatters.js';
|
import { escapeHtml } from '../../core/formatters.js';
|
||||||
import { i18n } from '../../core/i18n.js';
|
import { i18n } from '../../core/i18n.js';
|
||||||
|
import { oxiIconsInit } from '../../core/icons.js';
|
||||||
|
|
||||||
const API = '/api';
|
const API = '/api';
|
||||||
let currentAdminId = '';
|
let currentAdminId = '';
|
||||||
@@ -1041,6 +1042,7 @@ async function completeMigration() {
|
|||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
try {
|
try {
|
||||||
|
oxiIconsInit();
|
||||||
const me = await fetch(`${API}/auth/me`, {
|
const me = await fetch(`${API}/auth/me`, {
|
||||||
headers: headers(),
|
headers: headers(),
|
||||||
credentials: 'same-origin'
|
credentials: 'same-origin'
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// device-verify.js — Extracted from inline <script> in device-verify.html
|
// device-verify.js — Extracted from inline <script> in device-verify.html
|
||||||
import { getCsrfHeaders } from '../../core/csrf.js';
|
import { getCsrfHeaders } from '../../core/csrf.js';
|
||||||
|
import { oxiIconsInit } from '../../core/icons.js';
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
var API_BASE = window.location.origin;
|
var API_BASE = window.location.origin;
|
||||||
@@ -12,6 +13,8 @@ import { getCsrfHeaders } from '../../core/csrf.js';
|
|||||||
var debounceTimer = null;
|
var debounceTimer = null;
|
||||||
var currentCode = '';
|
var currentCode = '';
|
||||||
|
|
||||||
|
oxiIconsInit();
|
||||||
|
|
||||||
// Pre-fill from URL query param (?code=ABCD-1234)
|
// Pre-fill from URL query param (?code=ABCD-1234)
|
||||||
var params = new URLSearchParams(window.location.search);
|
var params = new URLSearchParams(window.location.search);
|
||||||
if (params.get('code')) {
|
if (params.get('code')) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { getCsrfHeaders } from '../../core/csrf.js';
|
import { getCsrfHeaders } from '../../core/csrf.js';
|
||||||
import { i18n } from '../../core/i18n.js';
|
import { i18n } from '../../core/i18n.js';
|
||||||
|
import { oxiIconsInit } from '../../core/icons.js';
|
||||||
|
|
||||||
const API = '/api';
|
const API = '/api';
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ function timeAgo(dateStr) {
|
|||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
try {
|
try {
|
||||||
|
oxiIconsInit();
|
||||||
const resp = await fetch(`${API}/auth/me`, {
|
const resp = await fetch(`${API}/auth/me`, {
|
||||||
headers: headers(),
|
headers: headers(),
|
||||||
credentials: 'same-origin'
|
credentials: 'same-origin'
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { oxiIconsInit } from '../../core/icons';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* publicShare.js — Client-side logic for the public share page (/s/{token}).
|
* publicShare.js — Client-side logic for the public share page (/s/{token}).
|
||||||
*
|
*
|
||||||
@@ -27,6 +29,8 @@
|
|||||||
const tokenIdx = pathParts.indexOf('s');
|
const tokenIdx = pathParts.indexOf('s');
|
||||||
const TOKEN = tokenIdx !== -1 ? pathParts[tokenIdx + 1] : null;
|
const TOKEN = tokenIdx !== -1 ? pathParts[tokenIdx + 1] : null;
|
||||||
|
|
||||||
|
oxiIconsInit();
|
||||||
|
|
||||||
if (!TOKEN) {
|
if (!TOKEN) {
|
||||||
showState('expired');
|
showState('expired');
|
||||||
$expiredMsg.textContent = 'Invalid share link.';
|
$expiredMsg.textContent = 'Invalid share link.';
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ Usage:
|
|||||||
|
|
||||||
What it does:
|
What it does:
|
||||||
1. Scans every file under static/ for fas fa-<name> occurrences.
|
1. Scans every file under static/ for fas fa-<name> occurrences.
|
||||||
2. Reads the _ICONS registry from static/js/core/icons.js.
|
2. Reads the OxiIcons registry from static/js/core/icons.js.
|
||||||
3. For each icon name that is missing from the registry, looks up
|
3. For each icon name that is missing from the registry, looks up
|
||||||
tmp/Font-Awesome/svgs/solid/<name>.svg
|
tmp/Font-Awesome/svgs/solid/<name>.svg
|
||||||
and adds the entry '<name>': [<width>, '<path>'] to _ICONS.
|
and adds the entry '<name>': [<width>, '<path>'] to OxiIcons.
|
||||||
4. Rewrites icons.js in-place (unless --dry-run is given).
|
4. Rewrites icons.js in-place (unless --dry-run is given).
|
||||||
|
|
||||||
FontAwesome source: tmp/Font-Awesome/svgs/solid/
|
FontAwesome source: tmp/Font-Awesome/svgs/solid/
|
||||||
@@ -75,15 +75,15 @@ for path in sorted(STATIC_DIR.rglob("*")):
|
|||||||
|
|
||||||
print(f"Found {len(used_icons)} distinct FA icon(s) referenced in static/")
|
print(f"Found {len(used_icons)} distinct FA icon(s) referenced in static/")
|
||||||
|
|
||||||
# ── 2. Parse existing _ICONS keys from icons.js ───────────────────────────────
|
# ── 2. Parse existing OxiIcons keys from icons.js ───────────────────────────────
|
||||||
icons_src = ICONS_JS.read_text(encoding="utf-8")
|
icons_src = ICONS_JS.read_text(encoding="utf-8")
|
||||||
|
|
||||||
# Extract only the _ICONS object body so we never accidentally match keys from
|
# Extract only the OxiIcons object body so we never accidentally match keys from
|
||||||
# other objects or functions elsewhere in the file.
|
# other objects or functions elsewhere in the file.
|
||||||
_ICONS_BLOCK_RE = re.compile(r'const _ICONS\s*=\s*\{(.*?)^};', re.DOTALL | re.MULTILINE)
|
_ICONS_BLOCK_RE = re.compile(r'const OxiIcons\s*=\s*\{(.*?)^};', re.DOTALL | re.MULTILINE)
|
||||||
block_match = _ICONS_BLOCK_RE.search(icons_src)
|
block_match = _ICONS_BLOCK_RE.search(icons_src)
|
||||||
if not block_match:
|
if not block_match:
|
||||||
print("✗ Could not locate 'const _ICONS = { … };' in icons.js — aborting.")
|
print("✗ Could not locate 'const OxiIcons = { … };' in icons.js — aborting.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
icons_block = block_match.group(1)
|
icons_block = block_match.group(1)
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ icons_block = block_match.group(1)
|
|||||||
ICON_KEY_RE = re.compile(r"""['"]?([\w-]+)['"]?\s*:\s*\[""")
|
ICON_KEY_RE = re.compile(r"""['"]?([\w-]+)['"]?\s*:\s*\[""")
|
||||||
registered: set[str] = {m.group(1) for m in ICON_KEY_RE.finditer(icons_block)}
|
registered: set[str] = {m.group(1) for m in ICON_KEY_RE.finditer(icons_block)}
|
||||||
|
|
||||||
print(f"Registry has {len(registered)} icon(s) in _ICONS")
|
print(f"Registry has {len(registered)} icon(s) in OxiIcons")
|
||||||
|
|
||||||
# ── 3. Find missing icons ─────────────────────────────────────────────────────
|
# ── 3. Find missing icons ─────────────────────────────────────────────────────
|
||||||
missing = {name: files for name, files in used_icons.items() if name not in registered}
|
missing = {name: files for name, files in used_icons.items() if name not in registered}
|
||||||
@@ -150,12 +150,12 @@ for name, width, d in new_entries:
|
|||||||
|
|
||||||
insert_block = "\n".join(insert_lines) + "\n"
|
insert_block = "\n".join(insert_lines) + "\n"
|
||||||
|
|
||||||
# Insert just before the closing "};" of _ICONS (line 396 area)
|
# Insert just before the closing "};" of OxiIcons (line 396 area)
|
||||||
# Anchor: the line that is exactly "};"
|
# Anchor: the line that is exactly "};"
|
||||||
ICONS_END_RE = re.compile(r'^};$', re.MULTILINE)
|
ICONS_END_RE = re.compile(r'^};$', re.MULTILINE)
|
||||||
m = ICONS_END_RE.search(icons_src)
|
m = ICONS_END_RE.search(icons_src)
|
||||||
if not m:
|
if not m:
|
||||||
print("\n✗ Could not locate the closing '}; ' of _ICONS in icons.js — aborting.")
|
print("\n✗ Could not locate the closing '}; ' of OxiIcons in icons.js — aborting.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Ensure the last existing entry has a trailing comma before we append.
|
# Ensure the last existing entry has a trailing comma before we append.
|
||||||
|
|||||||
Reference in New Issue
Block a user