chore: update package dependencies and improve documentation

- Updated `vite` to the beta version across multiple applications for enhanced features and performance.
- Removed deprecated dependencies such as `@clack/prompts`, `consola`, and `opentype.js` from `devDependencies`.
- Added new storage provider documentation in `routes.json` and updated last modified dates for existing entries.
- Refactored route imports in `routes.ts` to streamline the structure and improve readability.
- Enhanced the Table of Contents (TOC) data structure for better organization and accessibility.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei
2025-12-05 15:59:07 +08:00
parent f1738ae749
commit 51df233429
19 changed files with 409 additions and 267 deletions

View File

@@ -34,6 +34,7 @@
"@types/busboy": "1.5.4",
"better-auth": "1.4.5",
"busboy": "1.6.0",
"consola": "3.4.2",
"drizzle-orm": "^0.45.0",
"ejs": "3.1.10",
"hono": "4.10.7",
@@ -54,7 +55,7 @@
"@types/pg": "8.15.6",
"nodemon": "3.1.11",
"unplugin-swc": "1.5.9",
"vite": "7.2.6",
"vite": "8.0.0-beta.0",
"vite-bundle-analyzer": "1.2.3",
"vite-node": "5.2.0",
"vite-tsconfig-paths": "5.1.4",

View File

@@ -93,7 +93,7 @@
"tailwindcss-safe-area": "catalog:",
"tw-animate-css": "1.4.0",
"typescript": "5.9.3",
"vite": "7.2.6",
"vite": "8.0.0-beta.0",
"vite-plugin-checker": "0.11.0",
"vite-plugin-route-builder": "0.4.1",
"vite-tsconfig-paths": "5.1.4"

View File

@@ -1,6 +1,6 @@
import { Button, Prompt } from '@afilmory/ui'
import { clsxm } from '@afilmory/utils'
import { DynamicIcon } from 'lucide-react/dynamic'
import { DatabaseIcon, RadiationIcon, TriangleAlertIcon } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
@@ -220,8 +220,8 @@ export function DataManagementPanel() {
<LinearBorderPanel className="bg-background-secondary/40 p-4 sm:p-6">
<div className="flex flex-col gap-4 sm:gap-6 lg:flex-row lg:items-center lg:justify-between">
<div className="space-y-3 sm:space-y-4">
<span className="shape-squircle inline-flex items-center gap-2 bg-accent/10 px-2.5 sm:px-3 py-1 text-[11px] sm:text-xs font-medium text-accent">
<DynamicIcon name="database" className="h-3.5 sm:h-4 w-3.5 sm:w-4" />
<span className="inline-flex items-center gap-2 text-sm sm:text-xs font-semibold text-accent">
<DatabaseIcon className="h-3.5 sm:h-4 w-3.5 sm:w-4" />
{t(dataManagementKeys.summary.badge)}
</span>
<div className="space-y-1.5 sm:space-y-2">
@@ -260,7 +260,7 @@ export function DataManagementPanel() {
<div className="flex flex-col gap-3 sm:gap-4 md:flex-row md:items-center md:justify-between">
<div className="space-y-1.5 sm:space-y-2">
<div className="flex items-center gap-1.5 sm:gap-2 text-red">
<DynamicIcon name="triangle-alert" className="h-3.5 sm:h-4 w-3.5 sm:w-4" />
<TriangleAlertIcon className="h-3.5 sm:h-4 w-3.5 sm:w-4" />
<span className="text-xs sm:text-sm font-semibold">{t(dataManagementKeys.truncate.badge)}</span>
</div>
<div>
@@ -287,7 +287,7 @@ export function DataManagementPanel() {
<div className="flex flex-col gap-3 sm:gap-4 md:flex-row md:items-center md:justify-between">
<div className="space-y-1.5 sm:space-y-2">
<div className="flex items-center gap-1.5 sm:gap-2 text-red">
<DynamicIcon name="radiation" className="h-3.5 sm:h-4 w-3.5 sm:w-4" />
<RadiationIcon className="h-3.5 sm:h-4 w-3.5 sm:w-4" />
<span className="text-xs sm:text-sm font-semibold">{t(dataManagementKeys.delete.badge)}</span>
</div>
<div className="space-y-1">

View File

@@ -1,6 +1,6 @@
import { Button, Modal } from '@afilmory/ui'
import { clsxm } from '@afilmory/utils'
import { DynamicIcon } from 'lucide-react/dynamic'
import { CheckSquare, Square, Tags, Trash2, X } from 'lucide-react'
import type { ChangeEventHandler } from 'react'
import { useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next'
@@ -152,7 +152,7 @@ export function PhotoLibraryActionBar() {
onClick={handleEditSelectedTags}
className="flex items-center gap-1 text-text-secondary hover:text-text"
>
<DynamicIcon name="tags" className="h-3.5 w-3.5" />
<Tags className="h-3.5 w-3.5" />
{t(photoLibraryActionKeys.editTags)}
</Button>
<Button
@@ -163,11 +163,11 @@ export function PhotoLibraryActionBar() {
onClick={deleteSelected}
className="flex items-center gap-1 text-rose-400 hover:text-rose-300"
>
<DynamicIcon name="trash-2" className="h-3.5 w-3.5" />
<Trash2 className="h-3.5 w-3.5" />
{t(photoLibraryActionKeys.delete)}
</Button>
<Button type="button" className="gap-1" variant="ghost" size="sm" onClick={clearSelection}>
<DynamicIcon name="x" className="h-3.5 w-3.5" />
<X className="h-3.5 w-3.5" />
{t(photoLibraryActionKeys.clear)}
</Button>
</div>
@@ -179,7 +179,7 @@ export function PhotoLibraryActionBar() {
onClick={selectAll}
className="flex items-center gap-1 text-text-secondary hover:text-text"
>
<DynamicIcon name={canSelectAll ? 'square' : 'check-square'} className="size-4" />
{canSelectAll ? <Square className="size-4" /> : <CheckSquare className="size-4" />}
{selectAllLabel}
</Button>
</div>

View File

@@ -10,7 +10,22 @@ import {
} from '@afilmory/ui'
import { clsxm } from '@afilmory/utils'
import { useAtomValue } from 'jotai'
import { DynamicIcon } from 'lucide-react/dynamic'
import {
ArrowDown,
ArrowUp,
Camera,
Check,
ChevronDown,
Clock,
ExternalLink,
HardDrive,
Info,
MoreHorizontal,
Square,
Tags,
Trash2,
Upload,
} from 'lucide-react'
import type { ReactNode } from 'react'
import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -31,15 +46,15 @@ import type { DeleteAssetOptions } from './types'
type PhotoLibrarySortBy = 'uploadedAt' | 'capturedAt'
type PhotoLibrarySortOrder = 'desc' | 'asc'
const SORT_BY_OPTIONS: { value: PhotoLibrarySortBy; labelKey: I18nKeys; icon: string }[] = [
{ value: 'uploadedAt', labelKey: 'photos.library.sort.by-uploaded', icon: 'upload' },
{ value: 'capturedAt', labelKey: 'photos.library.sort.by-captured', icon: 'camera' },
]
const SORT_BY_OPTIONS = [
{ value: 'uploadedAt', labelKey: 'photos.library.sort.by-uploaded', Icon: Upload },
{ value: 'capturedAt', labelKey: 'photos.library.sort.by-captured', Icon: Camera },
] as const
const SORT_ORDER_OPTIONS: { value: PhotoLibrarySortOrder; labelKey: I18nKeys; icon: string }[] = [
{ value: 'desc', labelKey: 'photos.library.sort.order-desc', icon: 'arrow-down' },
{ value: 'asc', labelKey: 'photos.library.sort.order-asc', icon: 'arrow-up' },
]
const SORT_ORDER_OPTIONS = [
{ value: 'desc', labelKey: 'photos.library.sort.order-desc', Icon: ArrowDown },
{ value: 'asc', labelKey: 'photos.library.sort.order-asc', Icon: ArrowUp },
] as const
const photoLibraryGridKeys = {
card: {
@@ -185,7 +200,7 @@ function PhotoGridItem({
isSelected ? 'bg-accent text-white' : 'hover:bg-white/10',
)}
>
<DynamicIcon name={isSelected ? 'check' : 'square'} className="mr-1 h-3 w-3" />
{isSelected ? <Check className="mr-1 h-3 w-3" /> : <Square className="mr-1 h-3 w-3" />}
<span>{isSelected ? t(photoLibraryGridKeys.card.selected) : t(photoLibraryGridKeys.card.select)}</span>
</div>
</div>
@@ -193,15 +208,15 @@ function PhotoGridItem({
<div className="flex items-end justify-between gap-3 p-3">
<div className="flex flex-col gap-1.5 min-w-0 flex-1">
<div className="flex items-center gap-1.5 text-[10px] text-white/80">
<DynamicIcon name="camera" className="h-3 w-3 shrink-0 text-white/60" />
<Camera className="h-3 w-3 shrink-0 text-white/60" />
<span className="truncate">{deviceLabel}</span>
</div>
<div className="flex items-center gap-1.5 text-[10px] text-white/80">
<DynamicIcon name="clock" className="h-3 w-3 shrink-0 text-white/60" />
<Clock className="h-3 w-3 shrink-0 text-white/60" />
<span className="truncate">{updatedAtLabel}</span>
</div>
<div className="flex items-center gap-1.5 text-[10px] text-white/80">
<DynamicIcon name="hard-drive" className="h-3 w-3 shrink-0 text-white/60" />
<HardDrive className="h-3 w-3 shrink-0 text-white/60" />
<span className="truncate">{fileSizeLabel}</span>
</div>
</div>
@@ -213,7 +228,7 @@ function PhotoGridItem({
className="bg-black/40 text-white hover:bg-black/60 h-7 px-2.5"
onClick={() => onOpenAsset(asset)}
>
<DynamicIcon name="external-link" className="h-3.5 w-3.5" />
<ExternalLink className="h-3.5 w-3.5" />
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
@@ -223,26 +238,19 @@ function PhotoGridItem({
size="xs"
className="bg-black/40 text-white hover:bg-black/60 h-7 px-2.5"
>
<DynamicIcon name="more-horizontal" className="h-3.5 w-3.5" />
<MoreHorizontal className="h-3.5 w-3.5" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="min-w-[140px]">
<DropdownMenuItem
icon={<DynamicIcon name="tags" className="size-4" />}
onSelect={() => onEditTags(asset)}
>
<DropdownMenuItem icon={<Tags className="size-4" />} onSelect={() => onEditTags(asset)}>
{t('photos.library.card.edit-tags')}
</DropdownMenuItem>
<DropdownMenuItem
icon={<DynamicIcon name="info" className="size-4" />}
disabled={!manifest}
onSelect={handleViewExif}
>
<DropdownMenuItem icon={<Info className="size-4" />} disabled={!manifest} onSelect={handleViewExif}>
{t('photos.library.card.view-exif')}
</DropdownMenuItem>
<div className="h-[0.5px] bg-border my-1" />
<DropdownMenuItem
icon={<DynamicIcon name="trash-2" className="size-4" />}
icon={<Trash2 className="size-4" />}
disabled={isDeleting}
onSelect={handleDelete}
className="text-red focus:text-red focus:bg-red/10"
@@ -354,9 +362,9 @@ export function PhotoLibraryGrid() {
size="sm"
className="hover:bg-background-secondary/70 flex items-center gap-1.5 rounded-full border px-3 h-8 text-text"
>
<DynamicIcon name={currentSortBy.icon as any} className="size-4" />
<currentSortBy.Icon className="size-4" />
<span className="font-medium">{t(currentSortBy.labelKey)}</span>
<DynamicIcon name="chevron-down" className="h-3 w-3 text-text-tertiary" />
<ChevronDown className="h-3 w-3 text-text-tertiary" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="min-w-[180px]">
@@ -364,7 +372,7 @@ export function PhotoLibraryGrid() {
<DropdownMenuItem
key={option.value}
active={option.value === sortBy}
icon={<DynamicIcon name={option.icon as any} className="size-4" />}
icon={<option.Icon className="size-4" />}
onSelect={() => setSortBy(option.value)}
>
{t(option.labelKey)}
@@ -381,9 +389,9 @@ export function PhotoLibraryGrid() {
size="sm"
className="hover:bg-background-secondary/70 flex items-center gap-1.5 rounded-full border px-3 h-8 text-text"
>
<DynamicIcon name={currentSortOrder.icon as any} className="size-4" />
<currentSortOrder.Icon className="size-4" />
<span className="font-medium">{t(currentSortOrder.labelKey)}</span>
<DynamicIcon name="chevron-down" className="h-3 w-3 text-text-tertiary" />
<ChevronDown className="h-3 w-3 text-text-tertiary" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="min-w-[180px]">
@@ -391,7 +399,7 @@ export function PhotoLibraryGrid() {
<DropdownMenuItem
key={option.value}
active={option.value === sortOrder}
icon={<DynamicIcon name={option.icon as any} className="size-4" />}
icon={<option.Icon className="size-4" />}
onSelect={() => setSortOrder(option.value)}
>
{t(option.labelKey)}

View File

@@ -1,5 +1,5 @@
import { Button, Modal } from '@afilmory/ui'
import { DynamicIcon } from 'lucide-react/dynamic'
import { HardDrive } from 'lucide-react'
import { m } from 'motion/react'
import { useTranslation } from 'react-i18next'
@@ -61,7 +61,7 @@ export function ManagedStorageEntryCard({
<div className="relative">
<div className="bg-accent/15 inline-flex h-12 w-12 items-center justify-center rounded-lg">
<DynamicIcon name="hard-drive" className="h-6 w-6 text-accent" />
<HardDrive className="h-6 w-6 text-accent" />
</div>
</div>

View File

@@ -1,6 +1,20 @@
import { Button } from '@afilmory/ui'
import { clsxm } from '@afilmory/utils'
import { DynamicIcon } from 'lucide-react/dynamic'
import type {LucideIcon} from 'lucide-react';
import {
Check,
CheckCircle,
Cloud,
CloudDrizzle,
CloudSnow,
Database,
Folder,
Github,
Image,
Pencil,
Server,
XCircle
} from 'lucide-react'
import type { FC } from 'react'
import { useTranslation } from 'react-i18next'
@@ -10,48 +24,48 @@ import type { StorageProvider } from '../types'
const providerTypeConfig: Record<
string,
{
icon: string
Icon: LucideIcon
color: string
bgColor: string
}
> = {
s3: {
icon: 'database',
Icon: Database,
color: 'text-orange-500',
bgColor: 'bg-orange-500/10',
},
oss: {
icon: 'cloud-drizzle',
Icon: CloudDrizzle,
color: 'text-emerald-500',
bgColor: 'bg-emerald-500/10',
},
cos: {
icon: 'cloud-snow',
Icon: CloudSnow,
color: 'text-cyan-500',
bgColor: 'bg-cyan-500/10',
},
b2: {
icon: 'cloud',
Icon: Cloud,
color: 'text-sky-500',
bgColor: 'bg-sky-500/10',
},
github: {
icon: 'github',
Icon: Github,
color: 'text-purple-500',
bgColor: 'bg-purple-500/10',
},
local: {
icon: 'folder',
Icon: Folder,
color: 'text-blue-500',
bgColor: 'bg-blue-500/10',
},
minio: {
icon: 'server',
Icon: Server,
color: 'text-red-500',
bgColor: 'bg-red-500/10',
},
eagle: {
icon: 'image',
Icon: Image,
color: 'text-amber-500',
bgColor: 'bg-amber-500/10',
},
@@ -110,7 +124,7 @@ export const ProviderCard: FC<ProviderCardProps> = ({ provider, isActive, onEdit
{isActive && (
<div className="absolute top-3 right-3">
<span className="bg-accent inline-flex items-center gap-1 rounded px-2 py-0.5 text-[10px] font-semibold tracking-wide text-white uppercase">
<DynamicIcon name="check-circle" className="h-3 w-3" />
<CheckCircle className="h-3 w-3" />
{t(storageProvidersI18nKeys.card.active)}
</span>
</div>
@@ -119,7 +133,7 @@ export const ProviderCard: FC<ProviderCardProps> = ({ provider, isActive, onEdit
{/* Provider Icon */}
<div className="relative">
<div className={clsxm('inline-flex h-12 w-12 items-center justify-center rounded-lg', config.bgColor)}>
<DynamicIcon name={config.icon as any} className={clsxm('h-6 w-6', config.color)} />
<config.Icon className={clsxm('h-6 w-6', config.color)} />
</div>
</div>
@@ -142,17 +156,17 @@ export const ProviderCard: FC<ProviderCardProps> = ({ provider, isActive, onEdit
className="text-text-secondary hover:text-text"
onClick={onToggleActive}
>
<DynamicIcon name="x-circle" className="mr-1 h-3.5 w-3.5" />
<XCircle className="mr-1 h-3.5 w-3.5" />
<span>{t(storageProvidersI18nKeys.card.makeInactive)}</span>
</Button>
) : (
<Button type="button" variant="ghost" size="sm" onClick={onToggleActive}>
<DynamicIcon name="check" className="h-3.5 w-3.5 mr-1" />
<Check className="h-3.5 w-3.5 mr-1" />
<span>{t(storageProvidersI18nKeys.card.makeActive)}</span>
</Button>
)}
<Button type="button" variant="ghost" size="sm" onClick={onEdit}>
<DynamicIcon name="pencil" className="mr-1 h-3.5 w-3.5" />
<Pencil className="mr-1 h-3.5 w-3.5" />
<span>{t(storageProvidersI18nKeys.card.edit)}</span>
</Button>
</div>

View File

@@ -14,7 +14,7 @@ import {
Textarea,
} from '@afilmory/ui'
import { clsxm, Spring } from '@afilmory/utils'
import { DynamicIcon } from 'lucide-react/dynamic'
import { Edit, Plus, PlusCircle, Save } from 'lucide-react'
import { m } from 'motion/react'
import { nanoid } from 'nanoid'
import { useEffect, useMemo, useState } from 'react'
@@ -106,7 +106,7 @@ export function ProviderEditModal({
isNewProvider ? 'bg-accent/10 text-accent' : 'bg-fill text-text',
)}
>
<DynamicIcon name={isNewProvider ? 'plus-circle' : 'edit'} className="size-5" />
{isNewProvider ? <PlusCircle className="size-5" /> : <Edit className="size-5" />}
</div>
<div className="flex-1 space-y-1">
<h2 className="text-text text-xl font-semibold">
@@ -248,7 +248,7 @@ export function ProviderEditModal({
{t(storageProvidersI18nKeys.actions.cancel)}
</Button>
<Button type="button" onClick={handleSave} variant="primary" size="sm">
<DynamicIcon name="plus" className="mr-2 h-3.5 w-3.5" />
<Plus className="mr-2 h-3.5 w-3.5" />
<span>{t(storageProvidersI18nKeys.actions.create)}</span>
</Button>
</div>
@@ -256,7 +256,7 @@ export function ProviderEditModal({
// Edit mode: Delete + cancel + set active + save
<div className="flex items-center justify-end gap-3">
<Button type="button" onClick={handleSave} disabled={!isDirty} variant="primary" size="sm">
<DynamicIcon name="save" className="mr-2 h-3.5 w-3.5" />
<Save className="mr-2 h-3.5 w-3.5" />
<span>{t(storageProvidersI18nKeys.actions.save)}</span>
</Button>
</div>
@@ -273,3 +273,11 @@ ProviderEditModal.contentProps = {
maxHeight: '90vh',
},
}
// Configure modal content
ProviderEditModal.contentClassName = 'max-w-2xl w-[95vw] max-h-[90vh] p-0'
ProviderEditModal.contentProps = {
style: {
maxHeight: '90vh',
},
}

View File

@@ -1,6 +1,6 @@
import { Button, Modal, Prompt, Switch } from '@afilmory/ui'
import { Spring } from '@afilmory/utils'
import { DynamicIcon } from 'lucide-react/dynamic'
import { ShieldCheck } from 'lucide-react'
import { m } from 'motion/react'
import { startTransition, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -356,7 +356,7 @@ export function StorageProvidersManager() {
<div className="flex items-start gap-3 sm:gap-4">
<div className="shrink-0">
<div className="bg-accent/10 inline-flex h-8 w-8 items-center justify-center rounded-lg sm:h-10 sm:w-10">
<DynamicIcon name="shield-check" className="h-4 w-4 text-accent sm:h-5 sm:w-5" />
<ShieldCheck className="h-4 w-4 text-accent sm:h-5 sm:w-5" />
</div>
</div>
<div className="flex-1 space-y-1.5 sm:space-y-2">

View File

@@ -282,11 +282,7 @@ function PlanCard({
disabled={!canCheckout || checkoutLoading}
onClick={handleCheckout}
>
{checkoutLoading
? t(planI18nKeys.checkoutLoading)
: canCheckout
? t(planI18nKeys.checkoutUpgrade)
: t(planI18nKeys.checkoutComingSoon)}
{checkoutLoading ? t(planI18nKeys.checkoutLoading) : t(planI18nKeys.checkoutUpgrade)}
</Button>
)}

View File

@@ -19,7 +19,7 @@
"@types/node": "^24.10.1",
"nodemon": "3.1.11",
"typescript": "catalog:",
"vite": "7.2.6",
"vite": "8.0.0-beta.0",
"vite-node": "5.2.0"
}
}