mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 19:36:36 +08:00
b0fe40142a
* Rebrand MMTL to MeBox across codebase and assets Rename the project display name, Go module path, environment variable prefix (MEBOX_*), Docker image references, and UI branding from MMTL/mmtl to MeBox/mebox. Replace logo assets with the new MeBox icon and keep legacy SQLite migration support for existing mmtl.db deployments. Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Fix logo icons: use cube-only crop without truncated text Previous icon generation cropped too much of the source image, including partial MeBox wordmark text that was cut off in square icon containers. Regenerate logo-64/192/512, favicon, and SVG from cube-only region. Co-authored-by: truewhile <truewhile@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: truewhile <truewhile@users.noreply.github.com>
49 lines
1.6 KiB
TypeScript
49 lines
1.6 KiB
TypeScript
const STORAGE_KEY = 'mebox_pinned_libraries'
|
|
|
|
function parsePinnedIds(raw: string | null): string[] {
|
|
if (!raw) return []
|
|
try {
|
|
const parsed = JSON.parse(raw)
|
|
if (!Array.isArray(parsed)) return []
|
|
return parsed.filter((id): id is string => typeof id === 'string' && id.trim() !== '')
|
|
} catch {
|
|
return []
|
|
}
|
|
}
|
|
|
|
export function readPinnedLibraryIds(): string[] {
|
|
if (typeof window === 'undefined') return []
|
|
return parsePinnedIds(window.localStorage.getItem(STORAGE_KEY))
|
|
}
|
|
|
|
export function writePinnedLibraryIds(ids: string[]): void {
|
|
if (typeof window === 'undefined') return
|
|
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(ids))
|
|
}
|
|
|
|
export function togglePinnedLibraryId(id: string): string[] {
|
|
const current = readPinnedLibraryIds()
|
|
const index = current.indexOf(id)
|
|
const next = index >= 0 ? current.filter((item) => item !== id) : [...current, id]
|
|
writePinnedLibraryIds(next)
|
|
return next
|
|
}
|
|
|
|
export function isLibraryPinned(id: string, pinnedIds: string[]): boolean {
|
|
return pinnedIds.includes(id)
|
|
}
|
|
|
|
export function sortLibraryPreviews<T extends { library: { id: string } }>(items: T[], pinnedIds: string[]): T[] {
|
|
if (pinnedIds.length === 0) return items
|
|
const rank = new Map(pinnedIds.map((id, index) => [id, index]))
|
|
return [...items].sort((a, b) => {
|
|
const aRank = rank.get(a.library.id)
|
|
const bRank = rank.get(b.library.id)
|
|
const aPinned = aRank !== undefined
|
|
const bPinned = bRank !== undefined
|
|
if (aPinned !== bPinned) return aPinned ? -1 : 1
|
|
if (aPinned && bPinned) return aRank - bRank
|
|
return 0
|
|
})
|
|
}
|