Merge pull request #405 from EdouardVanbelle/feat-light-dark-like_os
This commit is contained in:
@@ -676,12 +676,13 @@ pub async fn list_my_shares(
|
||||
file_idx += 1;
|
||||
match result {
|
||||
Ok(file_dto) => {
|
||||
// Caller is the granter — they had share-access to the
|
||||
// resource, so the containing hierarchy is already known
|
||||
// to them. Keep `path` (unlike list_shared_with_me).
|
||||
items.push(OutgoingResourceItemDto {
|
||||
resource_type: ResourceTypeDto::File,
|
||||
first_shared_at: summary.first_shared_at,
|
||||
resource: ResourceContentDto::File(
|
||||
file_dto.clone().without_hierarchy_info(),
|
||||
),
|
||||
resource: ResourceContentDto::File(file_dto.clone()),
|
||||
grants,
|
||||
});
|
||||
}
|
||||
@@ -708,9 +709,7 @@ pub async fn list_my_shares(
|
||||
items.push(OutgoingResourceItemDto {
|
||||
resource_type: ResourceTypeDto::Folder,
|
||||
first_shared_at: summary.first_shared_at,
|
||||
resource: ResourceContentDto::Folder(
|
||||
folder_dto.clone().without_hierarchy_info(),
|
||||
),
|
||||
resource: ResourceContentDto::Folder(folder_dto.clone()),
|
||||
grants,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<!-- Declares the page supports both modes. Switching between them is
|
||||
driven by the CSS `color-scheme` property (via the `data-color-scheme`
|
||||
attribute on <html> set by theme-init.js), not by mutating this tag. -->
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
<title>OxiCloud — Admin Panel</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/logo/logo-plain.svg">
|
||||
<script src="/js/core/theme-init.js"></script>
|
||||
|
||||
+118
-84
@@ -1,49 +1,72 @@
|
||||
/*
|
||||
* OxiCloud design tokens.
|
||||
*
|
||||
* Single source of truth for light + dark colours. Each token whose value
|
||||
* differs between modes uses `light-dark(LIGHT, DARK)`, which the browser
|
||||
* resolves against the page's `color-scheme`.
|
||||
*
|
||||
* Mode switching:
|
||||
* • `<meta name="color-scheme" content="light dark">` in <head> declares
|
||||
* both schemes are supported.
|
||||
* • `:root { color-scheme: light dark }` (default) lets the UA follow the
|
||||
* OS preference (`prefers-color-scheme`).
|
||||
* • `html[data-color-scheme="light"]` / `…="dark"` force a specific mode.
|
||||
* `theme-init.js` sets the attribute from localStorage.
|
||||
*
|
||||
* Fallback: browsers that don't support `light-dark()` (Chrome < 123 /
|
||||
* Safari < 17.5 / Firefox < 120) hit a `@supports not (...)` block in
|
||||
* `themes/dark.css` that still applies the old `[data-theme="dark"]` overrides.
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* Default: follow the OS preference. Overridden by html[data-color-scheme]. */
|
||||
color-scheme: light dark;
|
||||
|
||||
--sidebar-width: 250px;
|
||||
|
||||
/* Backgrounds */
|
||||
--color-bg-page: #f5f7fa;
|
||||
--color-bg-surface: #ffffff;
|
||||
--color-bg-input: #f9fafb;
|
||||
--color-bg-hover: #f8fafc;
|
||||
--color-bg-muted: #f0f3f7;
|
||||
--color-bg-subtle: #f8f9fa;
|
||||
--color-bg-alt: #f7fafc;
|
||||
--color-bg-input-alt: #edf2f7;
|
||||
--color-bg-empty: #f0f0f0;
|
||||
--color-bg-page: light-dark(#f5f7fa, #0f172a);
|
||||
--color-bg-surface: light-dark(#ffffff, #1e293b);
|
||||
--color-bg-input: light-dark(#f9fafb, #0f172a);
|
||||
--color-bg-hover: light-dark(#f8fafc, #334155);
|
||||
--color-bg-muted: light-dark(#f0f3f7, #1a2540);
|
||||
--color-bg-subtle: light-dark(#f8f9fa, #162032);
|
||||
--color-bg-alt: light-dark(#f7fafc, #0f172a);
|
||||
--color-bg-input-alt: light-dark(#edf2f7, #253045);
|
||||
--color-bg-empty: light-dark(#f0f0f0, #253045);
|
||||
|
||||
/* Borders */
|
||||
--color-border: #e2e8f0;
|
||||
--color-border-light: #f1f5f9;
|
||||
--color-border-medium: #cbd5e0;
|
||||
--color-border-faint: #e0e6ed;
|
||||
--color-border-subtle: #e0e5e8;
|
||||
--color-border-xfaint: #f0f0f0;
|
||||
--color-border-ddd: #ddd;
|
||||
--color-border: light-dark(#e2e8f0, #334155);
|
||||
--color-border-light: light-dark(#f1f5f9, #334155);
|
||||
--color-border-medium: light-dark(#cbd5e0, #475569);
|
||||
--color-border-faint: light-dark(#e0e6ed, #2a3650);
|
||||
--color-border-subtle: light-dark(#e0e5e8, #2a3650);
|
||||
--color-border-xfaint: light-dark(#f0f0f0, #1e293b);
|
||||
--color-border-ddd: light-dark(#ddd, #334155);
|
||||
|
||||
/* Text */
|
||||
--color-text: #2d3748;
|
||||
--color-text-heading: #1e293b;
|
||||
--color-text-muted: #718096;
|
||||
--color-text-faint: #94a3b8;
|
||||
--color-text-secondary: #4a5568;
|
||||
--color-text-subtle: #64748b;
|
||||
--color-text-dark: #334155;
|
||||
--color-text-placeholder: #a0aec0;
|
||||
--color-text-gray: #6c757d;
|
||||
--color-text-medium: #666;
|
||||
--color-text-faint2: #888;
|
||||
--color-text-light: #999;
|
||||
--color-text-black: #333;
|
||||
--color-text-dim: #555;
|
||||
--color-text: light-dark(#2d3748, #e2e8f0);
|
||||
--color-text-heading: light-dark(#1e293b, #f1f5f9);
|
||||
--color-text-muted: light-dark(#718096, #94a3b8);
|
||||
--color-text-faint: light-dark(#94a3b8, #7a8a9f);
|
||||
--color-text-secondary: light-dark(#4a5568, #cbd5e1);
|
||||
--color-text-subtle: light-dark(#64748b, #94a3b8);
|
||||
--color-text-dark: light-dark(#334155, #cbd5e1);
|
||||
--color-text-placeholder: light-dark(#a0aec0, #64748b);
|
||||
--color-text-gray: light-dark(#6c757d, #94a3b8);
|
||||
--color-text-medium: light-dark(#666, #94a3b8);
|
||||
--color-text-faint2: light-dark(#888, #64748b);
|
||||
--color-text-light: light-dark(#999, #64748b);
|
||||
--color-text-black: light-dark(#333, #e2e8f0);
|
||||
--color-text-dim: light-dark(#555, #94a3b8);
|
||||
|
||||
/* Accent */
|
||||
/* Accent (orange) — mostly mode-agnostic. */
|
||||
--color-accent: #ff5e3a;
|
||||
--color-accent-hover: #e04520;
|
||||
--color-accent-hover: light-dark(#e04520, #ff7a5c);
|
||||
--color-accent-gradient: linear-gradient(135deg, #ff5e3a 0%, #ff2d55 100%);
|
||||
--color-accent-shadow: rgba(255, 94, 58, 0.3);
|
||||
--color-accent-ring: rgba(255, 94, 58, 0.1);
|
||||
--color-accent-tint: #fff5f3;
|
||||
--color-accent-ring: light-dark(rgba(255, 94, 58, 0.1), rgba(255, 94, 58, 0.15));
|
||||
--color-accent-tint: light-dark(#fff5f3, #2a1a15);
|
||||
--color-accent-mid: #ff8a5c;
|
||||
--color-accent-shadow-lg: rgba(255, 94, 58, 0.4);
|
||||
--color-accent-bg: rgba(255, 94, 58, 0.06);
|
||||
@@ -54,15 +77,15 @@
|
||||
--color-accent-ring-xs: rgba(255, 94, 58, 0.05);
|
||||
|
||||
/* Feedback */
|
||||
--color-error-bg: #fee2e2;
|
||||
--color-error-text: #b91c1c;
|
||||
--color-success-bg: #dcfce7;
|
||||
--color-success-text: #15803d;
|
||||
--color-error-bg: light-dark(#fee2e2, #3b1111);
|
||||
--color-error-text: light-dark(#b91c1c, #fca5a5);
|
||||
--color-success-bg: light-dark(#dcfce7, #052e16);
|
||||
--color-success-text: light-dark(#15803d, #86efac);
|
||||
--color-success-border: #48bb78;
|
||||
--color-success-alt: #34c759;
|
||||
--color-success-bg-alt: #e6f4ea;
|
||||
--color-success-bg-alt: light-dark(#e6f4ea, #0a2015);
|
||||
--color-success-text-alt: #0d904f;
|
||||
--color-success-bg-green: #e8f5e9;
|
||||
--color-success-bg-green: light-dark(#e8f5e9, #0a2015);
|
||||
--color-success-text-green: #4eaa25;
|
||||
|
||||
/* Dangerous actions */
|
||||
@@ -72,48 +95,48 @@
|
||||
--color-danger-alt: #ef4444;
|
||||
--color-danger-ring: rgba(239, 68, 68, 0.3);
|
||||
--color-danger-ring-lg: rgba(239, 68, 68, 0.4);
|
||||
--color-danger-light-bg: #fef2f2;
|
||||
--color-danger-lighter: #fef2f2;
|
||||
--color-danger-light-bg: light-dark(#fef2f2, #2a0c0c);
|
||||
--color-danger-lighter: light-dark(#fef2f2, #2a0c0c);
|
||||
--color-danger-text-alt: #e53e3e;
|
||||
--color-danger-gradient: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
||||
|
||||
/* Warning/feedback */
|
||||
--color-warning-bg: #ffeaa7;
|
||||
--color-warning-bg-dark: #fdcb6e;
|
||||
--color-warning-bg: light-dark(#ffeaa7, #3d2e00);
|
||||
--color-warning-bg-dark: light-dark(#fdcb6e, #5a4200);
|
||||
--color-warning-text: #ffc107;
|
||||
--color-warning-border: #ffc107;
|
||||
--color-warning-ring: rgba(255, 193, 7, 0.1);
|
||||
--color-warning-shadow: rgba(255, 193, 7, 0.5);
|
||||
--color-warning-orange-bg: #fff3e0;
|
||||
--color-warning-orange-bg: light-dark(#fff3e0, #2a1c10);
|
||||
--color-warning-orange-border: #f57c00;
|
||||
--color-warning-orange-text: #f57c00;
|
||||
--color-warning-bg-light: #fef3c7;
|
||||
--color-warning-text-amber: #f59e0b;
|
||||
--color-warning-orange-text: light-dark(#f57c00, #fb923c);
|
||||
--color-warning-bg-light: light-dark(#fef3c7, #2a2410);
|
||||
--color-warning-text-amber: light-dark(#f59e0b, #fbbf24);
|
||||
--color-warning-bg-orange: #fef3e2;
|
||||
--color-warning-text-orange: #d04423;
|
||||
|
||||
/* Info */
|
||||
--color-info-bg: #ebf5fb;
|
||||
--color-info-bg: light-dark(#ebf5fb, #0c1e35);
|
||||
--color-info-border: #2b6cb0;
|
||||
--color-info-text: #2b6cb0;
|
||||
--color-info-bg-alt: #e0ecff;
|
||||
--color-info-bg-alt: light-dark(#e0ecff, #0c1e35);
|
||||
--color-info-text-alt: #3171d8;
|
||||
--color-info-surface: #e0e7ff;
|
||||
--color-info-surface: light-dark(#e0e7ff, #0c1e35);
|
||||
--color-info-blue: #3b82f6;
|
||||
|
||||
/* Shadows */
|
||||
--color-shadow: rgba(0, 0, 0, 0.1);
|
||||
--color-shadow-lg: rgba(0, 0, 0, 0.12);
|
||||
/* Shadows — slightly stronger in dark mode for parity. */
|
||||
--color-shadow: light-dark(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.3));
|
||||
--color-shadow-lg: light-dark(rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.3));
|
||||
--color-shadow-xs: rgba(0, 0, 0, 0.05);
|
||||
--color-shadow-sm: rgba(0, 0, 0, 0.08);
|
||||
--color-shadow-md: rgba(0, 0, 0, 0.15);
|
||||
--color-shadow-md: light-dark(rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.3));
|
||||
--color-shadow-xl: rgba(0, 0, 0, 0.2);
|
||||
--color-shadow-2xl: rgba(0, 0, 0, 0.25);
|
||||
--color-shadow-3xl: rgba(0, 0, 0, 0.3);
|
||||
--color-shadow-4xl: rgba(0, 0, 0, 0.4);
|
||||
--color-shadow-5xl: rgba(0, 0, 0, 0.5);
|
||||
|
||||
/* Overlays */
|
||||
/* Overlays — same in both modes; they sit on top of arbitrary content. */
|
||||
--color-overlay: rgba(0, 0, 0, 0.5);
|
||||
--color-overlay-light: rgba(0, 0, 0, 0.45);
|
||||
--color-overlay-heavy: rgba(0, 0, 0, 0.85);
|
||||
@@ -129,15 +152,15 @@
|
||||
/* Items */
|
||||
--color-item: var(--color-bg-surface);
|
||||
--color-item-hover: var(--color-bg-hover);
|
||||
--color-item-active: #f8d2ae;
|
||||
--color-item-selected: #fff8f6;
|
||||
--color-item-hover-accent: #fff0ec;
|
||||
--color-item-active: light-dark(#f8d2ae, #5a5047);
|
||||
--color-item-selected: light-dark(#fff8f6, #39281a);
|
||||
--color-item-hover-accent: light-dark(#fff0ec, #3d342c);
|
||||
--color-item-hover-blue: #f0f8ff;
|
||||
--color-item-hover-sky: #e0f2fe;
|
||||
|
||||
/* Sidebar — always dark */
|
||||
--color-sidebar-bg-from: #2a3042;
|
||||
--color-sidebar-bg-to: #232838;
|
||||
/* Sidebar — already dark-leaning in both modes; dark mode goes deeper. */
|
||||
--color-sidebar-bg-from: light-dark(#2a3042, #0f172a);
|
||||
--color-sidebar-bg-to: light-dark(#232838, #0c1322);
|
||||
--color-sidebar-text: rgba(255, 255, 255, 0.65);
|
||||
--color-sidebar-text-hover: rgba(255, 255, 255, 0.9);
|
||||
--color-sidebar-text-active: #ffffff;
|
||||
@@ -236,7 +259,7 @@
|
||||
--color-multiselect-danger-text-active: #ffffff;
|
||||
|
||||
/* Notification */
|
||||
--color-notification-bg: #ffffff;
|
||||
--color-notification-bg: light-dark(#ffffff, #1e293b);
|
||||
--color-notification-badge: #ff3b30;
|
||||
--color-notification-success: #34c759;
|
||||
--color-notification-error: #ff3b30;
|
||||
@@ -252,7 +275,7 @@
|
||||
--color-lightbox-text-muted: rgba(255, 255, 255, 0.7);
|
||||
|
||||
/* Purple/special */
|
||||
--color-purple-bg: #f3e8ff;
|
||||
--color-purple-bg: light-dark(#f3e8ff, #1a0a33);
|
||||
--color-purple-border: #7c3aed;
|
||||
--color-purple-text: #7c3aed;
|
||||
--color-purple-bg-gradient: linear-gradient(135deg, #e0e7ff 0%, #ede9fe 50%, #fce7f3 100%);
|
||||
@@ -275,14 +298,14 @@
|
||||
|
||||
/* Content area */
|
||||
--color-content-muted: #888;
|
||||
--color-content-bg-warn: #ffeaa7;
|
||||
--color-content-bg-warn-dark: #fdcb6e;
|
||||
--color-content-bg-warn: light-dark(#ffeaa7, #3d2e00);
|
||||
--color-content-bg-warn-dark: light-dark(#fdcb6e, #5a4200);
|
||||
--color-content-debug-bg: red;
|
||||
--color-content-debug-text: white;
|
||||
|
||||
/* User menu */
|
||||
--color-user-menu-header-bg: linear-gradient(135deg, #fef5f3 0%, #fdf2f8 100%);
|
||||
--color-user-menu-header-border: #fce7e1;
|
||||
--color-user-menu-header-bg: light-dark(linear-gradient(135deg, #fef5f3 0%, #fdf2f8 100%), linear-gradient(135deg, #1a2332 0%, #1e2940 100%));
|
||||
--color-user-menu-header-border: light-dark(#fce7e1, #3a2520);
|
||||
|
||||
/* Share dialog */
|
||||
--color-share-link-text: #1565c0;
|
||||
@@ -291,8 +314,8 @@
|
||||
--color-share-owner-text: #757575;
|
||||
|
||||
/* Primary (style.css) */
|
||||
--color-primary: #2563eb;
|
||||
--color-primary-hover: #1d4ed8;
|
||||
--color-primary: light-dark(#2563eb, #60a5fa);
|
||||
--color-primary-hover: light-dark(#1d4ed8, #6fa8ee);
|
||||
|
||||
/* Recent view */
|
||||
--color-recent-muted: #6c757d;
|
||||
@@ -367,13 +390,13 @@
|
||||
--color-storage-fill-orange: linear-gradient(90deg, #d97706, #f59e0b);
|
||||
--color-storage-fill-red: linear-gradient(90deg, #dc2626, #ef4444);
|
||||
|
||||
/* Error text (dark shade) */
|
||||
--color-error-text-dark: #991b1b;
|
||||
/* Error text (dark shade) — light-mode is dark red, dark-mode is light red. */
|
||||
--color-error-text-dark: light-dark(#991b1b, #f87171);
|
||||
|
||||
/* Stat warning border */
|
||||
--color-stat-warn-border: #fbbf24;
|
||||
|
||||
/* Status badge — light mode (success/emerald) */
|
||||
/* Status badge — success/emerald */
|
||||
--color-badge-success-bg: #ecfdf5;
|
||||
--color-badge-success-bg-medium: #d1fae5;
|
||||
--color-badge-success-text: #065f46;
|
||||
@@ -384,32 +407,33 @@
|
||||
--color-badge-green-bg: #ecfdf5;
|
||||
--color-badge-green-text: #065f46;
|
||||
|
||||
/* Status badge — light mode (orange/coral) */
|
||||
--color-badge-orange-bg: #fff5f3;
|
||||
--color-badge-orange-text: #ff5e3a;
|
||||
/* Status badge — orange/coral (used by role-chip & user-vignette) */
|
||||
--color-badge-orange-bg: light-dark(#fff5f3, #2a1814);
|
||||
--color-badge-orange-text: light-dark(#ff5e3a, #ff8a65);
|
||||
|
||||
/* Status badge — light mode (error/red) */
|
||||
/* Status badge — error/red */
|
||||
--color-badge-error-border: #fecaca;
|
||||
|
||||
/* Status badge — light mode (warning/amber) */
|
||||
/* Status badge — warning/amber */
|
||||
--color-badge-warning-text: #92400e;
|
||||
--color-badge-warning-border: #fde68a;
|
||||
--color-badge-amber-bg: #fef3c7;
|
||||
--color-badge-amber-text: #f59e0b;
|
||||
|
||||
/* Status badge — light mode (indigo/purple) */
|
||||
/* Status badge — indigo/purple */
|
||||
--color-badge-indigo-bg: #ede9fe;
|
||||
--color-badge-indigo-text: #6d28d9;
|
||||
|
||||
/* Status badge — light mode (blue) */
|
||||
--color-badge-blue-bg: #eff6ff;
|
||||
--color-badge-blue-text: #1e40af;
|
||||
/* Status badge — blue (used by role-chip & user-vignette) */
|
||||
--color-badge-blue-bg: light-dark(#eff6ff, #0c2d48);
|
||||
--color-badge-blue-text: light-dark(#1e40af, #93c5fd);
|
||||
--color-badge-blue-border: #bfdbfe;
|
||||
|
||||
/* Status badge — light mode (gray/disabled) */
|
||||
/* Status badge — gray/disabled */
|
||||
--color-badge-gray: #d1d5db;
|
||||
|
||||
/* FIXME: Status badge — dark mode */
|
||||
/* FIXME: legacy dark-mode badge tokens — kept for now; can be folded into
|
||||
* the main badges once their last consumers migrate. */
|
||||
--color-badge-success-dark-bg: #052e16;
|
||||
--color-badge-success-dark-border: #065f46;
|
||||
--color-badge-error-dark-bg: #3b1111;
|
||||
@@ -439,3 +463,13 @@
|
||||
--color-video-play: white;
|
||||
--color-video-play-shadow: black;
|
||||
}
|
||||
|
||||
/* Explicit user choice overrides the OS preference. `theme-init.js` writes
|
||||
* the attribute from localStorage on render-blocking startup. */
|
||||
html[data-color-scheme="light"] {
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
html[data-color-scheme="dark"] {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
@@ -1,29 +1,19 @@
|
||||
/* ── Item tooltip — "technical sheet" ─────────────────────────────────────── */
|
||||
/* */
|
||||
/* Overlays the search bar area in the top-bar while hovering a file item. */
|
||||
/* Spans from the sidebar right-edge to just before .user-controls. */
|
||||
/* Absolutely overlays `.search-slot` in the top-bar. The search bar stays in */
|
||||
/* place underneath; only the tooltip's opacity is animated, so there is no */
|
||||
/* second element to keep in sync and no display:none flicker between rapid */
|
||||
/* hover transitions. RTL just works (inset:0). */
|
||||
/* */
|
||||
/* 3-column CSS grid: [icon] [label] [value] */
|
||||
/* All values start at the same x position regardless of label width. */
|
||||
/* 3-column CSS grid: [icon] [label] [value] */
|
||||
/* All values start at the same x position regardless of label width. */
|
||||
|
||||
.path-tooltip {
|
||||
position: fixed;
|
||||
|
||||
/* Fill most of the top-bar (topbar height 70px, 8px margin top/bottom) */
|
||||
top: 8px;
|
||||
|
||||
/* Desktop: starts at sidebar right edge + top-bar padding */
|
||||
left: calc(var(--sidebar-width) + 30px);
|
||||
|
||||
/*
|
||||
* Right edge stops just before .user-controls:
|
||||
* 30px (top-bar right padding) + ~90px (notif bell + gap + avatar btn) + 4px gap
|
||||
* Using left + right instead of width so the browser computes it dynamically.
|
||||
*/
|
||||
right: 124px;
|
||||
|
||||
/* High enough to sit above the top-bar content */
|
||||
z-index: 200;
|
||||
/* Cover the search slot, sit above it (later in DOM order). */
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
/* Opaque background so the search bar underneath is fully masked. */
|
||||
background-color: var(--color-bg-surface);
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 1em max-content 1fr;
|
||||
@@ -31,20 +21,24 @@
|
||||
row-gap: 6px;
|
||||
align-items: center;
|
||||
|
||||
/* Fill the top-bar height minus an 8px margin on each side */
|
||||
height: 54px;
|
||||
padding: 0 16px;
|
||||
border-radius: 10px;
|
||||
border: 2px solid var(--color-border-medium);
|
||||
|
||||
background-color: var(--color-bg-input);
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.75rem;
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
/* avoid blinking when pointer moves between items */
|
||||
transition: display 0.2s allow-discrete;
|
||||
/* Show instantly. The hide direction (.hidden) overrides this with a
|
||||
1s delay so the tooltip lingers after the mouse leaves an item;
|
||||
rapid item-to-item moves cancel the pending fade before it fires. */
|
||||
opacity: 1;
|
||||
transition: opacity 0.2s ease 0s;
|
||||
}
|
||||
|
||||
/* Hidden state — override the global `.hidden { display:none !important }`
|
||||
so we can fade with opacity instead of snapping to display:none. */
|
||||
.path-tooltip.hidden {
|
||||
display: grid !important;
|
||||
opacity: 0;
|
||||
transition-delay: 1s;
|
||||
}
|
||||
|
||||
/* Icon column */
|
||||
@@ -85,23 +79,6 @@
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* ── Narrow screens (sidebar hidden) ────────────────────────────────────── */
|
||||
/* On mobile the sidebar slides off-screen; align after the #sidebar-toggle */
|
||||
/* button (≈44px) that takes its place in the top-bar. */
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.path-tooltip {
|
||||
/* 16px topbar padding + ~44px sidebar-toggle + 8px gap */
|
||||
left: 68px;
|
||||
|
||||
/*
|
||||
* Right edge stops before search-toggle-btn + user-controls:
|
||||
* ~40px (search-toggle) + 12px (gap) + ~90px (user-controls) + 16px (padding)
|
||||
*/
|
||||
right: 158px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Vignette inside tooltip ─────────────────────────────────────────────── */
|
||||
/* The vignette name hard-codes 14px; scale it down to match the tooltip. */
|
||||
|
||||
|
||||
@@ -182,34 +182,58 @@
|
||||
color: var(--color-text-subtle);
|
||||
}
|
||||
|
||||
.user-menu-item .theme-toggle-pill {
|
||||
/* The appearance row is a plain container, not a button — the inner
|
||||
* segmented control captures the clicks. Match the height of other
|
||||
* .user-menu-item rows so the row reads as part of the same list. */
|
||||
.user-menu-item--theme {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.user-menu-item--theme:hover {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Light / Like OS / Dark — three-option pill, active option highlighted
|
||||
* with the accent colour. Sits at the right edge of the row. */
|
||||
.theme-segmented {
|
||||
margin-left: auto;
|
||||
width: 40px;
|
||||
display: inline-flex;
|
||||
background: var(--color-bg-muted);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 999px;
|
||||
padding: 2px;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.theme-segmented__opt {
|
||||
width: 28px;
|
||||
height: 22px;
|
||||
background: var(--color-border);
|
||||
border-radius: 11px;
|
||||
position: relative;
|
||||
transition: background 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--color-text-subtle);
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
padding: 0;
|
||||
transition:
|
||||
background 0.15s ease,
|
||||
color 0.15s ease;
|
||||
}
|
||||
|
||||
.user-menu-item .theme-toggle-pill.active {
|
||||
.theme-segmented__opt:hover {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.theme-segmented__opt--active {
|
||||
background: var(--color-accent);
|
||||
color: var(--color-danger-text);
|
||||
}
|
||||
|
||||
.user-menu-item .theme-toggle-knob {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background: var(--color-danger-text);
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
transition: transform 0.3s ease;
|
||||
box-shadow: 0 1px 4px var(--color-shadow-md);
|
||||
}
|
||||
|
||||
.user-menu-item .theme-toggle-pill.active .theme-toggle-knob {
|
||||
transform: translateX(18px);
|
||||
.theme-segmented__opt--active:hover {
|
||||
color: var(--color-danger-text);
|
||||
}
|
||||
|
||||
.user-menu-admin {
|
||||
|
||||
@@ -86,11 +86,14 @@
|
||||
color: var(--color-text-heading);
|
||||
}
|
||||
|
||||
.search-container {
|
||||
/* Slot wrapping .search-container + #path-tooltip — it owns the flex slot
|
||||
so the tooltip can absolutely overlay the search bar via inset:0 instead
|
||||
of viewport-fixed positioning. */
|
||||
.search-slot {
|
||||
flex-grow: 1;
|
||||
max-width: 600px;
|
||||
position: relative;
|
||||
margin-right: 20px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -100,6 +103,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.search-container {
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-container input {
|
||||
width: 100%;
|
||||
padding: 12px 50px 12px 44px;
|
||||
@@ -204,8 +214,8 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Hide full search bar on mobile by default */
|
||||
.search-container {
|
||||
/* Hide full search slot on mobile by default */
|
||||
.search-slot {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -224,7 +234,7 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.top-bar--search-active .search-container {
|
||||
.top-bar--search-active .search-slot {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
max-width: none;
|
||||
|
||||
+140
-111
@@ -1,122 +1,151 @@
|
||||
[data-theme="dark"] {
|
||||
/* Backgrounds */
|
||||
--color-bg-page: #0f172a;
|
||||
--color-bg-surface: #1e293b;
|
||||
--color-bg-input: #0f172a;
|
||||
--color-bg-hover: #334155;
|
||||
--color-bg-muted: #1a2540;
|
||||
--color-bg-subtle: #162032;
|
||||
--color-bg-alt: #0f172a;
|
||||
--color-bg-input-alt: #253045;
|
||||
--color-bg-empty: #253045;
|
||||
/*
|
||||
* Legacy dark-mode fallback for browsers that don't support `light-dark()`
|
||||
* (Chrome < 123 / Safari < 17.5 / Firefox < 120, all from early 2024).
|
||||
*
|
||||
* Modern browsers skip this entire stylesheet at parse time thanks to the
|
||||
* `@supports not (color: light-dark(white, black))` gate — the merged tokens
|
||||
* in `base/variables.css` handle dark mode declaratively via the
|
||||
* `color-scheme` CSS property.
|
||||
*
|
||||
* On capable browsers this file becomes dead code; once the supported
|
||||
* baseline raises beyond the cut-off, it can be deleted.
|
||||
*
|
||||
* The selector matches BOTH attribute names so the fallback works during
|
||||
* the transitional window (when some users may still have `data-theme`
|
||||
* stamped from cached app state):
|
||||
* • [data-theme="dark"] — legacy attribute, never set by new code
|
||||
* • html[data-color-scheme="dark"] — new attribute set by theme-init.js
|
||||
*/
|
||||
|
||||
/* Borders */
|
||||
--color-border: #334155;
|
||||
--color-border-light: #334155;
|
||||
--color-border-medium: #475569;
|
||||
--color-border-faint: #2a3650;
|
||||
--color-border-subtle: #2a3650;
|
||||
--color-border-xfaint: #1e293b;
|
||||
--color-border-ddd: #334155;
|
||||
@supports not (color: light-dark(white, black)) {
|
||||
[data-theme="dark"],
|
||||
html[data-color-scheme="dark"] {
|
||||
/* Backgrounds */
|
||||
--color-bg-page: #0f172a;
|
||||
--color-bg-surface: #1e293b;
|
||||
--color-bg-input: #0f172a;
|
||||
--color-bg-hover: #334155;
|
||||
--color-bg-muted: #1a2540;
|
||||
--color-bg-subtle: #162032;
|
||||
--color-bg-alt: #0f172a;
|
||||
--color-bg-input-alt: #253045;
|
||||
--color-bg-empty: #253045;
|
||||
|
||||
/* Text */
|
||||
--color-text: #e2e8f0;
|
||||
--color-text-heading: #f1f5f9;
|
||||
--color-text-muted: #94a3b8;
|
||||
--color-text-faint: #7a8a9f;
|
||||
--color-text-secondary: #cbd5e1;
|
||||
--color-text-subtle: #94a3b8;
|
||||
--color-text-dark: #cbd5e1;
|
||||
--color-text-placeholder: #64748b;
|
||||
--color-text-gray: #94a3b8;
|
||||
--color-text-medium: #94a3b8;
|
||||
--color-text-faint2: #64748b;
|
||||
--color-text-light: #64748b;
|
||||
--color-text-black: #e2e8f0;
|
||||
--color-text-dim: #94a3b8;
|
||||
/* Borders */
|
||||
--color-border: #334155;
|
||||
--color-border-light: #334155;
|
||||
--color-border-medium: #475569;
|
||||
--color-border-faint: #2a3650;
|
||||
--color-border-subtle: #2a3650;
|
||||
--color-border-xfaint: #1e293b;
|
||||
--color-border-ddd: #334155;
|
||||
|
||||
/* Accent */
|
||||
--color-accent: #ff5e3a;
|
||||
--color-accent-hover: #ff7a5c;
|
||||
--color-accent-gradient: linear-gradient(135deg, #ff5e3a 0%, #ff2d55 100%);
|
||||
--color-accent-shadow: rgba(255, 94, 58, 0.3);
|
||||
--color-accent-ring: rgba(255, 94, 58, 0.15);
|
||||
--color-accent-tint: #2a1a15;
|
||||
/* Text */
|
||||
--color-text: #e2e8f0;
|
||||
--color-text-heading: #f1f5f9;
|
||||
--color-text-muted: #94a3b8;
|
||||
--color-text-faint: #7a8a9f;
|
||||
--color-text-secondary: #cbd5e1;
|
||||
--color-text-subtle: #94a3b8;
|
||||
--color-text-dark: #cbd5e1;
|
||||
--color-text-placeholder: #64748b;
|
||||
--color-text-gray: #94a3b8;
|
||||
--color-text-medium: #94a3b8;
|
||||
--color-text-faint2: #64748b;
|
||||
--color-text-light: #64748b;
|
||||
--color-text-black: #e2e8f0;
|
||||
--color-text-dim: #94a3b8;
|
||||
|
||||
/* Feedback */
|
||||
--color-error-bg: #3b1111;
|
||||
--color-error-text: #fca5a5;
|
||||
--color-success-bg: #052e16;
|
||||
--color-success-text: #86efac;
|
||||
/* Accent */
|
||||
--color-accent: #ff5e3a;
|
||||
--color-accent-hover: #ff7a5c;
|
||||
--color-accent-gradient: linear-gradient(135deg, #ff5e3a 0%, #ff2d55 100%);
|
||||
--color-accent-shadow: rgba(255, 94, 58, 0.3);
|
||||
--color-accent-ring: rgba(255, 94, 58, 0.15);
|
||||
--color-accent-tint: #2a1a15;
|
||||
|
||||
/* Shadows (stronger on dark mode) */
|
||||
--color-shadow: rgba(0, 0, 0, 0.3);
|
||||
--color-shadow-lg: rgba(0, 0, 0, 0.3);
|
||||
--color-shadow-md: rgba(0, 0, 0, 0.3);
|
||||
/* Feedback */
|
||||
--color-error-bg: #3b1111;
|
||||
--color-error-text: #fca5a5;
|
||||
--color-success-bg: #052e16;
|
||||
--color-success-text: #86efac;
|
||||
|
||||
/* Additional dark overrides */
|
||||
--color-warning-bg: #3d2e00;
|
||||
--color-warning-bg-dark: #5a4200;
|
||||
--color-notification-bg: #1e293b;
|
||||
--color-user-menu-header-bg: linear-gradient(135deg, #1a2332 0%, #1e2940 100%);
|
||||
--color-user-menu-header-border: #3a2520;
|
||||
--color-info-bg: #0c1e35;
|
||||
--color-info-bg-alt: #0c1e35;
|
||||
--color-info-surface: #0c1e35;
|
||||
--color-danger-light-bg: #2a0c0c;
|
||||
--color-danger-lighter: #2a0c0c;
|
||||
--color-error-text-dark: #f87171;
|
||||
/* Shadows (stronger on dark mode) */
|
||||
--color-shadow: rgba(0, 0, 0, 0.3);
|
||||
--color-shadow-lg: rgba(0, 0, 0, 0.3);
|
||||
--color-shadow-md: rgba(0, 0, 0, 0.3);
|
||||
|
||||
/* Pastel chip backgrounds need dark equivalents — otherwise the light
|
||||
* cream / white-blue / pink-cream backdrops glow against dark surfaces
|
||||
* and the saturated text colours become unreadable on the now-dark fill.
|
||||
* Same pattern: dark tinted background + lighter pastel text. */
|
||||
--color-badge-orange-bg: #2a1814;
|
||||
--color-badge-orange-text: #ff8a65;
|
||||
--color-badge-blue-bg: #0c2d48;
|
||||
--color-badge-blue-text: #93c5fd;
|
||||
--color-warning-bg-light: #2a2410;
|
||||
--color-warning-text-amber: #fbbf24;
|
||||
--color-warning-orange-bg: #2a1c10;
|
||||
--color-warning-orange-text: #fb923c;
|
||||
--color-purple-bg: #1a0a33;
|
||||
--color-success-bg-alt: #0a2015;
|
||||
--color-success-bg-green: #0a2015;
|
||||
--color-content-bg-warn: #3d2e00;
|
||||
--color-content-bg-warn-dark: #5a4200;
|
||||
/* Additional dark overrides */
|
||||
--color-warning-bg: #3d2e00;
|
||||
--color-warning-bg-dark: #5a4200;
|
||||
--color-notification-bg: #1e293b;
|
||||
--color-user-menu-header-bg: linear-gradient(135deg, #1a2332 0%, #1e2940 100%);
|
||||
--color-user-menu-header-border: #3a2520;
|
||||
--color-info-bg: #0c1e35;
|
||||
--color-info-bg-alt: #0c1e35;
|
||||
--color-info-surface: #0c1e35;
|
||||
--color-danger-light-bg: #2a0c0c;
|
||||
--color-danger-lighter: #2a0c0c;
|
||||
--color-error-text-dark: #f87171;
|
||||
|
||||
/* Sidebar */
|
||||
--color-sidebar-bg-from: #0f172a;
|
||||
--color-sidebar-bg-to: #0c1322;
|
||||
/* Pastel chip backgrounds need dark equivalents — otherwise the light
|
||||
* cream / white-blue / pink-cream backdrops glow against dark surfaces
|
||||
* and the saturated text colours become unreadable on the now-dark fill.
|
||||
* Same pattern: dark tinted background + lighter pastel text. */
|
||||
--color-badge-orange-bg: #2a1814;
|
||||
--color-badge-orange-text: #ff8a65;
|
||||
--color-badge-blue-bg: #0c2d48;
|
||||
--color-badge-blue-text: #93c5fd;
|
||||
--color-warning-bg-light: #2a2410;
|
||||
--color-warning-text-amber: #fbbf24;
|
||||
--color-warning-orange-bg: #2a1c10;
|
||||
--color-warning-orange-text: #fb923c;
|
||||
--color-purple-bg: #1a0a33;
|
||||
--color-success-bg-alt: #0a2015;
|
||||
--color-success-bg-green: #0a2015;
|
||||
--color-content-bg-warn: #3d2e00;
|
||||
--color-content-bg-warn-dark: #5a4200;
|
||||
|
||||
/* Items */
|
||||
--color-item-active: #5a5047;
|
||||
--color-item-selected: #39281a;
|
||||
--color-item-hover-accent: #3d342c;
|
||||
/* Sidebar */
|
||||
--color-sidebar-bg-from: #0f172a;
|
||||
--color-sidebar-bg-to: #0c1322;
|
||||
|
||||
/* Primary */
|
||||
/* TODO: rename into primary-blue ? */
|
||||
--color-primary: #60a5fa;
|
||||
--color-primary-hover: #6fa8ee;
|
||||
}
|
||||
|
||||
/* ------------ */
|
||||
|
||||
[data-theme="dark"] select {
|
||||
background-color: #1e293b;
|
||||
border-color: #334155;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
[data-theme="dark"] select:hover {
|
||||
border-color: #475569;
|
||||
}
|
||||
|
||||
[data-theme="dark"] select:focus {
|
||||
border-color: #ff5e3a;
|
||||
background-color: #1e293b;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .smd-expiry-date-input::-webkit-calendar-picker-indicator {
|
||||
filter: invert(1);
|
||||
/* Items */
|
||||
--color-item-active: #5a5047;
|
||||
--color-item-selected: #39281a;
|
||||
--color-item-hover-accent: #3d342c;
|
||||
|
||||
/* Primary */
|
||||
/* TODO: rename into primary-blue ? */
|
||||
--color-primary: #60a5fa;
|
||||
--color-primary-hover: #6fa8ee;
|
||||
}
|
||||
|
||||
/* ------------ */
|
||||
/* Native form controls need explicit dark styling on browsers without
|
||||
* `color-scheme` — on capable browsers the UA does this automatically
|
||||
* once `color-scheme: dark` is in effect. */
|
||||
|
||||
[data-theme="dark"] select,
|
||||
html[data-color-scheme="dark"] select {
|
||||
background-color: #1e293b;
|
||||
border-color: #334155;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
[data-theme="dark"] select:hover,
|
||||
html[data-color-scheme="dark"] select:hover {
|
||||
border-color: #475569;
|
||||
}
|
||||
|
||||
[data-theme="dark"] select:focus,
|
||||
html[data-color-scheme="dark"] select:focus {
|
||||
border-color: #ff5e3a;
|
||||
background-color: #1e293b;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .smd-expiry-date-input::-webkit-calendar-picker-indicator,
|
||||
html[data-color-scheme="dark"] .smd-expiry-date-input::-webkit-calendar-picker-indicator {
|
||||
filter: invert(1);
|
||||
}
|
||||
}
|
||||
|
||||
+36
-10
@@ -3,6 +3,10 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<!-- Declares the page supports both modes. Switching between them is
|
||||
driven by the CSS `color-scheme` property (via the `data-color-scheme`
|
||||
attribute on <html> set by theme-init.js), not by mutating this tag. -->
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<title data-i18n="app.title">OxiCloud</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/logo/logo-plain.svg">
|
||||
<!-- Apply saved theme immediately to prevent flash of light mode -->
|
||||
@@ -132,12 +136,17 @@
|
||||
<button class="search-back-btn" id="search-back-btn" aria-label="Close search">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
</button>
|
||||
<div class="search-container">
|
||||
<i class="fas fa-search search-icon"></i>
|
||||
<input type="text" id="search-input" data-i18n-placeholder="actions.search" placeholder="Search files, folders...">
|
||||
<button id="search-button" class="search-button" data-i18n-title="actions.search_btn" title="Search">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
<!-- Search slot — positioning context for the item tooltip, which
|
||||
absolutely overlays the search bar when an item is hovered. -->
|
||||
<div class="search-slot">
|
||||
<div class="search-container">
|
||||
<i class="fas fa-search search-icon"></i>
|
||||
<input type="text" id="search-input" data-i18n-placeholder="actions.search" placeholder="Search files, folders...">
|
||||
<button id="search-button" class="search-button" data-i18n-title="actions.search_btn" title="Search">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="path-tooltip" class="path-tooltip hidden" aria-hidden="true"></div>
|
||||
</div>
|
||||
|
||||
<div class="user-controls">
|
||||
@@ -195,13 +204,30 @@
|
||||
</button>
|
||||
<div class="user-menu-divider hidden" id="user-menu-admin-divider"></div>
|
||||
<div id="language-selector"></div>
|
||||
<button class="user-menu-item" id="user-menu-theme">
|
||||
<div class="user-menu-item user-menu-item--theme">
|
||||
<i class="fas fa-moon"></i>
|
||||
<span data-i18n="user_menu.appearance">Appearance</span>
|
||||
<div class="theme-toggle-pill" id="theme-toggle-pill">
|
||||
<div class="theme-toggle-knob"></div>
|
||||
<div class="theme-segmented" id="user-menu-theme-segmented" role="radiogroup" data-i18n-aria-label="user_menu.appearance" aria-label="Appearance">
|
||||
<button type="button" class="theme-segmented__opt" data-mode="light"
|
||||
role="radio" aria-checked="false"
|
||||
data-i18n-title="user_menu.theme.light" title="Light"
|
||||
data-i18n-aria-label="user_menu.theme.light" aria-label="Light">
|
||||
<i class="fas fa-sun"></i>
|
||||
</button>
|
||||
<button type="button" class="theme-segmented__opt" data-mode="auto"
|
||||
role="radio" aria-checked="false"
|
||||
data-i18n-title="user_menu.theme.auto" title="Like OS"
|
||||
data-i18n-aria-label="user_menu.theme.auto" aria-label="Like OS">
|
||||
<i class="fas fa-desktop"></i>
|
||||
</button>
|
||||
<button type="button" class="theme-segmented__opt" data-mode="dark"
|
||||
role="radio" aria-checked="false"
|
||||
data-i18n-title="user_menu.theme.dark" title="Dark"
|
||||
data-i18n-aria-label="user_menu.theme.dark" aria-label="Dark">
|
||||
<i class="fas fa-moon"></i>
|
||||
</button>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<button class="user-menu-item" id="user-menu-about">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<span data-i18n="user_menu.about">About OxiCloud</span>
|
||||
|
||||
@@ -33,8 +33,8 @@ import { uiNotifications } from './uiNotifications.js';
|
||||
/** @import {FileItem, FolderItem} from '../core/types.js' */
|
||||
|
||||
/**
|
||||
* @typedef {{ key: string, label: string, orderBy: string,
|
||||
* keyFn: (item: FileItem|FolderItem) => string|null,
|
||||
* @typedef {{ key: string, label: string, icon?: string, orderBy: string,
|
||||
* keyFn?: (item: FileItem|FolderItem) => string|null,
|
||||
* labelFn?: (key: string) => string }} GroupByDef
|
||||
*/
|
||||
|
||||
@@ -48,11 +48,21 @@ import { uiNotifications } from './uiNotifications.js';
|
||||
* @type {GroupByDef[]}
|
||||
*/
|
||||
const GROUP_BY_DEFS = [
|
||||
{
|
||||
key: '',
|
||||
get label() {
|
||||
return i18n.t('files.name', 'Name');
|
||||
},
|
||||
icon: 'fas fa-arrow-up-a-z',
|
||||
orderBy: 'name'
|
||||
// no keyFn → flat list.
|
||||
},
|
||||
{
|
||||
key: 'type',
|
||||
get label() {
|
||||
return i18n.t('groupby.type', 'Type');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'type',
|
||||
// Folders → 'Folder'; files → their pre-computed category string.
|
||||
keyFn: (item) => ('mime_type' in item ? /** @type {Record<string,string>} */ (/** @type {unknown} */ (item)).category || 'other' : 'Folder'),
|
||||
@@ -82,6 +92,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.size', 'Size');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'size',
|
||||
// sizeBucket(-1) → "Folders" sentinel; no labelFn needed.
|
||||
keyFn: (item) => {
|
||||
@@ -95,6 +106,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.modifiedAt', 'Modified date');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'modified_at',
|
||||
// keyFn returns the human-readable bucket; the bucket IS the key.
|
||||
keyFn: (item) => {
|
||||
@@ -107,6 +119,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.createdAt', 'Created date');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'created_at',
|
||||
keyFn: (item) => {
|
||||
const r = /** @type {Record<string, number>} */ (/** @type {unknown} */ (item));
|
||||
|
||||
+15
-13
@@ -86,7 +86,6 @@ const _toggleButtons = `
|
||||
<div class="group-by-selector hidden" id="group-by-selector">
|
||||
<button class="toggle-btn group-by-btn" id="group-by-btn"
|
||||
title="Group by" data-i18n-title="groupby.title">
|
||||
<i class="fas fa-layer-group"></i>
|
||||
<span class="group-by-label"></span>
|
||||
</button>
|
||||
<button class="toggle-btn sort-dir-btn" id="sort-dir-btn"
|
||||
@@ -168,7 +167,6 @@ const ACTIONS_BAR_TEMPLATES = {
|
||||
<div class="group-by-selector hidden" id="group-by-selector">
|
||||
<button class="toggle-btn group-by-btn" id="group-by-btn"
|
||||
title="Group by" data-i18n-title="groupby.title">
|
||||
<i class="fas fa-layer-group"></i>
|
||||
<span class="group-by-label"></span>
|
||||
</button>
|
||||
<button class="toggle-btn sort-dir-btn" id="sort-dir-btn"
|
||||
@@ -251,7 +249,7 @@ let _groupByDocumentClickHandler = null;
|
||||
* Must be called AFTER `setActionsBarMode()` so the selector elements exist
|
||||
* in the DOM.
|
||||
*
|
||||
* @param {Array<{key: string, label: string}>} [defs]
|
||||
* @param {Array<{key: string, label: string, icon?: string}>} [defs]
|
||||
*/
|
||||
function syncGroupByMenu(defs = []) {
|
||||
const selector = document.getElementById('group-by-selector');
|
||||
@@ -274,15 +272,18 @@ function syncGroupByMenu(defs = []) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Rebuild menu options — call i18n.t() directly so each label is resolved
|
||||
// at call time (translations are loaded by the time any section switch runs).
|
||||
// A def with key='' lets the section override the default "None" label.
|
||||
const noneOverride = defs.find((d) => d.key === '');
|
||||
const noneLabel = noneOverride ? noneOverride.label : i18n.t('groupby.none', 'None');
|
||||
menu.innerHTML = `<button class="group-by-option active" data-group-by="">${escapeHtml(noneLabel)}</button>`;
|
||||
// Rebuild menu options — call i18n.t() at call time so labels are
|
||||
// localised. Each def supplies its own icon. A section opts into an
|
||||
// ungrouped/sorted entry by including a def with key=''.
|
||||
menu.innerHTML = '';
|
||||
for (const def of defs) {
|
||||
if (def.key === '') continue;
|
||||
menu.insertAdjacentHTML('beforeend', `<button class="group-by-option" data-group-by="${escapeHtml(def.key)}">${escapeHtml(def.label)}</button>`);
|
||||
const iconClass = def.icon ?? 'fas fa-layer-group';
|
||||
menu.insertAdjacentHTML(
|
||||
'beforeend',
|
||||
`<button class="group-by-option" data-group-by="${escapeHtml(def.key)}">` +
|
||||
`<i class="${escapeHtml(iconClass)}"></i> ${escapeHtml(def.label)}` +
|
||||
`</button>`
|
||||
);
|
||||
}
|
||||
|
||||
// One stable document-level handler to close the menu on outside clicks.
|
||||
@@ -314,9 +315,10 @@ function setupActionsBarDelegation() {
|
||||
btn.classList.add('active');
|
||||
document.getElementById('group-by-menu')?.classList.add('hidden');
|
||||
const groupByBtn = document.getElementById('group-by-btn');
|
||||
groupByBtn?.classList.toggle('active', key !== '');
|
||||
// Always active — there's no neutral "None" state anymore.
|
||||
groupByBtn?.classList.add('active');
|
||||
const lbl = groupByBtn?.querySelector('.group-by-label');
|
||||
if (lbl) lbl.textContent = key !== '' ? (btn.textContent ?? '') : '';
|
||||
if (lbl) lbl.innerHTML = btn.innerHTML; // clone icon + label markup
|
||||
// Changing order-by dimension resets direction to ascending
|
||||
_groupByView?.setDirection(false);
|
||||
document.getElementById('sort-dir-btn')?.classList.remove('active');
|
||||
|
||||
@@ -417,6 +417,8 @@ function switchToTrashSection() {
|
||||
setActionsBarMode('trash');
|
||||
setGroupByView(trashView);
|
||||
syncGroupByMenu(trashView.groupByDefs);
|
||||
const trashPrefs = viewPrefs.load('trash');
|
||||
applyGroupByMenuState(trashPrefs.groupBy || 'remainingDays', trashPrefs.reversed);
|
||||
|
||||
//reset files view + remove any error
|
||||
ui.resetFilesList();
|
||||
|
||||
+38
-43
@@ -6,14 +6,13 @@ import { createUserVignette } from '../components/userVignette.js';
|
||||
import { getCsrfHeaders } from '../core/csrf.js';
|
||||
import { formatFileSize, formatQuotaSize } from '../core/formatters.js';
|
||||
import { i18n } from '../core/i18n.js';
|
||||
import { ui } from './ui.js';
|
||||
|
||||
function setupUserMenu() {
|
||||
const wrapper = document.getElementById('user-menu-wrapper');
|
||||
const avatarBtn = document.getElementById('user-avatar-btn');
|
||||
const menu = document.getElementById('user-menu');
|
||||
const logoutBtn = document.getElementById('user-menu-logout');
|
||||
const themeBtn = document.getElementById('user-menu-theme');
|
||||
const themeSegmented = document.getElementById('user-menu-theme-segmented');
|
||||
const aboutBtn = document.getElementById('user-menu-about');
|
||||
const adminBtn = document.getElementById('user-menu-admin');
|
||||
const adminDivider = document.getElementById('user-menu-admin-divider');
|
||||
@@ -65,53 +64,49 @@ function setupUserMenu() {
|
||||
});
|
||||
}
|
||||
|
||||
if (themeBtn) {
|
||||
const pill = document.getElementById('theme-toggle-pill');
|
||||
|
||||
// Sync pill UI with current theme state
|
||||
function syncThemePill() {
|
||||
const isDark = localStorage.getItem('oxicloud_theme') === 'dark';
|
||||
if (pill) {
|
||||
if (isDark) {
|
||||
pill.classList.add('active');
|
||||
} else {
|
||||
pill.classList.remove('active');
|
||||
}
|
||||
}
|
||||
// Ensure document theme matches localStorage
|
||||
if (isDark) {
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
} else {
|
||||
document.documentElement.removeAttribute('data-theme');
|
||||
}
|
||||
if (themeSegmented) {
|
||||
/**
|
||||
* Read the current selection. Returns `'auto'` when no explicit choice
|
||||
* is stored — that's the default and means "follow OS preference".
|
||||
* @returns {'light' | 'dark' | 'auto'}
|
||||
*/
|
||||
function currentMode() {
|
||||
const saved = localStorage.getItem('oxicloud_theme');
|
||||
return saved === 'light' || saved === 'dark' ? saved : 'auto';
|
||||
}
|
||||
|
||||
// Initialize pill state on load
|
||||
syncThemePill();
|
||||
|
||||
themeBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
// Toggle theme based on current state, not pill state
|
||||
const currentIsDark = localStorage.getItem('oxicloud_theme') === 'dark';
|
||||
const newIsDark = !currentIsDark;
|
||||
|
||||
localStorage.setItem('oxicloud_theme', newIsDark ? 'dark' : 'light');
|
||||
|
||||
if (newIsDark) {
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
/**
|
||||
* Apply a mode: persist it, stamp the html attribute (or remove it
|
||||
* for 'auto' so CSS `color-scheme: light dark` resolves via OS), and
|
||||
* highlight the matching button.
|
||||
* @param {'light' | 'dark' | 'auto'} mode
|
||||
*/
|
||||
function applyMode(mode) {
|
||||
if (mode === 'auto') {
|
||||
localStorage.setItem('oxicloud_theme', 'auto');
|
||||
document.documentElement.removeAttribute('data-color-scheme');
|
||||
} else {
|
||||
document.documentElement.removeAttribute('data-theme');
|
||||
localStorage.setItem('oxicloud_theme', mode);
|
||||
document.documentElement.setAttribute('data-color-scheme', mode);
|
||||
}
|
||||
themeSegmented.querySelectorAll('.theme-segmented__opt').forEach((b) => {
|
||||
const el = /** @type {HTMLElement} */ (b);
|
||||
const isActive = el.dataset.mode === mode;
|
||||
el.classList.toggle('theme-segmented__opt--active', isActive);
|
||||
el.setAttribute('aria-checked', String(isActive));
|
||||
});
|
||||
}
|
||||
|
||||
if (pill) {
|
||||
if (newIsDark) {
|
||||
pill.classList.add('active');
|
||||
} else {
|
||||
pill.classList.remove('active');
|
||||
}
|
||||
}
|
||||
// Initial highlight (theme-init.js already stamped the attribute).
|
||||
applyMode(currentMode());
|
||||
|
||||
ui.showNotification(newIsDark ? '🌙' : '☀️', newIsDark ? 'Dark mode enabled' : 'Light mode enabled');
|
||||
themeSegmented.addEventListener('click', (e) => {
|
||||
const btn = /** @type {HTMLElement | null} */ (/** @type {HTMLElement} */ (e.target).closest('.theme-segmented__opt'));
|
||||
if (!btn) return;
|
||||
e.stopPropagation();
|
||||
const mode = /** @type {'light' | 'dark' | 'auto'} */ (btn.dataset.mode);
|
||||
if (mode !== 'light' && mode !== 'dark' && mode !== 'auto') return;
|
||||
applyMode(mode);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -167,6 +167,8 @@ class MySharesList {
|
||||
_buildResourceLaneHeader(item) {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'ms-resource-row';
|
||||
if (item.resource.path) row.dataset.path = item.resource.path;
|
||||
if (item.resource.owner_id) row.dataset.ownerId = item.resource.owner_id;
|
||||
|
||||
row.appendChild(buildResourceIcon(item.resource, item.resource_type));
|
||||
|
||||
@@ -229,6 +231,13 @@ class MySharesList {
|
||||
if (_expiryState(grant.expires_at ?? null) === 'expired') {
|
||||
row.classList.add('ms-grant-row--expired');
|
||||
}
|
||||
// In sharedWith mode each grant row represents a (resource → subject)
|
||||
// pair, so stamp the resource hierarchy info for the hover tooltip.
|
||||
// In items mode the row represents a subject — no resource attrs.
|
||||
if (viewMode === 'sharedWith') {
|
||||
if (item.resource.path) row.dataset.path = item.resource.path;
|
||||
if (item.resource.owner_id) row.dataset.ownerId = item.resource.owner_id;
|
||||
}
|
||||
|
||||
row.appendChild(this._buildIdentity(grant, item, viewMode));
|
||||
row.appendChild(this._buildRolePill(grant.role));
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
*
|
||||
* No-op when the menu elements are not in the DOM (e.g. before initApp).
|
||||
*
|
||||
* @param {string} groupBy Active group-by key, or `''` for "None".
|
||||
* @param {string} groupBy Active group-by key, e.g. `''` for the "Name"
|
||||
* / flat-sort option a section opts into.
|
||||
* @param {boolean} reversed Whether sort direction is reversed.
|
||||
*/
|
||||
function applyGroupByMenuState(groupBy, reversed) {
|
||||
@@ -32,18 +33,14 @@ function applyGroupByMenuState(groupBy, reversed) {
|
||||
btn.classList.toggle('active', (btn.dataset.groupBy ?? '') === groupBy);
|
||||
}
|
||||
|
||||
// Group-by button: active class + label text.
|
||||
// Group-by button always reflects the active option (icon + label).
|
||||
const groupByBtn = document.getElementById('group-by-btn');
|
||||
groupByBtn?.classList.toggle('active', groupBy !== '');
|
||||
groupByBtn?.classList.add('active');
|
||||
|
||||
const lbl = groupByBtn?.querySelector('.group-by-label');
|
||||
if (lbl) {
|
||||
if (groupBy === '') {
|
||||
lbl.textContent = '';
|
||||
} else {
|
||||
const activeOpt = /** @type {HTMLElement|null} */ (document.querySelector(`.group-by-option[data-group-by="${CSS.escape(groupBy)}"]`));
|
||||
lbl.textContent = activeOpt?.textContent ?? '';
|
||||
}
|
||||
const activeOpt = /** @type {HTMLElement|null} */ (document.querySelector(`.group-by-option[data-group-by="${CSS.escape(groupBy)}"]`));
|
||||
lbl.innerHTML = activeOpt?.innerHTML ?? '';
|
||||
}
|
||||
|
||||
// Sort-direction button: active = reversed.
|
||||
|
||||
+79
-68
@@ -17,18 +17,6 @@
|
||||
|
||||
/** @type {Record<String, Array<number | String>>} */
|
||||
const OxiIcons = {
|
||||
'arrow-left': [
|
||||
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'
|
||||
],
|
||||
'arrows-alt': [
|
||||
512,
|
||||
'M278.6 9.4c-12.5-12.5-32.8-12.5-45.3 0l-64 64c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8l32 0 0 96-96 0 0-32c0-12.9-7.8-24.6-19.8-29.6s-25.7-2.2-34.9 6.9l-64 64c-12.5 12.5-12.5 32.8 0 45.3l64 64c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6l0-32 96 0 0 96-32 0c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9l64 64c12.5 12.5 32.8 12.5 45.3 0l64-64c9.2-9.2 11.9-22.9 6.9-34.9s-16.6-19.8-29.6-19.8l-32 0 0-96 96 0 0 32c0 12.9 7.8 24.6 19.8 29.6s25.7 2.2 34.9-6.9l64-64c12.5-12.5 12.5-32.8 0-45.3l-64-64c-9.2-9.2-22.9-11.9-34.9-6.9s-19.8 16.6-19.8 29.6l0 32-96 0 0-96 32 0c12.9 0 24.6-7.8 29.6-19.8s2.2-25.7-6.9-34.9l-64-64z'
|
||||
],
|
||||
'arrow-up': [
|
||||
512,
|
||||
'M214.6 9.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 109.3 160 480c0 17.7 14.3 32 32 32s32-14.3 32-32l0-370.7 105.4 105.4c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z'
|
||||
],
|
||||
'arrow-down': [
|
||||
512,
|
||||
'M169.4 502.6c12.5 12.5 32.8 12.5 45.3 0l160-160c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L224 402.7 224 32c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 370.7-105.4-105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l160 160z'
|
||||
@@ -41,6 +29,26 @@ const OxiIcons = {
|
||||
576,
|
||||
'M246.6 374.6l-96 96c-12.5 12.5-32.8 12.5-45.3 0l-96-96c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L96 370.7 96 64c0-17.7 14.3-32 32-32s32 14.3 32 32l0 306.7 41.4-41.4c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3zM320 480c-17.7 0-32-14.3-32-32s14.3-32 32-32l32 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-32 0zm0-128c-17.7 0-32-14.3-32-32s14.3-32 32-32l96 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-96 0zm0-128c-17.7 0-32-14.3-32-32s14.3-32 32-32l160 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-160 0zm0-128c-17.7 0-32-14.3-32-32s14.3-32 32-32l224 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L320 96z'
|
||||
],
|
||||
'arrow-left': [
|
||||
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'
|
||||
],
|
||||
'arrow-up': [
|
||||
512,
|
||||
'M214.6 9.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 109.3 160 480c0 17.7 14.3 32 32 32s32-14.3 32-32l0-370.7 105.4 105.4c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z'
|
||||
],
|
||||
'arrow-up-a-z': [
|
||||
576,
|
||||
'M183.6 42.4C177.5 35.8 169 32 160 32s-17.5 3.8-23.6 10.4l-88 96c-11.9 13-11.1 33.3 2 45.2s33.3 11.1 45.2-2L128 146.3 128 448c0 17.7 14.3 32 32 32s32-14.3 32-32l0-301.7 32.4 35.4c11.9 13 32.2 13.9 45.2 2s13.9-32.2 2-45.2l-88-96zM320 320c0 17.7 14.3 32 32 32l50.7 0-73.4 73.4c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8l128 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-50.7 0 73.4-73.4c9.2-9.2 11.9-22.9 6.9-34.9s-16.6-19.8-29.6-19.8l-128 0c-17.7 0-32 14.3-32 32zM416 32c-12.1 0-23.2 6.8-28.6 17.7l-64 128-16 32c-7.9 15.8-1.5 35 14.3 42.9s35 1.5 42.9-14.3l7.2-14.3 88.4 0 7.2 14.3c7.9 15.8 27.1 22.2 42.9 14.3s22.2-27.1 14.3-42.9l-16-32-64-128C439.2 38.8 428.1 32 416 32zM395.8 176L416 135.6 436.2 176l-40.4 0z'
|
||||
],
|
||||
'arrows-alt': [
|
||||
512,
|
||||
'M278.6 9.4c-12.5-12.5-32.8-12.5-45.3 0l-64 64c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8l32 0 0 96-96 0 0-32c0-12.9-7.8-24.6-19.8-29.6s-25.7-2.2-34.9 6.9l-64 64c-12.5 12.5-12.5 32.8 0 45.3l64 64c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6l0-32 96 0 0 96-32 0c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9l64 64c12.5 12.5 32.8 12.5 45.3 0l64-64c9.2-9.2 11.9-22.9 6.9-34.9s-16.6-19.8-29.6-19.8l-32 0 0-96 96 0 0 32c0 12.9 7.8 24.6 19.8 29.6s25.7 2.2 34.9-6.9l64-64c12.5-12.5 12.5-32.8 0-45.3l-64-64c-9.2-9.2-22.9-11.9-34.9-6.9s-19.8 16.6-19.8 29.6l0 32-96 0 0-96 32 0c12.9 0 24.6-7.8 29.6-19.8s2.2-25.7-6.9-34.9l-64-64z'
|
||||
],
|
||||
backward: [
|
||||
512,
|
||||
'M204.3 43.1C215.9 32 233 28.9 247.7 35.2S272 56 272 72l0 136.3 172.3-165.1C455.9 32 473 28.9 487.7 35.2S512 56 512 72l0 368c0 16-9.6 30.5-24.3 36.8s-31.8 3.2-43.4-7.9L272 303.7 272 440c0 16-9.6 30.5-24.3 36.8s-31.8 3.2-43.4-7.9l-192-184C4.5 277.3 0 266.9 0 256s4.5-21.3 12.3-28.9l192-184z'
|
||||
],
|
||||
ban: [
|
||||
512,
|
||||
'M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM159.3 388.7L388.7 159.3c4.6-4.6 11.5-5.9 17.4-3.5c14.5 6 26.4 15.3 35.1 27c3.8 5.2 3.2 12.3-1.2 16.8L210.2 428.4c-4.4 4.4-11.6 5-16.8 1.2c-11.7-8.7-21-20.6-27-35.1c-2.5-5.9-1.1-12.8 3.5-17.4z'
|
||||
@@ -69,6 +77,10 @@ const OxiIcons = {
|
||||
512,
|
||||
'M120 0c13.3 0 24 10.7 24 24l0 40 160 0 0-40c0-13.3 10.7-24 24-24s24 10.7 24 24l0 40 32 0c35.3 0 64 28.7 64 64l0 288c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 128C0 92.7 28.7 64 64 64l32 0 0-40c0-13.3 10.7-24 24-24zm0 112l-56 0c-8.8 0-16 7.2-16 16l0 48 352 0 0-48c0-8.8-7.2-16-16-16l-264 0zM48 224l0 192c0 8.8 7.2 16 16 16l320 0c8.8 0 16-7.2 16-16l0-192-352 0z'
|
||||
],
|
||||
camera: [
|
||||
512,
|
||||
'M149.1 64.8L138.7 96 64 96C28.7 96 0 124.7 0 160L0 416c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-256c0-35.3-28.7-64-64-64l-74.7 0-10.4-31.2C356.4 45.2 338.1 32 317.4 32L194.6 32c-20.7 0-39 13.2-45.5 32.8zM256 192a96 96 0 1 1 0 192 96 96 0 1 1 0-192z'
|
||||
],
|
||||
'caret-down': [
|
||||
320,
|
||||
'M137.4 374.6c12.5 12.5 32.8 12.5 45.3 0l128-128c9.2-9.2 11.9-22.9 6.9-34.9s-16.6-19.8-29.6-19.8L32 192c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9l128 128z'
|
||||
@@ -125,23 +137,30 @@ const OxiIcons = {
|
||||
640,
|
||||
'M308.5 135.3c7.1-6.3 9.9-16.2 6.2-25c-2.3-5.3-4.8-10.5-7.6-15.5L304 89.4c-3-5-6.3-9.9-9.8-14.6c-5.7-7.6-15.7-10.1-24.7-7.1l-28.2 9.3c-10.7-8.8-23-16-36.2-20.9L199 27.1c-1.9-9.3-9.1-16.7-18.5-17.8C173.9 8.4 167.2 8 160.4 8l-.7 0c-6.8 0-13.5 .4-20.1 1.2c-9.4 1.1-16.6 8.6-18.5 17.8L115 56.1c-13.3 5-25.5 12.1-36.2 20.9L50.5 67.8c-9-3-19-.5-24.7 7.1c-3.5 4.7-6.8 9.6-9.9 14.6l-3 5.3c-2.8 5-5.3 10.2-7.6 15.6c-3.7 8.7-.9 18.6 6.2 25l22.2 19.8C32.6 161.9 32 168.9 32 176s.6 14.1 1.7 20.9L11.5 216.7c-7.1 6.3-9.9 16.2-6.2 25c2.3 5.3 4.8 10.5 7.6 15.6l3 5.2c3 5.1 6.3 9.9 9.9 14.6c5.7 7.6 15.7 10.1 24.7 7.1l28.2-9.3c10.7 8.8 23 16 36.2 20.9l6.1 29.1c1.9 9.3 9.1 16.7 18.5 17.8c6.7 .8 13.5 1.2 20.4 1.2s13.7-.4 20.4-1.2c9.4-1.1 16.6-8.6 18.5-17.8l6.1-29.1c13.3-5 25.5-12.1 36.2-20.9l28.2 9.3c9 3 19 .5 24.7-7.1c3.5-4.7 6.8-9.5 9.8-14.6l3.1-5.4c2.8-5 5.3-10.2 7.6-15.5c3.7-8.7 .9-18.6-6.2-25l-22.2-19.8c1.1-6.8 1.7-13.8 1.7-20.9s-.6-14.1-1.7-20.9l22.2-19.8zM112 176a48 48 0 1 1 96 0 48 48 0 1 1 -96 0zM504.7 500.5c6.3 7.1 16.2 9.9 25 6.2c5.3-2.3 10.5-4.8 15.5-7.6l5.4-3.1c5-3 9.9-6.3 14.6-9.8c7.6-5.7 10.1-15.7 7.1-24.7l-9.3-28.2c8.8-10.7 16-23 20.9-36.2l29.1-6.1c9.3-1.9 16.7-9.1 17.8-18.5c.8-6.7 1.2-13.5 1.2-20.4s-.4-13.7-1.2-20.4c-1.1-9.4-8.6-16.6-17.8-18.5L583.9 307c-5-13.3-12.1-25.5-20.9-36.2l9.3-28.2c3-9 .5-19-7.1-24.7c-4.7-3.5-9.6-6.8-14.6-9.9l-5.3-3c-5-2.8-10.2-5.3-15.6-7.6c-8.7-3.7-18.6-.9-25 6.2l-19.8 22.2c-6.8-1.1-13.8-1.7-20.9-1.7s-14.1 .6-20.9 1.7l-19.8-22.2c-6.3-7.1-16.2-9.9-25-6.2c-5.3 2.3-10.5 4.8-15.6 7.6l-5.2 3c-5.1 3-9.9 6.3-14.6 9.9c-7.6 5.7-10.1 15.7-7.1 24.7l9.3 28.2c-8.8 10.7-16 23-20.9 36.2L315.1 313c-9.3 1.9-16.7 9.1-17.8 18.5c-.8 6.7-1.2 13.5-1.2 20.4s.4 13.7 1.2 20.4c1.1 9.4 8.6 16.6 17.8 18.5l29.1 6.1c5 13.3 12.1 25.5 20.9 36.2l-9.3 28.2c-3 9-.5 19 7.1 24.7c4.7 3.5 9.5 6.8 14.6 9.8l5.4 3.1c5 2.8 10.2 5.3 15.5 7.6c8.7 3.7 18.6 .9 25-6.2l19.8-22.2c6.8 1.1 13.8 1.7 20.9 1.7s14.1-.6 20.9-1.7l19.8 22.2zM464 304a48 48 0 1 1 0 96 48 48 0 1 1 0-96z'
|
||||
],
|
||||
crown: [
|
||||
576,
|
||||
'M309 106c11.4-7 19-19.7 19-34c0-22.1-17.9-40-40-40s-40 17.9-40 40c0 14.4 7.6 27 19 34L209.7 220.6c-9.1 18.2-32.7 23.4-48.6 10.7L72 160c5-6.7 8-15 8-24c0-22.1-17.9-40-40-40S0 113.9 0 136s17.9 40 40 40c.2 0 .5 0 .7 0L86.4 427.4c5.5 30.4 32 52.6 63 52.6l277.2 0c30.9 0 57.4-22.1 63-52.6L535.3 176c.2 0 .5 0 .7 0c22.1 0 40-17.9 40-40s-17.9-40-40-40s-40 17.9-40 40c0 9 3 17.3 8 24l-89.1 71.3c-15.9 12.7-39.5 7.5-48.6-10.7L309 106z'
|
||||
'compact-disc': [
|
||||
512,
|
||||
'M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zM88 256H56c0-105.9 86.1-192 192-192v32c-88.2 0-160 71.8-160 160zm160 96c-53 0-96-43-96-96s43-96 96-96 96 43 96 96-43 96-96 96zm0-128c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.3-32-32-32z'
|
||||
],
|
||||
copy: [
|
||||
448,
|
||||
'M208 0L332.1 0c12.7 0 24.9 5.1 33.9 14.1l67.9 67.9c9 9 14.1 21.2 14.1 33.9L448 336c0 26.5-21.5 48-48 48l-192 0c-26.5 0-48-21.5-48-48l0-288c0-26.5 21.5-48 48-48zM48 128l80 0 0 64-64 0 0 256 192 0 0-32 64 0 0 48c0 26.5-21.5 48-48 48L48 512c-26.5 0-48-21.5-48-48L0 176c0-26.5 21.5-48 48-48z'
|
||||
],
|
||||
crown: [
|
||||
576,
|
||||
'M309 106c11.4-7 19-19.7 19-34c0-22.1-17.9-40-40-40s-40 17.9-40 40c0 14.4 7.6 27 19 34L209.7 220.6c-9.1 18.2-32.7 23.4-48.6 10.7L72 160c5-6.7 8-15 8-24c0-22.1-17.9-40-40-40S0 113.9 0 136s17.9 40 40 40c.2 0 .5 0 .7 0L86.4 427.4c5.5 30.4 32 52.6 63 52.6l277.2 0c30.9 0 57.4-22.1 63-52.6L535.3 176c.2 0 .5 0 .7 0c22.1 0 40-17.9 40-40s-17.9-40-40-40s-40 17.9-40 40c0 9 3 17.3 8 24l-89.1 71.3c-15.9 12.7-39.5 7.5-48.6-10.7L309 106z'
|
||||
],
|
||||
database: [
|
||||
448,
|
||||
'M448 80l0 48c0 44.2-100.3 80-224 80S0 172.2 0 128L0 80C0 35.8 100.3 0 224 0S448 35.8 448 80zM393.2 214.7c20.8-7.4 39.9-16.9 54.8-28.6L448 288c0 44.2-100.3 80-224 80S0 332.2 0 288L0 186.1c14.9 11.8 34 21.2 54.8 28.6C99.7 230.7 159.5 240 224 240s124.3-9.3 169.2-25.3zM0 346.1c14.9 11.8 34 21.2 54.8 28.6C99.7 390.7 159.5 400 224 400s124.3-9.3 169.2-25.3c20.8-7.4 39.9-16.9 54.8-28.6l0 85.9c0 44.2-100.3 80-224 80S0 476.2 0 432l0-85.9z'
|
||||
],
|
||||
desktop: [
|
||||
576,
|
||||
'M64 0C28.7 0 0 28.7 0 64L0 352c0 35.3 28.7 64 64 64l176 0-10.7 32L160 448c-17.7 0-32 14.3-32 32s14.3 32 32 32l256 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-69.3 0L336 416l176 0c35.3 0 64-28.7 64-64l0-288c0-35.3-28.7-64-64-64L64 0zM512 64l0 224L64 288 64 64l448 0z'
|
||||
],
|
||||
download: [
|
||||
512,
|
||||
'M288 32c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 242.7-73.4-73.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l128 128c12.5 12.5 32.8 12.5 45.3 0l128-128c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L288 274.7 288 32zM64 352c-35.3 0-64 28.7-64 64l0 32c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-32c0-35.3-28.7-64-64-64l-101.5 0-45.3 45.3c-25 25-65.5 25-90.5 0L165.5 352 64 352zm368 56a24 24 0 1 1 0 48 24 24 0 1 1 0-48z'
|
||||
],
|
||||
'ellipsis-v': [128, 'M64 360a56 56 0 1 0 0 112 56 56 0 1 0 0-112zm0-160a56 56 0 1 0 0 112 56 56 0 1 0 0-112zM120 96A56 56 0 1 0 8 96a56 56 0 1 0 112 0z'],
|
||||
envelope: [
|
||||
512,
|
||||
'M48 64C21.5 64 0 85.5 0 112c0 15.1 7.1 29.3 19.2 38.4L236.8 313.6c11.4 8.5 27 8.5 38.4 0L492.8 150.4c12.1-9.1 19.2-23.3 19.2-38.4c0-26.5-21.5-48-48-48L48 64zM0 176L0 384c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-208L294.4 339.2c-22.8 17.1-54 17.1-76.8 0L0 176z'
|
||||
@@ -158,6 +177,10 @@ const OxiIcons = {
|
||||
448,
|
||||
'M32 32C14.3 32 0 46.3 0 64l0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-64 64 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L32 32zM64 352c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 96c0 17.7 14.3 32 32 32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-64 0 0-64zM320 32c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0 0 64c0 17.7 14.3 32 32 32s32-14.3 32-32l0-96c0-17.7-14.3-32-32-32l-96 0zM448 352c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l96 0c17.7 0 32-14.3 32-32l0-96z'
|
||||
],
|
||||
'external-link-alt': [
|
||||
512,
|
||||
'M290.4 19.8C295.4 7.8 307.1 0 320 0L480 0c17.7 0 32 14.3 32 32l0 160c0 12.9-7.8 24.6-19.8 29.6s-25.7 2.2-34.9-6.9L400 157.3 246.6 310.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L354.7 112 297.4 54.6c-9.2-9.2-11.9-22.9-6.9-34.9zM0 176c0-44.2 35.8-80 80-80l80 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-80 0c-8.8 0-16 7.2-16 16l0 256c0 8.8 7.2 16 16 16l256 0c8.8 0 16-7.2 16-16l0-80c0-17.7 14.3-32 32-32s32 14.3 32 32l0 80c0 44.2-35.8 80-80 80L80 512c-44.2 0-80-35.8-80-80L0 176z'
|
||||
],
|
||||
eye: [
|
||||
576,
|
||||
'M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z'
|
||||
@@ -218,6 +241,10 @@ const OxiIcons = {
|
||||
512,
|
||||
'M512 416c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96C0 60.7 28.7 32 64 32l128 0c20.1 0 39.1 9.5 51.2 25.6l19.2 25.6c6 8.1 15.5 12.8 25.6 12.8l160 0c35.3 0 64 28.7 64 64l0 256zM232 376c0 13.3 10.7 24 24 24s24-10.7 24-24l0-64 64 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-64 0 0-64c0-13.3-10.7-24-24-24s-24 10.7-24 24l0 64-64 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l64 0 0 64z'
|
||||
],
|
||||
forward: [
|
||||
512,
|
||||
'M371.7 43.1C360.1 32 343 28.9 328.3 35.2S304 56 304 72l0 136.3-172.3-165.1C120.1 32 103 28.9 88.3 35.2S64 56 64 72l0 368c0 16 9.6 30.5 24.3 36.8s31.8 3.2 43.4-7.9L304 303.7 304 440c0 16 9.6 30.5 24.3 36.8s31.8 3.2 43.4-7.9l192-184c7.9-7.5 12.3-18 12.3-28.9s-4.5-21.3-12.3-28.9l-192-184z'
|
||||
],
|
||||
github: [
|
||||
496,
|
||||
'M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z'
|
||||
@@ -226,6 +253,10 @@ const OxiIcons = {
|
||||
512,
|
||||
'M352 256c0 22.2-1.2 43.6-3.3 64l-185.3 0c-2.2-20.4-3.3-41.8-3.3-64s1.2-43.6 3.3-64l185.3 0c2.2 20.4 3.3 41.8 3.3 64zm28.8-64l123.1 0c5.3 20.5 8.1 41.9 8.1 64s-2.8 43.5-8.1 64l-123.1 0c2.1-20.6 3.2-42 3.2-64s-1.1-43.4-3.2-64zm112.6-32l-116.7 0c-10-63.9-29.8-117.4-55.3-151.6c78.3 20.7 142 77.5 171.9 151.6zm-149.1 0l-176.6 0c6.1-36.4 15.5-68.6 27-94.7c10.5-23.6 22.2-40.7 33.5-51.5C239.4 3.2 248.7 0 256 0s16.6 3.2 27.8 13.8c11.3 10.8 23 27.9 33.5 51.5c11.6 26 20.9 58.2 27 94.7zm-209 0L18.6 160C48.6 85.9 112.2 29.1 190.6 8.4C165.1 42.6 145.3 96.1 135.3 160zM8.1 192l123.1 0c-2.1 20.6-3.2 42-3.2 64s1.1 43.4 3.2 64L8.1 320C2.8 299.5 0 278.1 0 256s2.8-43.5 8.1-64zM194.7 446.6c-11.6-26-20.9-58.2-27-94.6l176.6 0c-6.1 36.4-15.5 68.6-27 94.6c-10.5 23.6-22.2 40.7-33.5 51.5C272.6 508.8 263.3 512 256 512s-16.6-3.2-27.8-13.8c-11.3-10.8-23-27.9-33.5-51.5zM135.3 352c10 63.9 29.8 117.4 55.3 151.6C112.2 482.9 48.6 426.1 18.6 352l116.7 0zm358.1 0c-30 74.1-93.6 130.9-171.9 151.6c25.5-34.2 45.2-87.7 55.3-151.6l116.7 0z'
|
||||
],
|
||||
'grip-vertical': [
|
||||
320,
|
||||
'M128 40c0-22.1-17.9-40-40-40L40 0C17.9 0 0 17.9 0 40L0 88c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48zm0 192c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48zM0 424l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40zM320 40c0-22.1-17.9-40-40-40L232 0c-22.1 0-40 17.9-40 40l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48zM192 232l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40zM320 424c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48z'
|
||||
],
|
||||
hdd: [
|
||||
512,
|
||||
'M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 184.4c-17-15.2-39.4-24.4-64-24.4L64 256c-24.6 0-47 9.2-64 24.4L0 96zM64 288l384 0c35.3 0 64 28.7 64 64l0 64c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64l0-64c0-35.3 28.7-64 64-64zM320 416a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm128-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z'
|
||||
@@ -262,6 +293,10 @@ const OxiIcons = {
|
||||
512,
|
||||
'M232.5 5.2c14.9-6.9 32.1-6.9 47 0l218.6 101c8.5 3.9 13.9 12.4 13.9 21.8s-5.4 17.9-13.9 21.8l-218.6 101c-14.9 6.9-32.1 6.9-47 0L13.9 149.8C5.4 145.8 0 137.3 0 128s5.4-17.9 13.9-21.8L232.5 5.2zM48.1 218.4l164.3 75.9c27.7 12.8 59.6 12.8 87.3 0l164.3-75.9 34.1 15.8c8.5 3.9 13.9 12.4 13.9 21.8s-5.4 17.9-13.9 21.8l-218.6 101c-14.9 6.9-32.1 6.9-47 0L13.9 277.8C5.4 273.8 0 265.3 0 256s5.4-17.9 13.9-21.8l34.1-15.8zM13.9 362.2l34.1-15.8 164.3 75.9c27.7 12.8 59.6 12.8 87.3 0l164.3-75.9 34.1 15.8c8.5 3.9 13.9 12.4 13.9 21.8s-5.4 17.9-13.9 21.8l-218.6 101c-14.9 6.9-32.1 6.9-47 0L13.9 405.8C5.4 401.8 0 393.3 0 384s5.4-17.9 13.9-21.8z'
|
||||
],
|
||||
'level-up-alt': [
|
||||
384,
|
||||
'M169.4 9.4c12.5-12.5 32.8-12.5 45.3 0l128 128c9.2 9.2 11.9 22.9 6.9 34.9S332.9 192 320 192l-64 0 0 160c0 88.4-71.6 160-160 160l-64 0c-17.7 0-32-14.3-32-32l0-64c0-17.7 14.3-32 32-32l64 0c17.7 0 32-14.3 32-32l0-160-64 0c-12.9 0-24.6-7.8-29.6-19.8s-2.2-25.7 6.9-34.9l128-128z'
|
||||
],
|
||||
link: [
|
||||
576,
|
||||
'M419.5 96c-16.6 0-32.7 4.5-46.8 12.7-15.8-16-34.2-29.4-54.5-39.5 28.2-24 64.1-37.2 101.3-37.2 86.4 0 156.5 70 156.5 156.5 0 41.5-16.5 81.3-45.8 110.6l-71.1 71.1c-29.3 29.3-69.1 45.8-110.6 45.8-86.4 0-156.5-70-156.5-156.5 0-1.5 0-3 .1-4.5 .5-17.7 15.2-31.6 32.9-31.1s31.6 15.2 31.1 32.9c0 .9 0 1.8 0 2.6 0 51.1 41.4 92.5 92.5 92.5 24.5 0 48-9.7 65.4-27.1l71.1-71.1c17.3-17.3 27.1-40.9 27.1-65.4 0-51.1-41.4-92.5-92.5-92.5zM275.2 173.3c-1.9-.8-3.8-1.9-5.5-3.1-12.6-6.5-27-10.2-42.1-10.2-24.5 0-48 9.7-65.4 27.1L91.1 258.2c-17.3 17.3-27.1 40.9-27.1 65.4 0 51.1 41.4 92.5 92.5 92.5 16.5 0 32.6-4.4 46.7-12.6 15.8 16 34.2 29.4 54.6 39.5-28.2 23.9-64 37.2-101.3 37.2-86.4 0-156.5-70-156.5-156.5 0-41.5 16.5-81.3 45.8-110.6l71.1-71.1c29.3-29.3 69.1-45.8 110.6-45.8 86.6 0 156.5 70.6 156.5 156.9 0 1.3 0 2.6 0 3.9-.4 17.7-15.1 31.6-32.8 31.2s-31.6-15.1-31.2-32.8c0-.8 0-1.5 0-2.3 0-33.7-18-63.3-44.8-79.6z'
|
||||
@@ -282,6 +317,10 @@ const OxiIcons = {
|
||||
384,
|
||||
'M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4c5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3c6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z'
|
||||
],
|
||||
music: [
|
||||
512,
|
||||
'M468 7c7.6 6.1 12 15.3 12 25l0 304c0 44.2-43 80-96 80s-96-35.8-96-80 43-80 96-80c11.2 0 22 1.6 32 4.6l0-116.7-224 49.8 0 206.3c0 44.2-43 80-96 80s-96-35.8-96-80 43-80 96-80c11.2 0 22 1.6 32 4.6L128 96c0-15 10.4-28 25.1-31.2l288-64c9.5-2.1 19.4 .2 27 6.3z'
|
||||
],
|
||||
oxiexport: [
|
||||
576,
|
||||
'M384.5 24l0 72-64 0c-79.5 0-144 64.5-144 144 0 93.4 82.8 134.8 100.6 142.6 2.2 1 4.6 1.4 7.1 1.4l2.5 0c9.8 0 17.8-8 17.8-17.8 0-8.3-5.9-15.5-12.8-20.3-8.9-6.2-19.2-18.2-19.2-40.5 0-45 36.5-81.5 81.5-81.5l30.5 0 0 72c0 9.7 5.8 18.5 14.8 22.2s19.3 1.7 26.2-5.2l136-136c9.4-9.4 9.4-24.6 0-33.9L425.5 7c-6.9-6.9-17.2-8.9-26.2-5.2S384.5 14.3 384.5 24zm-272 72c-44.2 0-80 35.8-80 80l0 256c0 44.2 35.8 80 80 80l256 0c44.2 0 80-35.8 80-80l0-32c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 32c0 8.8-7.2 16-16 16l-256 0c-8.8 0-16-7.2-16-16l0-256c0-8.8 7.2-16 16-16l16 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-16 0z'
|
||||
@@ -290,50 +329,34 @@ const OxiIcons = {
|
||||
576,
|
||||
'm 360.55,24 v 72 h 64 c 79.5,0 144,64.5 144,144 0,93.4 -82.8,134.8 -100.6,142.6 -2.2,1 -4.6,1.4 -7.1,1.4 h -2.5 c -9.8,0 -17.8,-8 -17.8,-17.8 0,-8.3 5.9,-15.5 12.8,-20.3 8.9,-6.2 19.2,-18.2 19.2,-40.5 0,-45 -36.5,-81.5 -81.5,-81.5 h -30.5 v 72 c 0,9.7 -5.8,18.5 -14.8,22.2 -9,3.7 -19.3,1.7 -26.2,-5.2 l -136,-136 c -9.4,-9.4 -9.4,-24.6 0,-33.9 l 136,-136 c 6.9,-6.9 17.2,-8.9 26.2,-5.2 9,3.7 14.8,12.5 14.8,22.2 z M 112.5,96 c -44.2,0 -80,35.8 -80,80 v 256 c 0,44.2 35.8,80 80,80 h 256 c 44.2,0 80,-35.8 80,-80 v -32 c 0,-17.7 -14.3,-32 -32,-32 -17.7,0 -32,14.3 -32,32 v 32 c 0,8.8 -7.2,16 -16,16 h -256 c -8.8,0 -16,-7.2 -16,-16 V 176 c 0,-8.8 7.2,-16 16,-16 h 16 c 17.7,0 32,-14.3 32,-32 0,-17.7 -14.3,-32 -32,-32 z'
|
||||
],
|
||||
pen: [
|
||||
512,
|
||||
'M362.7 19.3L314.3 67.7 444.3 197.7l48.4-48.4c25-25 25-65.5 0-90.5L453.3 19.3c-25-25-65.5-25-90.5 0zm-71 71L58.6 323.5c-10.4 10.4-18 23.3-22.2 37.4L1 481.2C-1.5 489.7 .8 498.8 7 505s15.3 8.5 23.7 6.1l120.3-35.4c14.1-4.2 27-11.8 37.4-22.2L421.7 220.3 291.7 90.3z'
|
||||
],
|
||||
play: [
|
||||
384,
|
||||
'M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80L0 432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z'
|
||||
],
|
||||
pause: [
|
||||
384,
|
||||
'M48 32C21.5 32 0 53.5 0 80L0 432c0 26.5 21.5 48 48 48l64 0c26.5 0 48-21.5 48-48l0-352c0-26.5-21.5-48-48-48L48 32zm224 0c-26.5 0-48 21.5-48 48l0 352c0 26.5 21.5 48 48 48l64 0c26.5 0 48-21.5 48-48l0-352c0-26.5-21.5-48-48-48l-64 0z'
|
||||
],
|
||||
pen: [
|
||||
512,
|
||||
'M362.7 19.3L314.3 67.7 444.3 197.7l48.4-48.4c25-25 25-65.5 0-90.5L453.3 19.3c-25-25-65.5-25-90.5 0zm-71 71L58.6 323.5c-10.4 10.4-18 23.3-22.2 37.4L1 481.2C-1.5 489.7 .8 498.8 7 505s15.3 8.5 23.7 6.1l120.3-35.4c14.1-4.2 27-11.8 37.4-22.2L421.7 220.3 291.7 90.3z'
|
||||
],
|
||||
'pencil-alt': [
|
||||
512,
|
||||
'M36.4 353.2c4.1-14.6 11.8-27.9 22.6-38.7l181.2-181.2 33.9-33.9c16.6 16.6 51.3 51.3 104 104l33.9 33.9-33.9 33.9-181.2 181.2c-10.7 10.7-24.1 18.5-38.7 22.6L30.4 510.6c-8.3 2.3-17.3 0-23.4-6.2S-1.4 489.3 .9 481L36.4 353.2zm55.6-3.7c-4.4 4.7-7.6 10.4-9.3 16.6l-24.1 86.9 86.9-24.1c6.4-1.8 12.2-5.1 17-9.7L91.9 349.5zm354-146.1c-16.6-16.6-51.3-51.3-104-104L308 65.5C334.5 39 349.4 24.1 352.9 20.6 366.4 7 384.8-.6 404-.6S441.6 7 455.1 20.6l35.7 35.7C504.4 69.9 512 88.3 512 107.4s-7.6 37.6-21.2 51.1c-3.5 3.5-18.4 18.4-44.9 44.9z'
|
||||
],
|
||||
shuffle: [
|
||||
512,
|
||||
'M403.8 34.4c12-5 25.7-2.2 34.9 6.9l64 64c6 6 9.4 14.1 9.4 22.6s-3.4 16.6-9.4 22.6l-64 64c-9.2 9.2-22.9 11.9-34.9 6.9S384 204.9 384 192l0-32-32 0c-10.1 0-19.6 4.7-25.6 12.8l-32.4 43.2-40-53.3 21.2-28.3C293.3 110.2 321.8 96 352 96l32 0 0-32c0-12.9 7.8-24.6 19.8-29.6zM154 296l40 53.3-21.2 28.3C154.7 401.8 126.2 416 96 416l-64 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l64 0c10.1 0 19.6-4.7 25.6-12.8L154 296zM438.6 470.6c-9.2 9.2-22.9 11.9-34.9 6.9S384 460.9 384 448l0-32-32 0c-30.2 0-58.7-14.2-76.8-38.4L121.6 172.8c-6-8.1-15.5-12.8-25.6-12.8l-64 0c-17.7 0-32-14.3-32-32S14.3 96 32 96l64 0c30.2 0 58.7 14.2 76.8 38.4L326.4 339.2c6 8.1 15.5 12.8 25.6 12.8l32 0 0-32c0-12.9 7.8-24.6 19.8-29.6s25.7-2.2 34.9 6.9l64 64c6 6 9.4 14.1 9.4 22.6s-3.4 16.6-9.4 22.6l-64 64z'
|
||||
play: [
|
||||
384,
|
||||
'M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80L0 432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z'
|
||||
],
|
||||
repeat: [
|
||||
512,
|
||||
'M470.6 118.6c12.5-12.5 12.5-32.8 0-45.3l-64-64c-9.2-9.2-22.9-11.9-34.9-6.9S352 19.1 352 32l0 32-160 0C86 64 0 150 0 256 0 273.7 14.3 288 32 288s32-14.3 32-32c0-70.7 57.3-128 128-128l160 0 0 32c0 12.9 7.8 24.6 19.8 29.6s25.7 2.2 34.9-6.9l64-64zM41.4 393.4c-12.5 12.5-12.5 32.8 0 45.3l64 64c9.2 9.2 22.9 11.9 34.9 6.9S160 492.9 160 480l0-32 160 0c106 0 192-86 192-192 0-17.7-14.3-32-32-32s-32 14.3-32 32c0 70.7-57.3 128-128 128l-160 0 0-32c0-12.9-7.8-24.6-19.8-29.6s-25.7-2.2-34.9 6.9l-64 64z'
|
||||
],
|
||||
forward: [
|
||||
512,
|
||||
'M371.7 43.1C360.1 32 343 28.9 328.3 35.2S304 56 304 72l0 136.3-172.3-165.1C120.1 32 103 28.9 88.3 35.2S64 56 64 72l0 368c0 16 9.6 30.5 24.3 36.8s31.8 3.2 43.4-7.9L304 303.7 304 440c0 16 9.6 30.5 24.3 36.8s31.8 3.2 43.4-7.9l192-184c7.9-7.5 12.3-18 12.3-28.9s-4.5-21.3-12.3-28.9l-192-184z'
|
||||
],
|
||||
backward: [
|
||||
512,
|
||||
'M204.3 43.1C215.9 32 233 28.9 247.7 35.2S272 56 272 72l0 136.3 172.3-165.1C455.9 32 473 28.9 487.7 35.2S512 56 512 72l0 368c0 16-9.6 30.5-24.3 36.8s-31.8 3.2-43.4-7.9L272 303.7 272 440c0 16-9.6 30.5-24.3 36.8s-31.8 3.2-43.4-7.9l-192-184C4.5 277.3 0 266.9 0 256s4.5-21.3 12.3-28.9l192-184z'
|
||||
],
|
||||
volume: [
|
||||
512,
|
||||
'M48 352l48 0 134.1 119.2c6.4 5.7 14.6 8.8 23.1 8.8 19.2 0 34.8-15.6 34.8-34.8l0-378.4c0-19.2-15.6-34.8-34.8-34.8-8.5 0-16.7 3.1-23.1 8.8L96 160 48 160c-26.5 0-48 21.5-48 48l0 96c0 26.5 21.5 48 48 48zM441.1 107c-10.3-8.4-25.4-6.8-33.8 3.5s-6.8 25.4 3.5 33.8C443.3 170.7 464 210.9 464 256s-20.7 85.3-53.2 111.8c-10.3 8.4-11.8 23.5-3.5 33.8s23.5 11.8 33.8 3.5c43.2-35.2 70.9-88.9 70.9-149s-27.7-113.8-70.9-149zm-60.5 74.5c-10.3-8.4-25.4-6.8-33.8 3.5s-6.8 25.4 3.5 33.8C361.1 227.6 368 241 368 256s-6.9 28.4-17.7 37.3c-10.3 8.4-11.8 23.5-3.5 33.8s23.5 11.8 33.8 3.5C402.1 312.9 416 286.1 416 256s-13.9-56.9-35.5-74.5z'
|
||||
],
|
||||
'compact-disc': [
|
||||
512,
|
||||
'M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zM88 256H56c0-105.9 86.1-192 192-192v32c-88.2 0-160 71.8-160 160zm160 96c-53 0-96-43-96-96s43-96 96-96 96 43 96 96-43 96-96 96zm0-128c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.3-32-32-32z'
|
||||
plus: [
|
||||
448,
|
||||
'M256 64c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 160-160 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l160 0 0 160c0 17.7 14.3 32 32 32s32-14.3 32-32l0-160 160 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-160 0 0-160z'
|
||||
],
|
||||
'question-circle': [
|
||||
512,
|
||||
'M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM169.8 165.3c7.9-22.3 29.1-37.3 52.8-37.3l58.3 0c34.9 0 63.1 28.3 63.1 63.1c0 22.6-12.1 43.5-31.7 54.8L280 264.4c-.2 13-10.9 23.6-24 23.6c-13.3 0-24-10.7-24-24l0-13.5c0-8.6 4.6-16.5 12.1-20.8l44.3-25.4c4.7-2.7 7.6-7.7 7.6-13.1c0-8.4-6.8-15.1-15.1-15.1l-58.3 0c-3.4 0-6.4 2.1-7.5 5.3l-.4 1.2c-4.4 12.5-18.2 19-30.6 14.6s-19-18.2-14.6-30.6l.4-1.2zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z'
|
||||
],
|
||||
repeat: [
|
||||
512,
|
||||
'M470.6 118.6c12.5-12.5 12.5-32.8 0-45.3l-64-64c-9.2-9.2-22.9-11.9-34.9-6.9S352 19.1 352 32l0 32-160 0C86 64 0 150 0 256 0 273.7 14.3 288 32 288s32-14.3 32-32c0-70.7 57.3-128 128-128l160 0 0 32c0 12.9 7.8 24.6 19.8 29.6s25.7 2.2 34.9-6.9l64-64zM41.4 393.4c-12.5 12.5-12.5 32.8 0 45.3l64 64c9.2 9.2 22.9 11.9 34.9 6.9S160 492.9 160 480l0-32 160 0c106 0 192-86 192-192 0-17.7-14.3-32-32-32s-32 14.3-32 32c0 70.7-57.3 128-128 128l-160 0 0-32c0-12.9-7.8-24.6-19.8-29.6s-25.7-2.2-34.9 6.9l-64 64z'
|
||||
],
|
||||
save: [
|
||||
448,
|
||||
'M64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l320 0c35.3 0 64-28.7 64-64l0-242.7c0-17-6.7-33.3-18.7-45.3L352 50.7C340 38.7 323.7 32 306.7 32L64 32zm0 96c0-17.7 14.3-32 32-32l192 0c17.7 0 32 14.3 32 32l0 64c0 17.7-14.3 32-32 32L96 224c-17.7 0-32-14.3-32-32l0-64zM224 288a64 64 0 1 1 0 128 64 64 0 1 1 0-128z'
|
||||
@@ -362,6 +385,10 @@ const OxiIcons = {
|
||||
512,
|
||||
'M256 0c4.6 0 9.2 1 13.4 2.9L457.7 82.8c22 9.3 38.4 31 38.3 57.2c-.5 99.2-41.3 280.7-213.6 363.2c-16.7 8-36.1 8-52.8 0C57.3 420.7 16.5 239.2 16 140c-.1-26.2 16.3-47.9 38.3-57.2L242.7 2.9C246.8 1 251.4 0 256 0zm0 66.8l0 378.1C394 378 431.1 230.1 432 141.4L256 66.8s0 0 0 0z'
|
||||
],
|
||||
shuffle: [
|
||||
512,
|
||||
'M403.8 34.4c12-5 25.7-2.2 34.9 6.9l64 64c6 6 9.4 14.1 9.4 22.6s-3.4 16.6-9.4 22.6l-64 64c-9.2 9.2-22.9 11.9-34.9 6.9S384 204.9 384 192l0-32-32 0c-10.1 0-19.6 4.7-25.6 12.8l-32.4 43.2-40-53.3 21.2-28.3C293.3 110.2 321.8 96 352 96l32 0 0-32c0-12.9 7.8-24.6 19.8-29.6zM154 296l40 53.3-21.2 28.3C154.7 401.8 126.2 416 96 416l-64 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l64 0c10.1 0 19.6-4.7 25.6-12.8L154 296zM438.6 470.6c-9.2 9.2-22.9 11.9-34.9 6.9S384 460.9 384 448l0-32-32 0c-30.2 0-58.7-14.2-76.8-38.4L121.6 172.8c-6-8.1-15.5-12.8-25.6-12.8l-64 0c-17.7 0-32-14.3-32-32S14.3 96 32 96l64 0c30.2 0 58.7 14.2 76.8 38.4L326.4 339.2c6 8.1 15.5 12.8 25.6 12.8l32 0 0-32c0-12.9 7.8-24.6 19.8-29.6s25.7-2.2 34.9 6.9l64 64c6 6 9.4 14.1 9.4 22.6s-3.4 16.6-9.4 22.6l-64 64z'
|
||||
],
|
||||
'sign-in-alt': [
|
||||
512,
|
||||
'M217.9 105.9L340.7 228.7c7.2 7.2 11.3 17.1 11.3 27.3s-4.1 20.1-11.3 27.3L217.9 406.1c-6.4 6.4-15 9.9-24 9.9c-18.7 0-33.9-15.2-33.9-33.9l0-62.1L32 320c-17.7 0-32-14.3-32-32l0-64c0-17.7 14.3-32 32-32l128 0 0-62.1c0-18.7 15.2-33.9 33.9-33.9c9 0 17.6 3.6 24 9.9zM352 416l64 0c17.7 0 32-14.3 32-32l0-256c0-17.7-14.3-32-32-32l-64 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l64 0c53 0 96 43 96 96l0 256c0 53-43 96-96 96l-64 0c-17.7 0-32-14.3-32-32s14.3-32 32-32z'
|
||||
@@ -386,6 +413,10 @@ const OxiIcons = {
|
||||
576,
|
||||
'M287.9 0c9.2 0 17.6 5.2 21.6 13.5l68.6 141.3 153.2 22.6c9 1.3 16.5 7.6 19.3 16.3s.5 18.1-5.9 24.5L439.6 319.9l24.6 145.7c1.5 9-2.2 18.1-9.7 23.5s-17.3 6-25.3 1.7l-137-73.2L155.2 490.8c-8 4.3-17.8 3.7-25.3-1.7s-11.2-14.5-9.7-23.5l24.6-145.7L39.6 218.2c-6.4-6.4-8.7-15.9-5.9-24.5s10.3-14.9 19.3-16.3l153.2-22.6L274.3 13.5C278.3 5.2 286.7 0 295.9 0h-8zm0 79L235.4 187.2c-3.5 7.1-10.2 12.1-18.1 13.3L99 218.9l85.8 85.1c5.5 5.5 8.1 13.3 6.8 21L171.3 444.7l111.5-59.5c7-3.7 15.3-3.7 22.3 0l111.5 59.5-20.3-119.7c-1.3-7.7 1.2-15.5 6.8-21l85.8-85.1-118.3-17.4c-7.8-1.2-14.6-6.1-18.1-13.3L287.9 79z'
|
||||
],
|
||||
sun: [
|
||||
512,
|
||||
'M375.7 19.7c-1.5-8-6.9-14.7-14.4-17.8s-16.1-2.2-22.8 2.4L256 61 173.5 4.2c-6.7-4.6-15.3-5.5-22.8-2.4s-12.9 9.8-14.4 17.8l-18.1 98.5L19.7 136.3c-8 1.5-14.7 6.9-17.8 14.4s-2.2 16.1 2.4 22.8L61 256 4.2 338.5c-4.6 6.7-5.5 15.3-2.4 22.8s9.8 13 17.8 14.4l98.5 18.1 18.1 98.5c1.5 8 6.9 14.7 14.4 17.8s16.1 2.2 22.8-2.4L256 451l82.5 56.8c6.7 4.6 15.3 5.5 22.8 2.4s12.9-9.8 14.4-17.8l18.1-98.5 98.5-18.1c8-1.5 14.7-6.9 17.8-14.4s2.2-16.1-2.4-22.8L451 256l56.8-82.5c4.6-6.7 5.5-15.3 2.4-22.8s-9.8-12.9-17.8-14.4l-98.5-18.1L375.7 19.7zM269.6 110l65.6-45.2 14.4 78.3c1.8 9.8 9.5 17.5 19.3 19.3l78.3 14.4L402 242.4c-5.7 8.2-5.7 19 0 27.2l45.2 65.6-78.3 14.4c-9.8 1.8-17.5 9.5-19.3 19.3l-14.4 78.3L269.6 402c-8.2-5.7-19-5.7-27.2 0l-65.6 45.2-14.4-78.3c-1.8-9.8-9.5-17.5-19.3-19.3L64.8 335.2 110 269.6c5.7-8.2 5.7-19 0-27.2L64.8 176.8l78.3-14.4c9.8-1.8 17.5-9.5 19.3-19.3l14.4-78.3L242.4 110c8.2 5.7 19 5.7 27.2 0zM256 368a112 112 0 1 0 0-224 112 112 0 1 0 0 224zM192 256a64 64 0 1 1 128 0 64 64 0 1 1 -128 0z'
|
||||
],
|
||||
terminal: [
|
||||
576,
|
||||
'M9.4 86.6C-3.1 74.1-3.1 53.9 9.4 41.4s32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L178.7 256 9.4 86.6zM256 416l288 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-288 0c-17.7 0-32-14.3-32-32s14.3-32 32-32z'
|
||||
@@ -438,29 +469,9 @@ const OxiIcons = {
|
||||
512,
|
||||
'M342.6 9.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l9.4 9.4L28.1 342.6C10.1 360.6 0 385 0 410.5L0 416c0 53 43 96 96 96l5.5 0c25.5 0 49.9-10.1 67.9-28.1L448 205.3l9.4 9.4c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-32-32-96-96-32-32zM205.3 256L352 109.3 402.7 160l-96 96-101.5 0z'
|
||||
],
|
||||
camera: [
|
||||
volume: [
|
||||
512,
|
||||
'M149.1 64.8L138.7 96 64 96C28.7 96 0 124.7 0 160L0 416c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-256c0-35.3-28.7-64-64-64l-74.7 0-10.4-31.2C356.4 45.2 338.1 32 317.4 32L194.6 32c-20.7 0-39 13.2-45.5 32.8zM256 192a96 96 0 1 1 0 192 96 96 0 1 1 0-192z'
|
||||
],
|
||||
'external-link-alt': [
|
||||
512,
|
||||
'M290.4 19.8C295.4 7.8 307.1 0 320 0L480 0c17.7 0 32 14.3 32 32l0 160c0 12.9-7.8 24.6-19.8 29.6s-25.7 2.2-34.9-6.9L400 157.3 246.6 310.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L354.7 112 297.4 54.6c-9.2-9.2-11.9-22.9-6.9-34.9zM0 176c0-44.2 35.8-80 80-80l80 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-80 0c-8.8 0-16 7.2-16 16l0 256c0 8.8 7.2 16 16 16l256 0c8.8 0 16-7.2 16-16l0-80c0-17.7 14.3-32 32-32s32 14.3 32 32l0 80c0 44.2-35.8 80-80 80L80 512c-44.2 0-80-35.8-80-80L0 176z'
|
||||
],
|
||||
'grip-vertical': [
|
||||
320,
|
||||
'M128 40c0-22.1-17.9-40-40-40L40 0C17.9 0 0 17.9 0 40L0 88c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48zm0 192c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48zM0 424l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40zM320 40c0-22.1-17.9-40-40-40L232 0c-22.1 0-40 17.9-40 40l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48zM192 232l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40zM320 424c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40l0 48c0 22.1 17.9 40 40 40l48 0c22.1 0 40-17.9 40-40l0-48z'
|
||||
],
|
||||
'level-up-alt': [
|
||||
384,
|
||||
'M169.4 9.4c12.5-12.5 32.8-12.5 45.3 0l128 128c9.2 9.2 11.9 22.9 6.9 34.9S332.9 192 320 192l-64 0 0 160c0 88.4-71.6 160-160 160l-64 0c-17.7 0-32-14.3-32-32l0-64c0-17.7 14.3-32 32-32l64 0c17.7 0 32-14.3 32-32l0-160-64 0c-12.9 0-24.6-7.8-29.6-19.8s-2.2-25.7 6.9-34.9l128-128z'
|
||||
],
|
||||
music: [
|
||||
512,
|
||||
'M468 7c7.6 6.1 12 15.3 12 25l0 304c0 44.2-43 80-96 80s-96-35.8-96-80 43-80 96-80c11.2 0 22 1.6 32 4.6l0-116.7-224 49.8 0 206.3c0 44.2-43 80-96 80s-96-35.8-96-80 43-80 96-80c11.2 0 22 1.6 32 4.6L128 96c0-15 10.4-28 25.1-31.2l288-64c9.5-2.1 19.4 .2 27 6.3z'
|
||||
],
|
||||
plus: [
|
||||
448,
|
||||
'M256 64c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 160-160 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l160 0 0 160c0 17.7 14.3 32 32 32s32-14.3 32-32l0-160 160 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-160 0 0-160z'
|
||||
'M48 352l48 0 134.1 119.2c6.4 5.7 14.6 8.8 23.1 8.8 19.2 0 34.8-15.6 34.8-34.8l0-378.4c0-19.2-15.6-34.8-34.8-34.8-8.5 0-16.7 3.1-23.1 8.8L96 160 48 160c-26.5 0-48 21.5-48 48l0 96c0 26.5 21.5 48 48 48zM441.1 107c-10.3-8.4-25.4-6.8-33.8 3.5s-6.8 25.4 3.5 33.8C443.3 170.7 464 210.9 464 256s-20.7 85.3-53.2 111.8c-10.3 8.4-11.8 23.5-3.5 33.8s23.5 11.8 33.8 3.5c43.2-35.2 70.9-88.9 70.9-149s-27.7-113.8-70.9-149zm-60.5 74.5c-10.3-8.4-25.4-6.8-33.8 3.5s-6.8 25.4 3.5 33.8C361.1 227.6 368 241 368 256s-6.9 28.4-17.7 37.3c-10.3 8.4-11.8 23.5-3.5 33.8s23.5 11.8 33.8 3.5C402.1 312.9 416 286.1 416 256s-13.9-56.9-35.5-74.5z'
|
||||
],
|
||||
'volume-up': [
|
||||
640,
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
// Apply saved theme immediately (render-blocking) to prevent FOUC.
|
||||
// This file MUST be loaded without "defer" or "async".
|
||||
if (localStorage.getItem('oxicloud_theme') === 'dark') document.documentElement.setAttribute('data-theme', 'dark');
|
||||
// Apply saved colour-scheme choice immediately (render-blocking) to prevent FOUC.
|
||||
// This file MUST be loaded WITHOUT "defer" or "async" so it runs before any paint.
|
||||
//
|
||||
// localStorage values:
|
||||
// 'light' / 'dark' → force that mode (stamp html[data-color-scheme]).
|
||||
// 'auto' / absent → follow the OS preference (no attribute → CSS default
|
||||
// `color-scheme: light dark` resolves via prefers-color-scheme).
|
||||
//
|
||||
// Backward compat: existing users have 'light' / 'dark' stored from the old
|
||||
// binary toggle; both still apply correctly. New 'auto' value is introduced
|
||||
// by the segmented control in user-menu.
|
||||
|
||||
(() => {
|
||||
var saved = localStorage.getItem('oxicloud_theme');
|
||||
var html = document.documentElement;
|
||||
if (saved === 'light' || saved === 'dark') {
|
||||
html.setAttribute('data-color-scheme', saved);
|
||||
} else {
|
||||
html.removeAttribute('data-color-scheme');
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -27,21 +27,19 @@ import { i18n } from '../core/i18n.js';
|
||||
import { systemUsers } from '../model/systemUsers.js';
|
||||
|
||||
// ── Tooltip DOM ───────────────────────────────────────────────────────────────
|
||||
//
|
||||
// The tooltip element is a static node inside `.search-slot` (see index.html)
|
||||
// and is absolutely positioned over `.search-container`. Show/hide is a
|
||||
// simple `.hidden` toggle on the tooltip itself — the search bar underneath
|
||||
// is never touched, so there's no two-element swap to keep in sync.
|
||||
|
||||
/** @returns {HTMLElement} */
|
||||
function _getOrCreateTooltip() {
|
||||
let el = document.getElementById('path-tooltip');
|
||||
if (!el) {
|
||||
el = document.createElement('div');
|
||||
el.id = 'path-tooltip';
|
||||
el.className = 'path-tooltip hidden';
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
return el;
|
||||
/** @returns {HTMLElement | null} */
|
||||
function _getTooltip() {
|
||||
return document.getElementById('path-tooltip');
|
||||
}
|
||||
|
||||
function _hide() {
|
||||
const el = document.getElementById('path-tooltip');
|
||||
const el = _getTooltip();
|
||||
if (el) el.classList.add('hidden');
|
||||
}
|
||||
|
||||
@@ -98,7 +96,8 @@ function _onEnter(e) {
|
||||
// Nothing to show — don't display an all-? tooltip.
|
||||
if (!ownerId && !path) return;
|
||||
|
||||
const tooltip = _getOrCreateTooltip();
|
||||
const tooltip = _getTooltip();
|
||||
if (!tooltip) return;
|
||||
|
||||
// Clear previous content.
|
||||
while (tooltip.firstChild) tooltip.removeChild(tooltip.firstChild);
|
||||
@@ -140,14 +139,19 @@ const _registry = new WeakMap();
|
||||
|
||||
// ── Public API ────────────────────────────────────────────────────────────────
|
||||
|
||||
/** Containers known to expose `data-path`/`data-owner-id` for tooltip use.
|
||||
* Add new opt-in row classes here when other views want the tooltip — each
|
||||
* one must stamp the dataset attributes itself. */
|
||||
const _TOOLTIP_SELECTOR = '.file-item, .ms-resource-row, .ms-grant-row';
|
||||
|
||||
/**
|
||||
* Attach tooltip listeners to every `.file-item` inside `container`.
|
||||
* Items with neither `data-owner-id` nor `data-path` will not trigger the
|
||||
* Attach tooltip listeners to every tooltip-capable row inside `container`.
|
||||
* Rows with neither `data-owner-id` nor `data-path` will not trigger the
|
||||
* tooltip. Safe to call repeatedly — already-wired elements are skipped.
|
||||
* @param {HTMLElement} container
|
||||
*/
|
||||
function init(container) {
|
||||
for (const item of container.querySelectorAll('.file-item')) {
|
||||
for (const item of container.querySelectorAll(_TOOLTIP_SELECTOR)) {
|
||||
const el = /** @type {HTMLElement} */ (item);
|
||||
if (_registry.has(el)) continue; // already wired
|
||||
|
||||
@@ -161,12 +165,12 @@ function init(container) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove tooltip listeners from all `.file-item` elements inside `container`
|
||||
* Remove tooltip listeners from all tooltip-capable rows inside `container`
|
||||
* and hide any visible tooltip.
|
||||
* @param {HTMLElement} container
|
||||
*/
|
||||
function destroy(container) {
|
||||
for (const item of container.querySelectorAll('.file-item')) {
|
||||
for (const item of container.querySelectorAll(_TOOLTIP_SELECTOR)) {
|
||||
const el = /** @type {HTMLElement} */ (item);
|
||||
const h = _registry.get(el);
|
||||
if (h) {
|
||||
|
||||
@@ -31,8 +31,8 @@ import { systemUsers } from '../../model/systemUsers.js';
|
||||
/** @import {FavoritesResourceItem, FileItem, FolderItem, ResourceTypeEnum} from '../../core/types.js' */
|
||||
|
||||
/**
|
||||
* @typedef {{ key: string, label: string, orderBy: string,
|
||||
* keyFn: (item: FileItem|FolderItem) => string|null,
|
||||
* @typedef {{ key: string, label: string, icon?: string, orderBy: string,
|
||||
* keyFn?: (item: FileItem|FolderItem) => string|null,
|
||||
* labelFn?: (key: string) => string,
|
||||
* headerNodeFn?: (key: string) => HTMLElement }} GroupByDef
|
||||
*/
|
||||
@@ -44,11 +44,21 @@ import { systemUsers } from '../../model/systemUsers.js';
|
||||
* @type {GroupByDef[]}
|
||||
*/
|
||||
const GROUP_BY_DEFS = [
|
||||
{
|
||||
key: '',
|
||||
get label() {
|
||||
return i18n.t('files.name', 'Name');
|
||||
},
|
||||
icon: 'fas fa-arrow-up-a-z',
|
||||
orderBy: 'name'
|
||||
// no keyFn → flat list.
|
||||
},
|
||||
{
|
||||
key: 'owner',
|
||||
get label() {
|
||||
return i18n.t('groupby.owner', 'Owner');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'owner',
|
||||
// keyFn groups by UUID — stable, avoids collisions on identical display names.
|
||||
keyFn: (item) => {
|
||||
@@ -63,6 +73,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.type', 'Type');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'type',
|
||||
// keyFn: folders → 'Folder' swimlane; files → their `category` field.
|
||||
keyFn: (item) => ('mime_type' in item ? /** @type {Record<string,string>} */ (/** @type {unknown} */ (item)).category || 'other' : 'Folder'),
|
||||
@@ -92,6 +103,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.size', 'Size');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'size',
|
||||
// Folders have no size — sizeBucket(-1) returns the "Folders" label.
|
||||
keyFn: (item) => {
|
||||
@@ -105,6 +117,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.favoriteDate', 'Favorite date');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'favorited_at',
|
||||
// sort_date is stored as unix seconds in _mapItems().
|
||||
keyFn: (item) => {
|
||||
@@ -117,6 +130,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.modifiedAt', 'Modified date');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'modified_at',
|
||||
// modified_at is a unix seconds timestamp on FileItem/FolderItem.
|
||||
keyFn: (item) => {
|
||||
|
||||
@@ -22,7 +22,7 @@ import { grants } from '../../model/grants.js';
|
||||
/** @import {FileItem, FolderItem} from '../../core/types.js' */
|
||||
|
||||
/**
|
||||
* @typedef {{ key: string, label: string, orderBy: string }} GroupByDef
|
||||
* @typedef {{ key: string, label: string, icon?: string, orderBy: string }} GroupByDef
|
||||
* @typedef {'items'|'sharedWith'} ViewMode
|
||||
*/
|
||||
|
||||
@@ -42,6 +42,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.byFiles', 'By files');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'type'
|
||||
},
|
||||
{
|
||||
@@ -49,6 +50,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.sharedWith', 'Shared with');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'subject'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -32,8 +32,8 @@ import { systemUsers } from '../../model/systemUsers.js';
|
||||
/** @import {FileItem, FolderItem, ResourceTypeEnum} from '../../core/types.js' */
|
||||
|
||||
/**
|
||||
* @typedef {{ key: string, label: string, orderBy: string,
|
||||
* keyFn: (item: FileItem|FolderItem) => string|null,
|
||||
* @typedef {{ key: string, label: string, icon?: string, orderBy: string,
|
||||
* keyFn?: (item: FileItem|FolderItem) => string|null,
|
||||
* labelFn?: (key: string) => string,
|
||||
* headerNodeFn?: (key: string) => HTMLElement }} GroupByDef
|
||||
*/
|
||||
@@ -54,11 +54,21 @@ import { systemUsers } from '../../model/systemUsers.js';
|
||||
* @type {GroupByDef[]}
|
||||
*/
|
||||
const GROUP_BY_DEFS = [
|
||||
{
|
||||
key: '',
|
||||
get label() {
|
||||
return i18n.t('files.name', 'Name');
|
||||
},
|
||||
icon: 'fas fa-arrow-up-a-z',
|
||||
orderBy: 'name'
|
||||
// no keyFn → flat list.
|
||||
},
|
||||
{
|
||||
key: 'owner',
|
||||
get label() {
|
||||
return i18n.t('groupby.owner', 'Owner');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'owner',
|
||||
keyFn: (item) => {
|
||||
const r = /** @type {Record<string,string>} */ (/** @type {unknown} */ (item));
|
||||
@@ -72,6 +82,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.type', 'Type');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'type',
|
||||
keyFn: (item) => ('mime_type' in item ? /** @type {Record<string,string>} */ (/** @type {unknown} */ (item)).category || 'other' : 'Folder'),
|
||||
labelFn: (key) => {
|
||||
@@ -100,6 +111,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.size', 'Size');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'size',
|
||||
keyFn: (item) => {
|
||||
if (!('mime_type' in item)) return sizeBucket(-1);
|
||||
@@ -112,6 +124,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.accessedAt', 'Accessed date');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'accessed_at',
|
||||
// sort_date is unix seconds set in _mapItems(); keyFn returns the bucket label.
|
||||
keyFn: (item) => {
|
||||
@@ -124,6 +137,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.modifiedAt', 'Modified date');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'modified_at',
|
||||
keyFn: (item) => {
|
||||
const r = /** @type {Record<string,number>} */ (/** @type {unknown} */ (item));
|
||||
|
||||
@@ -25,8 +25,8 @@ import { systemUsers } from '../../model/systemUsers.js';
|
||||
/** @import {SharedWithMeItem, FileItem, FolderItem, ResourceTypeEnum} from '../../core/types.js' */
|
||||
|
||||
/**
|
||||
* @typedef {{ key: string, label: string, orderBy: string,
|
||||
* keyFn: (item: FileItem|FolderItem) => string|null,
|
||||
* @typedef {{ key: string, label: string, icon?: string, orderBy: string,
|
||||
* keyFn?: (item: FileItem|FolderItem) => string|null,
|
||||
* labelFn?: (key: string) => string,
|
||||
* headerNodeFn?: (key: string) => HTMLElement }} GroupByDef
|
||||
*/
|
||||
@@ -44,6 +44,15 @@ import { systemUsers } from '../../model/systemUsers.js';
|
||||
* @type {GroupByDef[]}
|
||||
*/
|
||||
const GROUP_BY_DEFS = [
|
||||
{
|
||||
key: '',
|
||||
get label() {
|
||||
return i18n.t('files.name', 'Name');
|
||||
},
|
||||
icon: 'fas fa-arrow-up-a-z',
|
||||
orderBy: 'name'
|
||||
// no keyFn → flat list.
|
||||
},
|
||||
{
|
||||
key: 'owner',
|
||||
// label is accessed via syncGroupByMenu → read at section-switch time,
|
||||
@@ -51,6 +60,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.owner', 'Owner');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'granted_by',
|
||||
// keyFn groups by UUID — stable and unique, avoids collisions between
|
||||
// users with the same display name.
|
||||
@@ -67,6 +77,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.type', 'Type');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'type',
|
||||
// keyFn: folders get their own swimlane; files use the pre-computed
|
||||
// `category` field from the DTO (e.g. 'Image', 'Video', 'Audio' …).
|
||||
@@ -99,6 +110,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.size', 'Size');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'size',
|
||||
// keyFn: the key IS the bucket label returned by sizeBucket(), so no
|
||||
// separate labelFn is needed (same pattern as shareDate).
|
||||
@@ -115,6 +127,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.shareDate', 'Share date');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'granted_at',
|
||||
// keyFn returns the human-readable bucket label; the label IS the key
|
||||
// because consecutive items with the same bucket should be in one group.
|
||||
|
||||
@@ -28,8 +28,8 @@ import { fetchTrashPage } from '../../model/trashModel.js';
|
||||
/** @import {FileItem, FolderItem, ResourceTypeEnum, TrashResourceItem} from '../../core/types.js' */
|
||||
|
||||
/**
|
||||
* @typedef {{ key: string, label: string, orderBy: string, reverseDefault?: boolean,
|
||||
* keyFn: (item: FileItem|FolderItem) => string|null,
|
||||
* @typedef {{ key: string, label: string, icon?: string, orderBy: string, reverseDefault?: boolean,
|
||||
* keyFn?: (item: FileItem|FolderItem) => string|null,
|
||||
* labelFn?: (key: string) => string,
|
||||
* headerNodeFn?: (key: string) => HTMLElement }} GroupByDef
|
||||
*/
|
||||
@@ -49,11 +49,22 @@ import { fetchTrashPage } from '../../model/trashModel.js';
|
||||
* @type {GroupByDef[]}
|
||||
*/
|
||||
const GROUP_BY_DEFS = [
|
||||
{
|
||||
key: '',
|
||||
get label() {
|
||||
return i18n.t('files.name', 'Name');
|
||||
},
|
||||
icon: 'fas fa-arrow-up-a-z',
|
||||
orderBy: 'name',
|
||||
reverseDefault: false
|
||||
// no keyFn → ResourceListComponent renders a flat list (server pins folders first).
|
||||
},
|
||||
{
|
||||
key: 'remainingDays',
|
||||
get label() {
|
||||
return i18n.t('trash.groupby.remaining_days', 'Remaining days');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'deletion_date',
|
||||
reverseDefault: false,
|
||||
keyFn: (item) => {
|
||||
@@ -66,6 +77,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.type', 'Type');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'type',
|
||||
reverseDefault: false,
|
||||
keyFn: (item) => ('mime_type' in item ? /** @type {Record<string,string>} */ (/** @type {unknown} */ (item)).category || 'other' : 'Folder'),
|
||||
@@ -95,6 +107,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('groupby.size', 'Size');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'size',
|
||||
reverseDefault: false,
|
||||
keyFn: (item) => {
|
||||
@@ -108,6 +121,7 @@ const GROUP_BY_DEFS = [
|
||||
get label() {
|
||||
return i18n.t('trash.groupby.trashed_time', 'Trashed time');
|
||||
},
|
||||
icon: 'fas fa-layer-group',
|
||||
orderBy: 'trashed_at',
|
||||
reverseDefault: false,
|
||||
keyFn: (item) => {
|
||||
|
||||
+26
-2
@@ -126,7 +126,12 @@
|
||||
"about_description": "منصة تخزين سحابي مبنية بـ Rust و Clean Architecture. سريعة وآمنة وخاصة.",
|
||||
"admin_panel": "لوحة الإدارة",
|
||||
"profile": "ملفي الشخصي",
|
||||
"role_user": "مستخدم"
|
||||
"role_user": "مستخدم",
|
||||
"theme": {
|
||||
"light": "فاتح",
|
||||
"dark": "داكن",
|
||||
"auto": "مثل النظام"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "رابط المشاركة",
|
||||
@@ -341,10 +346,29 @@
|
||||
"empty_state": "سلة المهملات فارغة",
|
||||
"original_location": "الموقع الأصلي",
|
||||
"deleted_date": "تاريخ الحذف",
|
||||
"remaining": "المتبقي",
|
||||
"actions": "الإجراءات",
|
||||
"restore": "استعادة",
|
||||
"delete_permanently": "حذف نهائياً",
|
||||
"empty_confirm": "هل أنت متأكد أنك تريد تفريغ سلة المهملات؟ سيتم حذف جميع العناصر نهائياً."
|
||||
"empty_confirm": "هل أنت متأكد أنك تريد تفريغ سلة المهملات؟ سيتم حذف جميع العناصر نهائياً.",
|
||||
"groupby": {
|
||||
"remaining_days": "الأيام المتبقية",
|
||||
"trashed_time": "وقت الحذف"
|
||||
}
|
||||
},
|
||||
"daysRemaining": {
|
||||
"expired": "منتهية الصلاحية",
|
||||
"today": "اليوم",
|
||||
"tomorrow": "غدًا",
|
||||
"inDays": "{{count}} يوم"
|
||||
},
|
||||
"expiryChip": {
|
||||
"never": "لا تنتهي الصلاحية",
|
||||
"expired": "منتهية الصلاحية",
|
||||
"today": "تنتهي الصلاحية اليوم",
|
||||
"tomorrow": "تنتهي الصلاحية غدًا",
|
||||
"inDays": "تنتهي الصلاحية خلال {{count}} يوم",
|
||||
"onDate": "تنتهي الصلاحية في {{date}}"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "تسجيل الدخول",
|
||||
|
||||
+26
-2
@@ -126,7 +126,12 @@
|
||||
"about_description": "Cloud-Speicherplattform mit Rust und Clean Architecture. Schnell, sicher und privat.",
|
||||
"admin_panel": "Admin-Panel",
|
||||
"profile": "Mein Profil",
|
||||
"role_user": "Benutzer"
|
||||
"role_user": "Benutzer",
|
||||
"theme": {
|
||||
"light": "Hell",
|
||||
"dark": "Dunkel",
|
||||
"auto": "Wie System"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "Link teilen",
|
||||
@@ -341,10 +346,29 @@
|
||||
"empty_state": "Der Papierkorb ist leer",
|
||||
"original_location": "Ursprünglicher Speicherort",
|
||||
"deleted_date": "Löschdatum",
|
||||
"remaining": "Verbleibend",
|
||||
"actions": "Aktionen",
|
||||
"restore": "Wiederherstellen",
|
||||
"delete_permanently": "Endgültig löschen",
|
||||
"empty_confirm": "Sind Sie sicher, dass Sie den Papierkorb leeren möchten? Alle Elemente werden endgültig gelöscht."
|
||||
"empty_confirm": "Sind Sie sicher, dass Sie den Papierkorb leeren möchten? Alle Elemente werden endgültig gelöscht.",
|
||||
"groupby": {
|
||||
"remaining_days": "Verbleibende Tage",
|
||||
"trashed_time": "Löschzeit"
|
||||
}
|
||||
},
|
||||
"daysRemaining": {
|
||||
"expired": "Abgelaufen",
|
||||
"today": "Heute",
|
||||
"tomorrow": "Morgen",
|
||||
"inDays": "{{count}} Tage"
|
||||
},
|
||||
"expiryChip": {
|
||||
"never": "Läuft nie ab",
|
||||
"expired": "Abgelaufen",
|
||||
"today": "Läuft heute ab",
|
||||
"tomorrow": "Läuft morgen ab",
|
||||
"inDays": "Läuft in {{count}} Tagen ab",
|
||||
"onDate": "Läuft am {{date}} ab"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "Anmelden",
|
||||
|
||||
@@ -126,7 +126,12 @@
|
||||
"about_description": "Cloud storage platform built with Rust & Clean Architecture. Fast, secure, and private.",
|
||||
"admin_panel": "Admin Panel",
|
||||
"profile": "My Profile",
|
||||
"role_user": "User"
|
||||
"role_user": "User",
|
||||
"theme": {
|
||||
"light": "Light",
|
||||
"dark": "Dark",
|
||||
"auto": "Like OS"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "Share Link",
|
||||
|
||||
+26
-2
@@ -245,7 +245,12 @@
|
||||
"about_description": "Plataforma de almacenamiento en la nube creada con Rust y Arquitectura Limpia. Rápida, segura y privada.",
|
||||
"admin_panel": "Panel de administración",
|
||||
"profile": "Mi perfil",
|
||||
"role_user": "Usuario"
|
||||
"role_user": "Usuario",
|
||||
"theme": {
|
||||
"light": "Claro",
|
||||
"dark": "Oscuro",
|
||||
"auto": "Como el sistema"
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"name": "Nombre",
|
||||
@@ -341,10 +346,29 @@
|
||||
"empty_state": "La papelera está vacía",
|
||||
"original_location": "Ubicación original",
|
||||
"deleted_date": "Fecha de eliminación",
|
||||
"remaining": "Restante",
|
||||
"actions": "Acciones",
|
||||
"restore": "Restaurar",
|
||||
"delete_permanently": "Eliminar permanentemente",
|
||||
"empty_confirm": "¿Estás seguro de que quieres vaciar la papelera? Esta acción eliminará permanentemente todos los elementos."
|
||||
"empty_confirm": "¿Estás seguro de que quieres vaciar la papelera? Esta acción eliminará permanentemente todos los elementos.",
|
||||
"groupby": {
|
||||
"remaining_days": "Días restantes",
|
||||
"trashed_time": "Fecha de eliminación"
|
||||
}
|
||||
},
|
||||
"daysRemaining": {
|
||||
"expired": "Caducado",
|
||||
"today": "Hoy",
|
||||
"tomorrow": "Mañana",
|
||||
"inDays": "{{count}} días"
|
||||
},
|
||||
"expiryChip": {
|
||||
"never": "Nunca caduca",
|
||||
"expired": "Caducado",
|
||||
"today": "Caduca hoy",
|
||||
"tomorrow": "Caduca mañana",
|
||||
"inDays": "Caduca en {{count}} días",
|
||||
"onDate": "Caduca el {{date}}"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "Iniciar sesión",
|
||||
|
||||
+26
-2
@@ -126,7 +126,12 @@
|
||||
"about_description": "پلتفرم ذخیرهسازی ابری ساخته شده با Rust و معماری تمیز. سریع، امن و خصوصی.",
|
||||
"admin_panel": "پنل مدیریت",
|
||||
"profile": "نمایه من",
|
||||
"role_user": "کاربر"
|
||||
"role_user": "کاربر",
|
||||
"theme": {
|
||||
"light": "روشن",
|
||||
"dark": "تاریک",
|
||||
"auto": "مانند سیستم"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "پیوند همرسانی",
|
||||
@@ -341,10 +346,29 @@
|
||||
"empty_state": "سطل زباله خالی است",
|
||||
"original_location": "محل اصلی",
|
||||
"deleted_date": "تاریخ حذف",
|
||||
"remaining": "باقیمانده",
|
||||
"actions": "عملیات",
|
||||
"restore": "بازیابی",
|
||||
"delete_permanently": "حذف دائمی",
|
||||
"empty_confirm": "آیا مطمئن هستید که میخواهید سطل زباله را خالی کنید؟ این کار همهٔ موارد را بهطور دائمی حذف خواهد کرد."
|
||||
"empty_confirm": "آیا مطمئن هستید که میخواهید سطل زباله را خالی کنید؟ این کار همهٔ موارد را بهطور دائمی حذف خواهد کرد.",
|
||||
"groupby": {
|
||||
"remaining_days": "روزهای باقیمانده",
|
||||
"trashed_time": "زمان حذف"
|
||||
}
|
||||
},
|
||||
"daysRemaining": {
|
||||
"expired": "منقضی شده",
|
||||
"today": "امروز",
|
||||
"tomorrow": "فردا",
|
||||
"inDays": "{{count}} روز"
|
||||
},
|
||||
"expiryChip": {
|
||||
"never": "هرگز منقضی نمیشود",
|
||||
"expired": "منقضی شده",
|
||||
"today": "امروز منقضی میشود",
|
||||
"tomorrow": "فردا منقضی میشود",
|
||||
"inDays": "در {{count}} روز منقضی میشود",
|
||||
"onDate": "در {{date}} منقضی میشود"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "ورود",
|
||||
|
||||
+26
-2
@@ -126,7 +126,12 @@
|
||||
"about_description": "Plateforme de stockage cloud construite avec Rust et Architecture Propre. Rapide, sécurisée et privée.",
|
||||
"admin_panel": "Panneau d'administration",
|
||||
"profile": "Mon profil",
|
||||
"role_user": "Utilisateur"
|
||||
"role_user": "Utilisateur",
|
||||
"theme": {
|
||||
"light": "Clair",
|
||||
"dark": "Sombre",
|
||||
"auto": "Comme le système"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "Lien de partage",
|
||||
@@ -341,10 +346,29 @@
|
||||
"empty_state": "La corbeille est vide",
|
||||
"original_location": "Emplacement d'origine",
|
||||
"deleted_date": "Date de suppression",
|
||||
"remaining": "Restant",
|
||||
"actions": "Actions",
|
||||
"restore": "Restaurer",
|
||||
"delete_permanently": "Supprimer définitivement",
|
||||
"empty_confirm": "Êtes-vous sûr de vouloir vider la corbeille ? Tous les éléments seront définitivement supprimés."
|
||||
"empty_confirm": "Êtes-vous sûr de vouloir vider la corbeille ? Tous les éléments seront définitivement supprimés.",
|
||||
"groupby": {
|
||||
"remaining_days": "Jours restants",
|
||||
"trashed_time": "Date de suppression"
|
||||
}
|
||||
},
|
||||
"daysRemaining": {
|
||||
"expired": "Expiré",
|
||||
"today": "Aujourd'hui",
|
||||
"tomorrow": "Demain",
|
||||
"inDays": "{{count}} jours"
|
||||
},
|
||||
"expiryChip": {
|
||||
"never": "N'expire jamais",
|
||||
"expired": "Expiré",
|
||||
"today": "Expire aujourd'hui",
|
||||
"tomorrow": "Expire demain",
|
||||
"inDays": "Expire dans {{count}} jours",
|
||||
"onDate": "Expire le {{date}}"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "Se connecter",
|
||||
|
||||
+26
-2
@@ -126,7 +126,12 @@
|
||||
"about_description": "Rust और Clean Architecture से बना क्लाउड स्टोरेज प्लेटफ़ॉर्म। तेज़, सुरक्षित और निजी।",
|
||||
"admin_panel": "एडमिन पैनल",
|
||||
"profile": "मेरी प्रोफ़ाइल",
|
||||
"role_user": "उपयोगकर्ता"
|
||||
"role_user": "उपयोगकर्ता",
|
||||
"theme": {
|
||||
"light": "हल्का",
|
||||
"dark": "गहरा",
|
||||
"auto": "सिस्टम जैसा"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "शेयर लिंक",
|
||||
@@ -341,10 +346,29 @@
|
||||
"empty_state": "रद्दी खाली है",
|
||||
"original_location": "मूल स्थान",
|
||||
"deleted_date": "हटाने की तिथि",
|
||||
"remaining": "शेष",
|
||||
"actions": "कार्य",
|
||||
"restore": "पुनर्स्थापित करें",
|
||||
"delete_permanently": "स्थायी रूप से हटाएँ",
|
||||
"empty_confirm": "क्या आप वाकई रद्दी खाली करना चाहते हैं? यह सभी आइटम स्थायी रूप से हटा देगा।"
|
||||
"empty_confirm": "क्या आप वाकई रद्दी खाली करना चाहते हैं? यह सभी आइटम स्थायी रूप से हटा देगा।",
|
||||
"groupby": {
|
||||
"remaining_days": "शेष दिन",
|
||||
"trashed_time": "हटाने का समय"
|
||||
}
|
||||
},
|
||||
"daysRemaining": {
|
||||
"expired": "समाप्त",
|
||||
"today": "आज",
|
||||
"tomorrow": "कल",
|
||||
"inDays": "{{count}} दिन"
|
||||
},
|
||||
"expiryChip": {
|
||||
"never": "कभी समाप्त नहीं होता",
|
||||
"expired": "समाप्त",
|
||||
"today": "आज समाप्त होता है",
|
||||
"tomorrow": "कल समाप्त होता है",
|
||||
"inDays": "{{count}} दिनों में समाप्त होता है",
|
||||
"onDate": "{{date}} को समाप्त होता है"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "साइन इन",
|
||||
|
||||
+26
-2
@@ -126,7 +126,12 @@
|
||||
"about_description": "Piattaforma di archiviazione cloud realizzata con Rust & Architettura Pulita. Veloce, sicura e privata.",
|
||||
"admin_panel": "Pannello di amministrazione",
|
||||
"profile": "Il mio profilo",
|
||||
"role_user": "Utente"
|
||||
"role_user": "Utente",
|
||||
"theme": {
|
||||
"light": "Chiaro",
|
||||
"dark": "Scuro",
|
||||
"auto": "Come il sistema"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "Link di condivisione",
|
||||
@@ -341,10 +346,29 @@
|
||||
"empty_state": "Il cestino è vuoto",
|
||||
"original_location": "Posizione originale",
|
||||
"deleted_date": "Data di eliminazione",
|
||||
"remaining": "Rimanente",
|
||||
"actions": "Azioni",
|
||||
"restore": "Ripristina",
|
||||
"delete_permanently": "Elimina definitivamente",
|
||||
"empty_confirm": "Sei sicuro di voler svuotare il cestino? Questa operazione eliminerà definitivamente tutti gli elementi."
|
||||
"empty_confirm": "Sei sicuro di voler svuotare il cestino? Questa operazione eliminerà definitivamente tutti gli elementi.",
|
||||
"groupby": {
|
||||
"remaining_days": "Giorni rimanenti",
|
||||
"trashed_time": "Data di eliminazione"
|
||||
}
|
||||
},
|
||||
"daysRemaining": {
|
||||
"expired": "Scaduto",
|
||||
"today": "Oggi",
|
||||
"tomorrow": "Domani",
|
||||
"inDays": "{{count}} giorni"
|
||||
},
|
||||
"expiryChip": {
|
||||
"never": "Non scade mai",
|
||||
"expired": "Scaduto",
|
||||
"today": "Scade oggi",
|
||||
"tomorrow": "Scade domani",
|
||||
"inDays": "Scade tra {{count}} giorni",
|
||||
"onDate": "Scade il {{date}}"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "Accedi",
|
||||
|
||||
+26
-2
@@ -126,7 +126,12 @@
|
||||
"about_description": "RustとClean Architectureで構築されたクラウドストレージプラットフォーム。高速・安全・プライベート。",
|
||||
"admin_panel": "管理パネル",
|
||||
"profile": "マイプロフィール",
|
||||
"role_user": "ユーザー"
|
||||
"role_user": "ユーザー",
|
||||
"theme": {
|
||||
"light": "ライト",
|
||||
"dark": "ダーク",
|
||||
"auto": "システムに合わせる"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "共有リンク",
|
||||
@@ -341,10 +346,29 @@
|
||||
"empty_state": "ゴミ箱は空です",
|
||||
"original_location": "元の場所",
|
||||
"deleted_date": "削除日",
|
||||
"remaining": "残り",
|
||||
"actions": "操作",
|
||||
"restore": "復元",
|
||||
"delete_permanently": "完全に削除",
|
||||
"empty_confirm": "ゴミ箱を空にしますか?すべてのアイテムが完全に削除されます。"
|
||||
"empty_confirm": "ゴミ箱を空にしますか?すべてのアイテムが完全に削除されます。",
|
||||
"groupby": {
|
||||
"remaining_days": "残り日数",
|
||||
"trashed_time": "削除日時"
|
||||
}
|
||||
},
|
||||
"daysRemaining": {
|
||||
"expired": "期限切れ",
|
||||
"today": "今日",
|
||||
"tomorrow": "明日",
|
||||
"inDays": "{{count}}日"
|
||||
},
|
||||
"expiryChip": {
|
||||
"never": "期限なし",
|
||||
"expired": "期限切れ",
|
||||
"today": "今日で期限切れ",
|
||||
"tomorrow": "明日で期限切れ",
|
||||
"inDays": "{{count}}日後に期限切れ",
|
||||
"onDate": "{{date}}に期限切れ"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "サインイン",
|
||||
|
||||
+26
-2
@@ -126,7 +126,12 @@
|
||||
"about_description": "Rust와 Clean Architecture로 구축된 클라우드 스토리지 플랫폼. 빠르고, 안전하고, 프라이빗합니다.",
|
||||
"admin_panel": "관리자 패널",
|
||||
"profile": "내 프로필",
|
||||
"role_user": "사용자"
|
||||
"role_user": "사용자",
|
||||
"theme": {
|
||||
"light": "라이트",
|
||||
"dark": "다크",
|
||||
"auto": "시스템과 동일"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "공유 링크",
|
||||
@@ -341,10 +346,29 @@
|
||||
"empty_state": "휴지통이 비어 있습니다",
|
||||
"original_location": "원래 위치",
|
||||
"deleted_date": "삭제일",
|
||||
"remaining": "남음",
|
||||
"actions": "작업",
|
||||
"restore": "복원",
|
||||
"delete_permanently": "영구 삭제",
|
||||
"empty_confirm": "휴지통을 비우시겠습니까? 모든 항목이 영구적으로 삭제됩니다."
|
||||
"empty_confirm": "휴지통을 비우시겠습니까? 모든 항목이 영구적으로 삭제됩니다.",
|
||||
"groupby": {
|
||||
"remaining_days": "남은 일수",
|
||||
"trashed_time": "삭제 시간"
|
||||
}
|
||||
},
|
||||
"daysRemaining": {
|
||||
"expired": "만료됨",
|
||||
"today": "오늘",
|
||||
"tomorrow": "내일",
|
||||
"inDays": "{{count}}일"
|
||||
},
|
||||
"expiryChip": {
|
||||
"never": "만료되지 않음",
|
||||
"expired": "만료됨",
|
||||
"today": "오늘 만료",
|
||||
"tomorrow": "내일 만료",
|
||||
"inDays": "{{count}}일 후 만료",
|
||||
"onDate": "{{date}}에 만료"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "로그인",
|
||||
|
||||
+26
-2
@@ -126,7 +126,12 @@
|
||||
"about_description": "Cloudopslagplatform gebouwd met Rust & Clean Architecture. Snel, veilig en privé.",
|
||||
"admin_panel": "Beheerpaneel",
|
||||
"profile": "Mijn profiel",
|
||||
"role_user": "Gebruiker"
|
||||
"role_user": "Gebruiker",
|
||||
"theme": {
|
||||
"light": "Licht",
|
||||
"dark": "Donker",
|
||||
"auto": "Zoals systeem"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "Deellink",
|
||||
@@ -341,10 +346,29 @@
|
||||
"empty_state": "Prullenbak is leeg",
|
||||
"original_location": "Oorspronkelijke locatie",
|
||||
"deleted_date": "Verwijderdatum",
|
||||
"remaining": "Resterend",
|
||||
"actions": "Acties",
|
||||
"restore": "Herstellen",
|
||||
"delete_permanently": "Permanent verwijderen",
|
||||
"empty_confirm": "Weet je zeker dat je de prullenbak wilt legen? Dit verwijdert alle items permanent."
|
||||
"empty_confirm": "Weet je zeker dat je de prullenbak wilt legen? Dit verwijdert alle items permanent.",
|
||||
"groupby": {
|
||||
"remaining_days": "Resterende dagen",
|
||||
"trashed_time": "Verwijderd op"
|
||||
}
|
||||
},
|
||||
"daysRemaining": {
|
||||
"expired": "Verlopen",
|
||||
"today": "Vandaag",
|
||||
"tomorrow": "Morgen",
|
||||
"inDays": "{{count}} dagen"
|
||||
},
|
||||
"expiryChip": {
|
||||
"never": "Verloopt nooit",
|
||||
"expired": "Verlopen",
|
||||
"today": "Verloopt vandaag",
|
||||
"tomorrow": "Verloopt morgen",
|
||||
"inDays": "Verloopt over {{count}} dagen",
|
||||
"onDate": "Verloopt op {{date}}"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "Inloggen",
|
||||
|
||||
+26
-2
@@ -126,7 +126,12 @@
|
||||
"about_description": "Platforma pamięci masowej w chmurze zbudowana w oparciu o Rust & Clean Architecture. Szybka, bezpieczna i prywatna.",
|
||||
"admin_panel": "Panel administratora",
|
||||
"profile": "Mój profil",
|
||||
"role_user": "Użytkownik"
|
||||
"role_user": "Użytkownik",
|
||||
"theme": {
|
||||
"light": "Jasny",
|
||||
"dark": "Ciemny",
|
||||
"auto": "Jak system"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "Link udostępniania",
|
||||
@@ -341,10 +346,29 @@
|
||||
"empty_state": "Kosz jest pusty",
|
||||
"original_location": "Pierwotna lokalizacja",
|
||||
"deleted_date": "Data usunięcia",
|
||||
"remaining": "Pozostało",
|
||||
"actions": "Akcje",
|
||||
"restore": "Przywróć",
|
||||
"delete_permanently": "Usuń trwale",
|
||||
"empty_confirm": "Czy na pewno chcesz opróżnić kosz? Wszystkie elementy zostaną trwale usunięte."
|
||||
"empty_confirm": "Czy na pewno chcesz opróżnić kosz? Wszystkie elementy zostaną trwale usunięte.",
|
||||
"groupby": {
|
||||
"remaining_days": "Pozostałe dni",
|
||||
"trashed_time": "Czas usunięcia"
|
||||
}
|
||||
},
|
||||
"daysRemaining": {
|
||||
"expired": "Wygasł",
|
||||
"today": "Dziś",
|
||||
"tomorrow": "Jutro",
|
||||
"inDays": "{{count}} dni"
|
||||
},
|
||||
"expiryChip": {
|
||||
"never": "Nigdy nie wygasa",
|
||||
"expired": "Wygasł",
|
||||
"today": "Wygasa dziś",
|
||||
"tomorrow": "Wygasa jutro",
|
||||
"inDays": "Wygasa za {{count}} dni",
|
||||
"onDate": "Wygasa {{date}}"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "Zaloguj się",
|
||||
|
||||
+26
-2
@@ -126,7 +126,12 @@
|
||||
"about_description": "Plataforma de armazenamento em nuvem construída com Rust e Arquitetura Limpa. Rápida, segura e privada.",
|
||||
"admin_panel": "Painel de administração",
|
||||
"profile": "Meu perfil",
|
||||
"role_user": "Usuário"
|
||||
"role_user": "Usuário",
|
||||
"theme": {
|
||||
"light": "Claro",
|
||||
"dark": "Escuro",
|
||||
"auto": "Como o sistema"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "Link de compartilhamento",
|
||||
@@ -341,10 +346,29 @@
|
||||
"empty_state": "A lixeira está vazia",
|
||||
"original_location": "Local original",
|
||||
"deleted_date": "Data de exclusão",
|
||||
"remaining": "Restante",
|
||||
"actions": "Ações",
|
||||
"restore": "Restaurar",
|
||||
"delete_permanently": "Excluir permanentemente",
|
||||
"empty_confirm": "Tem certeza de que deseja esvaziar a lixeira? Todos os itens serão excluídos permanentemente."
|
||||
"empty_confirm": "Tem certeza de que deseja esvaziar a lixeira? Todos os itens serão excluídos permanentemente.",
|
||||
"groupby": {
|
||||
"remaining_days": "Dias restantes",
|
||||
"trashed_time": "Data de exclusão"
|
||||
}
|
||||
},
|
||||
"daysRemaining": {
|
||||
"expired": "Expirado",
|
||||
"today": "Hoje",
|
||||
"tomorrow": "Amanhã",
|
||||
"inDays": "{{count}} dias"
|
||||
},
|
||||
"expiryChip": {
|
||||
"never": "Nunca expira",
|
||||
"expired": "Expirado",
|
||||
"today": "Expira hoje",
|
||||
"tomorrow": "Expira amanhã",
|
||||
"inDays": "Expira em {{count}} dias",
|
||||
"onDate": "Expira em {{date}}"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "Entrar",
|
||||
|
||||
+26
-2
@@ -126,7 +126,12 @@
|
||||
"about_description": "Платформа облачного хранения на Rust с чистой архитектурой. Быстрая, безопасная и конфиденциальная.",
|
||||
"admin_panel": "Панель администратора",
|
||||
"profile": "Мой профиль",
|
||||
"role_user": "Пользователь"
|
||||
"role_user": "Пользователь",
|
||||
"theme": {
|
||||
"light": "Светлая",
|
||||
"dark": "Тёмная",
|
||||
"auto": "Как в системе"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "Ссылка для обмена",
|
||||
@@ -341,10 +346,29 @@
|
||||
"empty_state": "Корзина пуста",
|
||||
"original_location": "Исходное расположение",
|
||||
"deleted_date": "Дата удаления",
|
||||
"remaining": "Осталось",
|
||||
"actions": "Действия",
|
||||
"restore": "Восстановить",
|
||||
"delete_permanently": "Удалить навсегда",
|
||||
"empty_confirm": "Вы уверены, что хотите очистить корзину? Все элементы будут удалены навсегда."
|
||||
"empty_confirm": "Вы уверены, что хотите очистить корзину? Все элементы будут удалены навсегда.",
|
||||
"groupby": {
|
||||
"remaining_days": "Осталось дней",
|
||||
"trashed_time": "Время удаления"
|
||||
}
|
||||
},
|
||||
"daysRemaining": {
|
||||
"expired": "Истёк",
|
||||
"today": "Сегодня",
|
||||
"tomorrow": "Завтра",
|
||||
"inDays": "{{count}} дн."
|
||||
},
|
||||
"expiryChip": {
|
||||
"never": "Никогда не истекает",
|
||||
"expired": "Истёк",
|
||||
"today": "Истекает сегодня",
|
||||
"tomorrow": "Истекает завтра",
|
||||
"inDays": "Истекает через {{count}} дн.",
|
||||
"onDate": "Истекает {{date}}"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "Вход",
|
||||
|
||||
@@ -126,7 +126,12 @@
|
||||
"about_description": "基於 Rust 和整潔架構構建的雲端儲存平臺。快速、安全、私密。",
|
||||
"admin_panel": "管理面板",
|
||||
"profile": "我的資料",
|
||||
"role_user": "使用者"
|
||||
"role_user": "使用者",
|
||||
"theme": {
|
||||
"light": "淺色",
|
||||
"dark": "深色",
|
||||
"auto": "跟隨系統"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "共享連結",
|
||||
@@ -341,10 +346,29 @@
|
||||
"empty_state": "回收站為空",
|
||||
"original_location": "原始位置",
|
||||
"deleted_date": "刪除日期",
|
||||
"remaining": "剩餘",
|
||||
"actions": "操作",
|
||||
"restore": "恢復",
|
||||
"delete_permanently": "永久刪除",
|
||||
"empty_confirm": "你確定要清空回收站嗎?這將永久刪除所有專案。"
|
||||
"empty_confirm": "你確定要清空回收站嗎?這將永久刪除所有專案。",
|
||||
"groupby": {
|
||||
"remaining_days": "剩餘天數",
|
||||
"trashed_time": "刪除時間"
|
||||
}
|
||||
},
|
||||
"daysRemaining": {
|
||||
"expired": "已過期",
|
||||
"today": "今天",
|
||||
"tomorrow": "明天",
|
||||
"inDays": "{{count}} 天"
|
||||
},
|
||||
"expiryChip": {
|
||||
"never": "永不過期",
|
||||
"expired": "已過期",
|
||||
"today": "今天到期",
|
||||
"tomorrow": "明天到期",
|
||||
"inDays": "{{count}} 天後到期",
|
||||
"onDate": "於 {{date}} 到期"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "登入",
|
||||
|
||||
+26
-2
@@ -126,7 +126,12 @@
|
||||
"about_description": "基于 Rust 和整洁架构构建的云存储平台。快速、安全、私密。",
|
||||
"admin_panel": "管理面板",
|
||||
"profile": "我的资料",
|
||||
"role_user": "用户"
|
||||
"role_user": "用户",
|
||||
"theme": {
|
||||
"light": "浅色",
|
||||
"dark": "深色",
|
||||
"auto": "跟随系统"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "共享链接",
|
||||
@@ -341,10 +346,29 @@
|
||||
"empty_state": "回收站为空",
|
||||
"original_location": "原始位置",
|
||||
"deleted_date": "删除日期",
|
||||
"remaining": "剩余",
|
||||
"actions": "操作",
|
||||
"restore": "恢复",
|
||||
"delete_permanently": "永久删除",
|
||||
"empty_confirm": "你确定要清空回收站吗?这将永久删除所有项目。"
|
||||
"empty_confirm": "你确定要清空回收站吗?这将永久删除所有项目。",
|
||||
"groupby": {
|
||||
"remaining_days": "剩余天数",
|
||||
"trashed_time": "删除时间"
|
||||
}
|
||||
},
|
||||
"daysRemaining": {
|
||||
"expired": "已过期",
|
||||
"today": "今天",
|
||||
"tomorrow": "明天",
|
||||
"inDays": "{{count}} 天"
|
||||
},
|
||||
"expiryChip": {
|
||||
"never": "永不过期",
|
||||
"expired": "已过期",
|
||||
"today": "今天到期",
|
||||
"tomorrow": "明天到期",
|
||||
"inDays": "{{count}} 天后到期",
|
||||
"onDate": "于 {{date}} 到期"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "登录",
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<!-- Declares the page supports both modes. Switching between them is
|
||||
driven by the CSS `color-scheme` property (via the `data-color-scheme`
|
||||
attribute on <html> set by theme-init.js), not by mutating this tag. -->
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<title data-i18n="app.title">OxiCloud - Login</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/logo/logo-plain.svg">
|
||||
<!-- Apply saved theme immediately to prevent flash of light mode -->
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<!-- Declares the page supports both modes. Switching between them is
|
||||
driven by the CSS `color-scheme` property (via the `data-color-scheme`
|
||||
attribute on <html> set by theme-init.js), not by mutating this tag. -->
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<title>OxiCloud — My Profile</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/logo/logo-plain.svg">
|
||||
<script src="/js/core/theme-init.js"></script>
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<!-- Declares the page supports both modes. Switching between them is
|
||||
driven by the CSS `color-scheme` property (via the `data-color-scheme`
|
||||
attribute on <html> set by theme-init.js), not by mutating this tag. -->
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<title>OxiCloud — Shared</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/logo/logo-plain.svg">
|
||||
<script src="/js/core/theme-init.js"></script>
|
||||
|
||||
@@ -59,12 +59,15 @@ test.describe('authenticated as admin', () => {
|
||||
test('theme can be changed to dark', async ({ page }) => {
|
||||
await page.locator('#user-avatar-btn').click();
|
||||
await expect(page.locator('#user-menu')).toBeVisible();
|
||||
await page.locator('#theme-toggle-pill').click();
|
||||
|
||||
// html element must carry data-theme="dark"
|
||||
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');
|
||||
// The appearance row is now a 3-option segmented control
|
||||
// (Light / Like OS / Dark). Each option carries a `data-mode` attribute.
|
||||
await page.locator('.theme-segmented__opt[data-mode="dark"]').click();
|
||||
|
||||
// localStorage must persist the choice
|
||||
// html element must carry data-color-scheme="dark" (new attribute).
|
||||
await expect(page.locator('html')).toHaveAttribute('data-color-scheme', 'dark');
|
||||
|
||||
// localStorage must persist the choice.
|
||||
const theme = await page.evaluate(() => localStorage.getItem('oxicloud_theme'));
|
||||
expect(theme).toBe('dark');
|
||||
|
||||
@@ -75,9 +78,9 @@ test.describe('authenticated as admin', () => {
|
||||
mask: [page.locator('.storage-bar'), page.locator('.storage-info') ]
|
||||
});
|
||||
|
||||
// Toggle back to light
|
||||
await page.locator('#theme-toggle-pill').click();
|
||||
await expect(page.locator('html')).not.toHaveAttribute('data-theme', 'dark');
|
||||
// Switch back to light via the Light option.
|
||||
await page.locator('.theme-segmented__opt[data-mode="light"]').click();
|
||||
await expect(page.locator('html')).toHaveAttribute('data-color-scheme', 'light');
|
||||
|
||||
const themeAfter = await page.evaluate(() => localStorage.getItem('oxicloud_theme'));
|
||||
expect(themeAfter).toBe('light');
|
||||
|
||||
Reference in New Issue
Block a user