feat: enable Immich ML, dark mode fixes, sidebar improvements #6
@@ -44,19 +44,24 @@
|
||||
{ label: "Speedtest", remoteHref: "https://speed.jimmygan.com:8443", icon: "speedtest", external: true },
|
||||
];
|
||||
|
||||
const defaultTools2 = [];
|
||||
|
||||
function itemKey(item) { return item.id || item.label; }
|
||||
|
||||
// All items indexed by key for cross-category lookup
|
||||
const allItems = new Map([...defaultLinks, ...defaultMedia, ...defaultTools].map(i => [itemKey(i), i]));
|
||||
const allItems = new Map([...defaultLinks, ...defaultMedia, ...defaultTools, ...defaultTools2].map(i => [itemKey(i), i]));
|
||||
|
||||
// Build category lists from saved order, supporting cross-category moves
|
||||
const categories = ["main", "media", "tools", "tools2"];
|
||||
const categoryDefaults = { main: defaultLinks, media: defaultMedia, tools: defaultTools, tools2: defaultTools2 };
|
||||
|
||||
function buildLists(savedOrder) {
|
||||
if (!savedOrder) return { main: [...defaultLinks], media: [...defaultMedia], tools: [...defaultTools] };
|
||||
if (!savedOrder) return { main: [...defaultLinks], media: [...defaultMedia], tools: [...defaultTools], tools2: [...defaultTools2] };
|
||||
|
||||
const used = new Set();
|
||||
const result = { main: [], media: [], tools: [] };
|
||||
const result = { main: [], media: [], tools: [], tools2: [] };
|
||||
|
||||
for (const cat of ["main", "media", "tools"]) {
|
||||
for (const cat of categories) {
|
||||
const keys = savedOrder[cat] || [];
|
||||
for (const k of keys) {
|
||||
if (allItems.has(k) && !used.has(k)) {
|
||||
@@ -67,9 +72,8 @@
|
||||
}
|
||||
|
||||
// Append any new items not in saved order to their default category
|
||||
const defaults = { main: defaultLinks, media: defaultMedia, tools: defaultTools };
|
||||
for (const cat of ["main", "media", "tools"]) {
|
||||
for (const item of defaults[cat]) {
|
||||
for (const cat of categories) {
|
||||
for (const item of categoryDefaults[cat]) {
|
||||
if (!used.has(itemKey(item))) {
|
||||
result[cat].push(item);
|
||||
used.add(itemKey(item));
|
||||
@@ -84,6 +88,10 @@
|
||||
let links = $derived(categoryLists.main);
|
||||
let media = $derived(categoryLists.media);
|
||||
let tools = $derived(categoryLists.tools);
|
||||
let tools2 = $derived(categoryLists.tools2);
|
||||
|
||||
// Collapsed state for categories (persisted in sidebarOrder)
|
||||
let collapsed = $state(sidebarOrder?.collapsed ? { ...sidebarOrder.collapsed } : {});
|
||||
|
||||
// Drag-and-drop state
|
||||
let dragCategory = $state(null);
|
||||
@@ -116,13 +124,15 @@
|
||||
e.preventDefault();
|
||||
if (dragCategory === null) { handleDragEnd(); return; }
|
||||
if (dragCategory === category && dragIndex === index) { handleDragEnd(); return; }
|
||||
const lists = { main: [...links], media: [...media], tools: [...tools] };
|
||||
const lists = { main: [...links], media: [...media], tools: [...tools], tools2: [...tools2] };
|
||||
const [moved] = lists[dragCategory].splice(dragIndex, 1);
|
||||
lists[category].splice(index, 0, moved);
|
||||
const newOrder = {
|
||||
main: lists.main.map(itemKey),
|
||||
media: lists.media.map(itemKey),
|
||||
tools: lists.tools.map(itemKey),
|
||||
tools2: lists.tools2.map(itemKey),
|
||||
collapsed,
|
||||
};
|
||||
onOrderChange(newOrder);
|
||||
handleDragEnd();
|
||||
@@ -139,13 +149,15 @@
|
||||
function handleCategoryDrop(category, e) {
|
||||
e.preventDefault();
|
||||
if (dragCategory === null) { handleDragEnd(); return; }
|
||||
const lists = { main: [...links], media: [...media], tools: [...tools] };
|
||||
const lists = { main: [...links], media: [...media], tools: [...tools], tools2: [...tools2] };
|
||||
const [moved] = lists[dragCategory].splice(dragIndex, 1);
|
||||
lists[category].push(moved);
|
||||
const newOrder = {
|
||||
main: lists.main.map(itemKey),
|
||||
media: lists.media.map(itemKey),
|
||||
tools: lists.tools.map(itemKey),
|
||||
tools2: lists.tools2.map(itemKey),
|
||||
collapsed,
|
||||
};
|
||||
onOrderChange(newOrder);
|
||||
handleDragEnd();
|
||||
@@ -171,6 +183,18 @@
|
||||
if (dropCategory === category && index === dropIndex) return "border-t-2 border-primary-500";
|
||||
return "";
|
||||
}
|
||||
|
||||
function toggleCollapse(cat) {
|
||||
collapsed[cat] = !collapsed[cat];
|
||||
const newOrder = {
|
||||
main: links.map(itemKey),
|
||||
media: media.map(itemKey),
|
||||
tools: tools.map(itemKey),
|
||||
tools2: tools2.map(itemKey),
|
||||
collapsed,
|
||||
};
|
||||
onOrderChange(newOrder);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#snippet iconSvg(icon)}
|
||||
@@ -232,11 +256,23 @@
|
||||
|
||||
<!-- Nav Links -->
|
||||
<nav class="flex-1 px-3 mt-2 overflow-y-auto">
|
||||
{#each [["main", "Main", links], ["media", "Media", media], ["tools", "Tools", tools]] as [cat, label, items], ci}
|
||||
{#each [["main", "Main", links], ["media", "Media", media], ["tools", "Tools", tools], ["tools2", "Tools 2", tools2]] as [cat, label, items], ci}
|
||||
{#if ci > 0}
|
||||
<div class="my-4 border-t border-surface-200 dark:border-surface-700"></div>
|
||||
{/if}
|
||||
<p class="text-[10px] font-semibold uppercase tracking-wider text-surface-400 px-3 mb-2" ondragover={(e) => handleCategoryDragOver(cat, e)} ondrop={(e) => handleCategoryDrop(cat, e)}>{label}</p>
|
||||
<button
|
||||
class="w-full text-left text-[10px] font-semibold uppercase tracking-wider text-surface-400 px-3 mb-2 flex items-center justify-between {cat !== 'main' ? 'cursor-pointer' : ''}"
|
||||
ondragover={(e) => handleCategoryDragOver(cat, e)}
|
||||
ondrop={(e) => handleCategoryDrop(cat, e)}
|
||||
onclick={() => cat !== 'main' && toggleCollapse(cat)}
|
||||
disabled={cat === 'main'}
|
||||
>
|
||||
{label}
|
||||
{#if cat !== "main"}
|
||||
<svg class="w-3 h-3 opacity-30 hover:opacity-50 transition-all duration-150 {collapsed[cat] ? '-rotate-90' : ''}" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" /></svg>
|
||||
{/if}
|
||||
</button>
|
||||
{#if cat === "main" || !collapsed[cat]}
|
||||
{#each items as item, i}
|
||||
{#if canAccess(item.id)}
|
||||
{#if item.external || item.remoteHref || item.port}
|
||||
@@ -277,6 +313,7 @@
|
||||
{/if}
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
{/each}
|
||||
</nav>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user