refactor: migrate icon set from Lucide to Tabler Icons

This commit is contained in:
Linus Rath
2026-09-19 13:32:41 +02:00
parent 5fb29a1174
commit d7548a4bab
166 changed files with 1005 additions and 283 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ import { useTranslations } from "next-intl";
import { useAuthStore } from "@/stores/auth-store";
import { apiFetch, getPathPrefix, toRouterPath } from "@/lib/browser-navigation";
import { buildSettingsPath } from "@/lib/deep-links";
import { Loader2, AlertCircle } from "lucide-react";
import { Loader2, AlertCircle } from "@/components/icons";
import { Button } from "@/components/ui/button";
import { useParams } from "next/navigation";
+1 -1
View File
@@ -2,7 +2,7 @@
import { useEffect } from "react";
import { useTranslations } from "next-intl";
import { AlertCircle, RefreshCw, Home } from "lucide-react";
import { AlertCircle, RefreshCw, Home } from "@/components/icons";
import { Button } from "@/components/ui/button";
import { useRouter } from "@/i18n/navigation";
+1 -1
View File
@@ -14,7 +14,7 @@ import { useConfig } from "@/hooks/use-config";
import { useMenuNavigation } from "@/hooks/use-menu-navigation";
import { apiFetch, getPathPrefix, toRouterPath, withBasePath } from "@/lib/browser-navigation";
import { cn } from "@/lib/utils";
import { AlertCircle, Loader2, X, Info, Eye, EyeOff, LogIn, Sun, Moon, Monitor, Check, Shield, Play, Copy } from "lucide-react";
import { AlertCircle, Loader2, X, Info, Eye, EyeOff, LogIn, Sun, Moon, Monitor, Check, Shield, Play, Copy } from "@/components/icons";
import { type OAuthMetadata } from "@/lib/oauth/discovery";
import { generateCodeVerifier, generateCodeChallenge, generateState } from "@/lib/oauth/pkce";
import { useUpdateStore, selectBanner } from "@/stores/update-store";
@@ -32,7 +32,7 @@ import {
PLUGIN_OAUTH_STORAGE_KEY,
type OAuthCallbackPayload,
} from "@/lib/plugin-oauth";
import { CheckCircle2, XCircle } from "lucide-react";
import { CheckCircle2, XCircle } from "@/components/icons";
import { useTranslations } from "next-intl";
import { useSearchParams } from "next/navigation";
import { Suspense, useEffect, useState } from "react";
@@ -1,7 +1,7 @@
'use client';
import { useEffect, useRef, useState } from 'react';
import { Plus, Trash2, RotateCcw, ChevronDown, ChevronRight } from 'lucide-react';
import { Plus, Trash2, RotateCcw, ChevronDown, ChevronRight } from '@/components/icons';
import type { JmapServerEntry } from '@/lib/admin/jmap-servers';
interface Props {
+1 -1
View File
@@ -1,7 +1,7 @@
'use client';
import { useEffect, useState } from 'react';
import { Save, Loader2, RotateCcw, Sparkles } from 'lucide-react';
import { Save, Loader2, RotateCcw, Sparkles } from '@/components/icons';
import { apiFetch } from '@/lib/browser-navigation';
interface ConfigEntry {
+1 -1
View File
@@ -1,7 +1,7 @@
'use client';
import { useEffect, useMemo, useRef, useState } from 'react';
import { Save, Loader2, RotateCcw, ImageIcon, Upload, Trash2, Globe, Plus, X } from 'lucide-react';
import { Save, Loader2, RotateCcw, ImageIcon, Upload, Trash2, Globe, Plus, X } from '@/components/icons';
import { apiFetch, withBasePath } from '@/lib/browser-navigation';
import {
BRANDING_OVERRIDE_KEYS,
+1 -1
View File
@@ -1,7 +1,7 @@
'use client';
import { useEffect, useState } from 'react';
import { AlertTriangle } from 'lucide-react';
import { AlertTriangle } from '@/components/icons';
import { SettingsSection, SettingItem, ToggleSwitch } from '@/components/settings/settings-section';
import type { AuditEntry } from '@/lib/admin/types';
import { apiFetch } from '@/lib/browser-navigation';
+1 -1
View File
@@ -1,7 +1,7 @@
'use client';
import { useEffect, useState, useCallback } from 'react';
import { RefreshCw } from 'lucide-react';
import { RefreshCw } from '@/components/icons';
import type { AuditEntry } from '@/lib/admin/types';
import { apiFetch } from '@/lib/browser-navigation';
+1 -1
View File
@@ -2,7 +2,7 @@
import { useEffect, useState, useCallback } from 'react';
import Link from 'next/link';
import { Search, Download, Check, Loader2, Store, Puzzle, SwatchBook, Star, Eye, AlertTriangle, ArrowUpCircle } from 'lucide-react';
import { Search, Download, Check, Loader2, Store, Puzzle, SwatchBook, Star, Eye, AlertTriangle, ArrowUpCircle } from '@/components/icons';
import { apiFetch } from '@/lib/browser-navigation';
import { compareVersions, isVersionSatisfied } from '@/lib/version-compare';
@@ -1,7 +1,7 @@
'use client';
import { useEffect, useState } from 'react';
import { Puzzle, ArrowLeft, Loader2, Eye, EyeOff } from 'lucide-react';
import { Puzzle, ArrowLeft, Loader2, Eye, EyeOff } from '@/components/icons';
import { apiFetch } from '@/lib/browser-navigation';
import { usePluginSlotOffers } from '@/hooks/use-plugin-slot-offers';
import { PluginIframeSlot } from '@/components/plugins/plugin-iframe-slot';
+1 -1
View File
@@ -1,7 +1,7 @@
'use client';
import { useEffect, useState, useRef } from 'react';
import { Upload, Trash2, Power, PowerOff, AlertTriangle, Loader2, Package, Save, Shield, Lock, LockOpen, Settings } from 'lucide-react';
import { Upload, Trash2, Power, PowerOff, AlertTriangle, Loader2, Package, Save, Shield, Lock, LockOpen, Settings } from '@/components/icons';
import type { SettingsPolicy } from '@/lib/admin/types';
import { DEFAULT_POLICY } from '@/lib/admin/types';
import { apiFetch } from '@/lib/browser-navigation';
+5 -5
View File
@@ -1,8 +1,8 @@
'use client';
import { useEffect, useState } from 'react';
import { Save, Loader2, Lock, Plus, Trash2, ArrowUp, ArrowDown } from 'lucide-react';
import { icons as lucideIcons, type LucideIcon } from 'lucide-react';
import { Save, Loader2, Lock, Plus, Trash2, ArrowUp, ArrowDown } from '@/components/icons';
import { iconForName } from '@/components/icons';
import type { SettingsPolicy, FeatureGates, PushRelayOption, AdminSidebarApp } from '@/lib/admin/types';
import { DEFAULT_FEATURE_GATES, DEFAULT_POLICY } from '@/lib/admin/types';
import { apiFetch } from '@/lib/browser-navigation';
@@ -455,7 +455,7 @@ export function PolicyTab() {
)}
{defaultSidebarApps.map((app, index) => {
const AppIcon = lucideIcons[app.icon as keyof typeof lucideIcons] as LucideIcon | undefined;
const AppIcon = iconForName(app.icon);
const urlInvalid = app.url.trim().length > 0 && !isValidDefaultAppUrl(app.url);
return (
<div key={app.id} className="rounded-md border border-border bg-muted/20 p-3 space-y-2">
@@ -493,7 +493,7 @@ export function PolicyTab() {
<div className="sm:col-span-3">
<label className="block text-[11px] font-medium text-muted-foreground mb-1">
Icon (
<a href="https://lucide.dev/icons" target="_blank" rel="noopener noreferrer" className="underline hover:text-foreground">Lucide</a>
<a href="https://tabler.io/icons" target="_blank" rel="noopener noreferrer" className="underline hover:text-foreground">Tabler</a>
)
</label>
<div className="flex items-center gap-2">
@@ -504,7 +504,7 @@ export function PolicyTab() {
maxLength={64}
value={app.icon}
onChange={(e) => updateDefaultApp(index, { icon: e.target.value })}
placeholder="Globe"
placeholder="world"
className="h-8 w-full rounded-md border border-input bg-background px-2 text-sm text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
/>
<span
+1 -1
View File
@@ -1,7 +1,7 @@
'use client';
import { useEffect, useState } from 'react';
import { Save, RotateCcw, Loader2 } from 'lucide-react';
import { Save, RotateCcw, Loader2 } from '@/components/icons';
import { apiFetch } from '@/lib/browser-navigation';
import { JmapServersSection } from './_jmap-servers-section';
import type { JmapServerEntry } from '@/lib/admin/jmap-servers';
+1 -1
View File
@@ -1,7 +1,7 @@
'use client';
import { useEffect, useState } from 'react';
import { Loader2, Send, Save, CheckCircle2, XCircle, ExternalLink } from 'lucide-react';
import { Loader2, Send, Save, CheckCircle2, XCircle, ExternalLink } from '@/components/icons';
import { apiFetch } from '@/lib/browser-navigation';
interface TelemetryStatus {
+1 -1
View File
@@ -1,7 +1,7 @@
'use client';
import { useEffect, useState, useRef } from 'react';
import { Upload, Trash2, Power, PowerOff, Loader2, Palette, Save, Shield, Lock, LockOpen } from 'lucide-react';
import { Upload, Trash2, Power, PowerOff, Loader2, Palette, Save, Shield, Lock, LockOpen } from '@/components/icons';
import type { SettingsPolicy } from '@/lib/admin/types';
import { DEFAULT_POLICY, DEFAULT_THEME_POLICY } from '@/lib/admin/types';
import { apiFetch } from '@/lib/browser-navigation';
+1 -1
View File
@@ -8,7 +8,7 @@ import {
AlertTriangle,
ShieldAlert,
ExternalLink,
} from 'lucide-react';
} from '@/components/icons';
import { SettingsSection, SettingItem } from '@/components/settings/settings-section';
import { apiFetch } from '@/lib/browser-navigation';
import type { UpdateStatus, UpdateSeverity } from '@/lib/version-check/types';
+1 -1
View File
@@ -2,7 +2,7 @@
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { Lock } from 'lucide-react';
import { Lock } from '@/components/icons';
import { apiFetch } from '@/lib/browser-navigation';
export default function ChangePasswordPage() {
+1 -1
View File
@@ -24,7 +24,7 @@ import {
Store,
Menu,
X,
} from 'lucide-react';
} from '@/components/icons';
import { cn } from '@/lib/utils';
import { useConfig } from '@/hooks/use-config';
import { usePolicyStore } from '@/stores/policy-store';
+1 -1
View File
@@ -2,7 +2,7 @@
import { useState, type FormEvent } from 'react';
import { useRouter } from 'next/navigation';
import { Shield } from 'lucide-react';
import { Shield } from '@/components/icons';
import { useConfig } from '@/hooks/use-config';
import { useThemeStore } from '@/stores/theme-store';
import { apiFetch, withBasePath } from '@/lib/browser-navigation';
+1 -1
View File
@@ -20,7 +20,7 @@ import {
FileCode,
ChevronDown,
ChevronUp,
} from 'lucide-react';
} from '@/components/icons';
import { apiFetch } from '@/lib/browser-navigation';
import { compareVersions, isVersionSatisfied } from '@/lib/version-compare';
+1 -1
View File
@@ -1,7 +1,7 @@
"use client";
import { useEffect } from "react";
import { AlertTriangle, RefreshCw } from "lucide-react";
import { AlertTriangle, RefreshCw } from "@/components/icons";
/**
* Global error boundary for the root layout.
+1 -1
View File
@@ -2,7 +2,7 @@
import { useEffect, useState, type FormEvent, type ReactNode } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { CheckCircle2, AlertTriangle, AlertCircle, Server, ShieldCheck, KeyRound, FileText, Palette, Lock, ShieldAlert } from 'lucide-react';
import { CheckCircle2, AlertTriangle, AlertCircle, Server, ShieldCheck, KeyRound, FileText, Palette, Lock, ShieldAlert } from '@/components/icons';
import { apiFetch, getPathPrefix, withBasePath } from '@/lib/browser-navigation';
type State = 'bootstrap' | 'configured' | 'env-managed';
+11 -11
View File
@@ -887,17 +887,17 @@ option {
/* RTL: mirror directional icons (chevrons/arrows) so prev/next, back/forward,
and panel-collapse affordances point the correct way in right-to-left layouts.
lucide-react emits a `lucide-<name>` class per icon, so we target the
Tabler emits a `tabler-icon-<name>` class per icon, so we target the
directional ones only — vertical chevrons (up/down) are intentionally left. */
[dir="rtl"] .lucide-chevron-left,
[dir="rtl"] .lucide-chevron-right,
[dir="rtl"] .lucide-chevrons-left,
[dir="rtl"] .lucide-chevrons-right,
[dir="rtl"] .lucide-arrow-left,
[dir="rtl"] .lucide-arrow-right,
[dir="rtl"] .lucide-arrow-big-left,
[dir="rtl"] .lucide-arrow-big-right,
[dir="rtl"] .lucide-panel-left,
[dir="rtl"] .lucide-panel-right {
[dir="rtl"] .tabler-icon-chevron-left,
[dir="rtl"] .tabler-icon-chevron-right,
[dir="rtl"] .tabler-icon-chevrons-left,
[dir="rtl"] .tabler-icon-chevrons-right,
[dir="rtl"] .tabler-icon-arrow-left,
[dir="rtl"] .tabler-icon-arrow-right,
[dir="rtl"] .tabler-icon-arrow-big-left,
[dir="rtl"] .tabler-icon-arrow-big-right,
[dir="rtl"] .tabler-icon-layout-sidebar-left,
[dir="rtl"] .tabler-icon-layout-sidebar-right {
transform: scaleX(-1);
}
+3 -3
View File
@@ -9,8 +9,8 @@ import { useConfig } from "@/hooks/use-config";
import { withBasePath } from "@/lib/browser-navigation";
import { cn } from "@/lib/utils";
import { useThemeStore } from "@/stores/theme-store";
import type { LucideIcon } from "lucide-react";
import { Loader2 } from "lucide-react";
import type { AppIcon } from "@/components/icons";
import { Loader2 } from "@/components/icons";
export interface OAuthStatusAction {
label: string;
@@ -19,7 +19,7 @@ export interface OAuthStatusAction {
export interface OAuthStatusCardProps {
/** Icon badge shown below the logo (defaults to a spinner) */
icon?: LucideIcon;
icon?: AppIcon;
/** Tailwind color classes for the icon badge, e.g. "bg-green-500/10 text-green-600" */
iconClassName?: string;
title: string;
+1 -1
View File
@@ -4,7 +4,7 @@ import { useMemo, useRef, useCallback } from "react";
import { useTranslations } from "next-intl";
import { useDisplayDateFormatter } from "@/hooks/use-display-date-formatter";
import { format, isTomorrow, startOfDay } from "date-fns";
import { MapPin, Users } from "lucide-react";
import { MapPin, Users } from "@/components/icons";
import { cn } from "@/lib/utils";
import { getEventColor } from "./event-card";
import { getEventDayBounds, getEventEndDate, getEventStartDate, getPrimaryCalendarId } from "@/lib/calendar-utils";
+1 -1
View File
@@ -3,7 +3,7 @@
import { useState, useEffect, useCallback, useRef, useMemo, type TouchEvent as ReactTouchEvent } from "react";
import { useRouter } from "@/i18n/navigation";
import { useTranslations } from "next-intl";
import { Plus } from "lucide-react";
import { Plus } from "@/components/icons";
import {
addMonths, subMonths, addWeeks, subWeeks, addDays, subDays,
format, parseISO,
+1 -1
View File
@@ -5,7 +5,7 @@ import { useTranslations } from "next-intl";
import { useDisplayDateFormatter } from "@/hooks/use-display-date-formatter";
import { format, isSameDay, parseISO, eachDayOfInterval, differenceInCalendarDays } from "date-fns";
import { cn } from "@/lib/utils";
import { Check } from "lucide-react";
import { Check } from "@/components/icons";
import { EventCard } from "./event-card";
import { QuickEventInput } from "./quick-event-input";
import { formatSnapTime, getEventDayBounds, getPrimaryCalendarId, isTimedEventFullDayOnDate, layoutOverlappingEvents } from "@/lib/calendar-utils";
+1 -1
View File
@@ -1,7 +1,7 @@
"use client";
import { useTranslations } from "next-intl";
import { Calendar as CalendarIcon, CheckSquare, Layers } from "lucide-react";
import { Calendar as CalendarIcon, CheckSquare, Layers } from "@/components/icons";
import { cn } from "@/lib/utils";
import type { CalendarComponentType } from "@/lib/jmap/types";
@@ -2,7 +2,7 @@
import { useMemo, useState } from "react";
import { useTranslations } from "next-intl";
import { ChevronDown, ChevronRight, Globe, ListTodo, Pencil, RefreshCw, Share2, Star, Trash2, Cake, User, Users, Plus, Eraser, Palette, Shuffle } from "lucide-react";
import { ChevronDown, ChevronRight, Globe, ListTodo, Pencil, RefreshCw, Share2, Star, Trash2, Cake, User, Users, Plus, Eraser, Palette, Shuffle } from "@/components/icons";
import { cn, formatDateTime } from "@/lib/utils";
import type { Calendar } from "@/lib/jmap/types";
import { CalendarColorPicker } from "@/components/settings/calendar-management-settings";
+1 -1
View File
@@ -3,7 +3,7 @@
import { useState, useRef, useEffect } from "react";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { ChevronLeft, ChevronRight, Plus, Upload, CalendarDays, Globe, ChevronDown, ArrowLeft, Menu } from "lucide-react";
import { ChevronLeft, ChevronRight, Plus, Upload, CalendarDays, Globe, ChevronDown, ArrowLeft, Menu } from "@/components/icons";
import { startOfWeek } from "date-fns";
import { cn } from "@/lib/utils";
import type { CalendarViewMode } from "@/stores/calendar-store";
+1 -1
View File
@@ -7,7 +7,7 @@ import {
startOfWeek, format, isSameDay, parseISO, eachDayOfInterval, differenceInCalendarDays,
} from "date-fns";
import { cn } from "@/lib/utils";
import { Check } from "lucide-react";
import { Check } from "@/components/icons";
import { EventCard } from "./event-card";
import { QuickEventInput } from "./quick-event-input";
import { buildTimedFullDayWeekSegments, buildWeekSegmentsRaw, formatSnapTime, getEventDayBounds, getPrimaryCalendarId, isTimedEventFullDayOnDate, layoutOverlappingEvents, packWeekSegments } from "@/lib/calendar-utils";
@@ -3,7 +3,7 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { X, Loader2, Calendar as CalendarIcon } from "lucide-react";
import { X, Loader2, Calendar as CalendarIcon } from "@/components/icons";
import type { IJMAPClient } from "@/lib/jmap/client-interface";
import { useCalendarStore } from "@/stores/calendar-store";
import { CalendarColorPicker } from "@/components/settings/calendar-management-settings";
@@ -6,7 +6,7 @@ import {
ContextMenuItem,
ContextMenuSeparator,
} from "@/components/ui/context-menu";
import { Plus, CalendarDays, CheckSquare, Clock } from "lucide-react";
import { Plus, CalendarDays, CheckSquare, Clock } from "@/components/icons";
interface Position {
x: number;
+1 -1
View File
@@ -5,7 +5,7 @@ import { useTranslations } from "next-intl";
import { cn } from "@/lib/utils";
import type { CalendarEvent, Calendar } from "@/lib/jmap/types";
import { format } from "date-fns";
import { Users } from "lucide-react";
import { Users } from "@/components/icons";
import { getParticipantCount } from "@/lib/calendar-participants";
import { getEventEndDate, getEventStartDate } from "@/lib/calendar-utils";
import { useSettingsStore } from "@/stores/settings-store";
+1 -1
View File
@@ -14,7 +14,7 @@ import {
Link as LinkIcon,
CalendarArrowUp as CalendarLinkIcon,
Trash2,
} from "lucide-react";
} from "@/components/icons";
import type { CalendarEvent } from "@/lib/jmap/types";
import { buildCalendarPath } from "@/lib/deep-links";
import { useCopyLink } from "@/hooks/use-copy-link";
+1 -1
View File
@@ -8,7 +8,7 @@ import { LinkifiedText } from "@/components/ui/linkified-text";
import {
X, Clock, MapPin, Video, Users, Repeat, Bell, AlignLeft,
Pencil, Trash2, Copy, Send, Check,
} from "lucide-react";
} from "@/components/icons";
import { format, isSameDay } from "date-fns";
import { cn } from "@/lib/utils";
import type { CalendarEvent, Calendar, CalendarParticipant } from "@/lib/jmap/types";
+1 -1
View File
@@ -5,7 +5,7 @@ import { useTranslations, useLocale } from "next-intl";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { LinkifiedText } from "@/components/ui/linkified-text";
import { X, Trash2, Check, Users, CalendarDays, Copy, Pencil, Clock, MapPin, Video, Repeat, Bell, AlignLeft, Plus } from "lucide-react";
import { X, Trash2, Check, Users, CalendarDays, Copy, Pencil, Clock, MapPin, Video, Repeat, Bell, AlignLeft, Plus } from "@/components/icons";
import { format, parseISO, addHours, addDays, isSameDay } from "date-fns";
import type { CalendarEvent, Calendar, CalendarParticipant, CalendarEventAlert, CalendarRecurrenceRule } from "@/lib/jmap/types";
import { RecurrenceEditor, buildRecurrenceSummary, isSimpleRecurrenceRule } from "./recurrence-editor";
+1 -1
View File
@@ -3,7 +3,7 @@
import { useState, useCallback, useRef, useEffect } from "react";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { X, Upload, Check, Loader2, RefreshCw, Globe } from "lucide-react";
import { X, Upload, Check, Loader2, RefreshCw, Globe } from "@/components/icons";
import { format } from "date-fns";
import type { CalendarEvent, Calendar } from "@/lib/jmap/types";
import type { IJMAPClient } from '@/lib/jmap/client-interface';
@@ -3,7 +3,7 @@
import { useState, useRef, useEffect, useCallback } from "react";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { X, Loader2, Globe } from "lucide-react";
import { X, Loader2, Globe } from "@/components/icons";
import type { IJMAPClient } from '@/lib/jmap/client-interface';
import { useCalendarStore, type ICalSubscription } from "@/stores/calendar-store";
import { CalendarColorPicker } from "@/components/settings/calendar-management-settings";
+1 -1
View File
@@ -2,7 +2,7 @@
import { useState, useMemo, Fragment } from "react";
import { useTranslations } from "next-intl";
import { ChevronLeft, ChevronRight, ChevronDown } from "lucide-react";
import { ChevronLeft, ChevronRight, ChevronDown } from "@/components/icons";
import {
addMonths, subMonths, addYears, subYears, setMonth, setYear,
getISOWeek, getWeek, format,
@@ -2,7 +2,7 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { useTranslations } from "next-intl";
import { Loader2 } from "lucide-react";
import { Loader2 } from "@/components/icons";
import { useAuthStore } from "@/stores/auth-store";
import type { IJMAPClient } from "@/lib/jmap/client-interface";
import type { BusyPeriod, Principal } from "@/lib/jmap/types";
+1 -1
View File
@@ -2,7 +2,7 @@
import { useState, useRef, useCallback, useEffect, forwardRef, useImperativeHandle } from "react";
import { useTranslations } from "next-intl";
import { X } from "lucide-react";
import { X } from "@/components/icons";
import { Input } from "@/components/ui/input";
import { Avatar } from "@/components/ui/avatar";
import { useContactStore } from "@/stores/contact-store";
@@ -4,7 +4,7 @@ import { useState, useId } from "react";
import { useTranslations } from "next-intl";
import { useFocusTrap } from "@/hooks/use-focus-trap";
import { Button } from "@/components/ui/button";
import { Repeat, Trash2 } from "lucide-react";
import { Repeat, Trash2 } from "@/components/icons";
export type RecurrenceEditScope = "this" | "this_and_future" | "all";
+1 -1
View File
@@ -4,7 +4,7 @@ import { useMemo, useCallback, useState } from "react";
import { useTranslations } from "next-intl";
import { format, parseISO, isBefore, isTomorrow } from "date-fns";
import { displayNow, isDisplayToday } from "@/lib/timezone";
import { Check, Flag, CalendarDays, ListTodo, Plus } from "lucide-react";
import { Check, Flag, CalendarDays, ListTodo, Plus } from "@/components/icons";
import { cn } from "@/lib/utils";
import type { CalendarTask, Calendar } from "@/lib/jmap/types";
import type { TaskViewFilter } from "@/stores/task-store";
+1 -1
View File
@@ -4,7 +4,7 @@ import { useState, useEffect, useCallback, useRef } from "react";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { X, Trash2, CalendarDays, Bell, Flag } from "lucide-react";
import { X, Trash2, CalendarDays, Bell, Flag } from "@/components/icons";
import { format, parseISO } from "date-fns";
import type { CalendarTask, Calendar, CalendarEventAlert } from "@/lib/jmap/types";
+1 -1
View File
@@ -1,7 +1,7 @@
"use client";
import { useTranslations } from "next-intl";
import { Plus } from "lucide-react";
import { Plus } from "@/components/icons";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import type { TaskViewFilter } from "@/stores/task-store";
+1 -1
View File
@@ -2,7 +2,7 @@
import { useEffect, useState } from "react";
import { useTranslations, useFormatter } from "next-intl";
import { Loader2 } from "lucide-react";
import { Loader2 } from "@/components/icons";
import { useRouter } from "@/i18n/navigation";
import { useAuthStore } from "@/stores/auth-store";
import { useEmailStore } from "@/stores/email-store";
+1 -1
View File
@@ -18,7 +18,7 @@ import {
Trash2,
Copy,
Printer,
} from "lucide-react";
} from "@/components/icons";
import type { ContactCard } from "@/lib/jmap/types";
import { getContactPrimaryEmail } from "@/stores/contact-store";
import { exportContact } from "./contact-export";
+1 -1
View File
@@ -2,7 +2,7 @@
import { useState, useEffect, useRef } from "react";
import { useTranslations } from "next-intl";
import { Mail, Phone, Building, MapPin, StickyNote, Pencil, Trash2, BookUser, Copy, Send, Globe, Cake, KeyRound, Users, Briefcase, Heart, Languages, Calendar, UserCircle, Download, MoreHorizontal, Printer } from "lucide-react";
import { Mail, Phone, Building, MapPin, StickyNote, Pencil, Trash2, BookUser, Copy, Send, Globe, Cake, KeyRound, Users, Briefcase, Heart, Languages, Calendar, UserCircle, Download, MoreHorizontal, Printer } from "@/components/icons";
import { Avatar } from "@/components/ui/avatar";
import { MailtoLink } from "@/components/ui/mailto-link";
import { Button } from "@/components/ui/button";
+1 -1
View File
@@ -2,7 +2,7 @@
import { useState, useMemo, useCallback, useRef } from "react";
import { useTranslations } from "next-intl";
import { X, Plus, ChevronDown, ChevronRight, User, Building, MapPin, Globe, Cake, Heart, Tag, StickyNote, Mail, Phone, Calendar, UserCircle, Book, Camera, Trash2 } from "lucide-react";
import { X, Plus, ChevronDown, ChevronRight, User, Building, MapPin, Globe, Cake, Heart, Tag, StickyNote, Mail, Phone, Calendar, UserCircle, Book, Camera, Trash2 } from "@/components/icons";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Avatar } from "@/components/ui/avatar";
+1 -1
View File
@@ -1,7 +1,7 @@
"use client";
import { useTranslations } from "next-intl";
import { Users, Pencil, Trash2, UserMinus, Mail } from "lucide-react";
import { Users, Pencil, Trash2, UserMinus, Mail } from "@/components/icons";
import { Avatar } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
+1 -1
View File
@@ -2,7 +2,7 @@
import { useState, useMemo } from "react";
import { useTranslations } from "next-intl";
import { Search, Check, X } from "lucide-react";
import { Search, Check, X } from "@/components/icons";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Avatar } from "@/components/ui/avatar";
+1 -1
View File
@@ -2,7 +2,7 @@
import { useMemo } from "react";
import { useTranslations } from "next-intl";
import { Users, Plus } from "lucide-react";
import { Users, Plus } from "@/components/icons";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import type { ContactCard } from "@/lib/jmap/types";
@@ -2,7 +2,7 @@
import { useState, useRef, useCallback } from "react";
import { useTranslations } from "next-intl";
import { Upload, FileText, AlertTriangle, X, Check } from "lucide-react";
import { Upload, FileText, AlertTriangle, X, Check } from "@/components/icons";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { parseVCard, detectDuplicates } from "@/lib/vcard";
+1 -1
View File
@@ -5,7 +5,7 @@ import { Avatar } from "@/components/ui/avatar";
import { cn } from "@/lib/utils";
import type { ContactCard } from "@/lib/jmap/types";
import { getContactDisplayName, getContactPrimaryEmail } from "@/stores/contact-store";
import { CheckSquare, Square } from "lucide-react";
import { CheckSquare, Square } from "@/components/icons";
import type { Density } from "@/stores/settings-store";
interface ContactListItemProps {
+1 -1
View File
@@ -2,7 +2,7 @@
import { useMemo, useState } from "react";
import { useTranslations, useLocale } from "next-intl";
import { Search, BookUser, Trash2, Users, Download, X, UserPlus, CheckSquare, Square, Filter, Mail, Phone, Image as ImageIcon, RotateCcw, Menu, ArrowDownAZ } from "lucide-react";
import { Search, BookUser, Trash2, Users, Download, X, UserPlus, CheckSquare, Square, Filter, Mail, Phone, Image as ImageIcon, RotateCcw, Menu, ArrowDownAZ } from "@/components/icons";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { ContactListItem } from "./contact-list-item";
+1 -1
View File
@@ -4,7 +4,7 @@ import { Suspense, useState, useEffect, useCallback, useRef, useMemo } from "rea
import { useTranslations } from "next-intl";
import { useSearchParams } from "next/navigation";
import { useRouter } from "@/i18n/navigation";
import { ArrowLeft, Users, AlertTriangle } from "lucide-react";
import { ArrowLeft, Users, AlertTriangle } from "@/components/icons";
import { Button } from "@/components/ui/button";
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
import { useConfirmDialog } from "@/hooks/use-confirm-dialog";
+1 -1
View File
@@ -2,7 +2,7 @@
import { useMemo, useState, useCallback, useEffect, useRef, type DragEvent } from "react";
import { useTranslations } from "next-intl";
import { BookUser, User, Users, Plus, Share2, Book, BookPlus, ChevronRight, ChevronDown, UserPlus, UsersRound, Upload, Tag, Pencil, Trash2, Settings, Mail, Star } from "lucide-react";
import { BookUser, User, Users, Plus, Share2, Book, BookPlus, ChevronRight, ChevronDown, UserPlus, UsersRound, Upload, Tag, Pencil, Trash2, Settings, Mail, Star } from "@/components/icons";
import { useRouter } from "@/i18n/navigation";
import { Button } from "@/components/ui/button";
import { ContextMenu, ContextMenuItem, ContextMenuSeparator, ContextMenuSubMenu } from "@/components/ui/context-menu";
+1 -1
View File
@@ -1,6 +1,6 @@
"use client";
import { FileText, FileSpreadsheet, FileImage, FileArchive, File as FileIcon } from "lucide-react";
import { FileText, FileSpreadsheet, FileImage, FileArchive, File as FileIcon } from "@/components/icons";
import { cn } from "@/lib/utils";
import type { Attachment } from "@/lib/jmap/types";
@@ -17,7 +17,7 @@ import {
AlertCircle,
ChevronDown,
ChevronUp,
} from 'lucide-react';
} from '@/components/icons';
import { useTranslations, useFormatter } from 'next-intl';
import { useRouter } from '@/i18n/navigation';
import { isDocumentRTL } from '@/i18n/direction';
+1 -1
View File
@@ -5,7 +5,7 @@ import { useFocusTrap } from "@/hooks/use-focus-trap";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { X, Paperclip, Send, Save, Check, Loader2, AlertCircle, FileText, BookmarkPlus, CalendarClock, ChevronDown, MailCheck, Search, Users, PackageCheck, LockKeyhole, Type } from "lucide-react";
import { X, Paperclip, Send, Save, Check, Loader2, AlertCircle, FileText, BookmarkPlus, CalendarClock, ChevronDown, MailCheck, Search, Users, PackageCheck, LockKeyhole, Type } from "@/components/icons";
import { cn, formatFileSize, formatDateTime, generateUUID } from "@/lib/utils";
import { debug } from "@/lib/debug";
import { toast } from "@/stores/toast-store";
+1 -1
View File
@@ -34,7 +34,7 @@ import {
Paperclip,
Link as LinkIcon,
MessagesSquare,
} from "lucide-react";
} from "@/components/icons";
import { buildMailPath } from "@/lib/deep-links";
import { useCopyLink } from "@/hooks/use-copy-link";
import { buildMailboxTree, MailboxNode } from "@/lib/utils";
+1 -1
View File
@@ -4,7 +4,7 @@ import { Email } from "@/lib/jmap/types";
import { useSettingsStore } from "@/stores/settings-store";
import type { HoverAction } from "@/stores/settings-store";
import { cn } from "@/lib/utils";
import { Trash2, Star, Mail, MailOpen, Archive, Tag, ShieldAlert, ShieldCheck } from "lucide-react";
import { Trash2, Star, Mail, MailOpen, Archive, Tag, ShieldAlert, ShieldCheck } from "@/components/icons";
import { useTranslations } from "next-intl";
import { useIsMobile } from "@/hooks/use-media-query";
+1 -1
View File
@@ -1,7 +1,7 @@
'use client';
import { useTranslations } from 'next-intl';
import { Mail, Tag } from 'lucide-react';
import { Mail, Tag } from '@/components/icons';
import { cn } from '@/lib/utils';
import type { Email, Identity } from '@/lib/jmap/types';
import { parseSubAddress } from '@/lib/sub-addressing';
+1 -1
View File
@@ -5,7 +5,7 @@ import { ThreadListItem } from "./thread-list-item";
import type { Attachment } from "@/lib/jmap/types";
import { EmailContextMenu } from "./email-context-menu";
import { cn } from "@/lib/utils";
import { Trash2, Mail, MailX, MailOpen, Loader2, SearchX, AlertTriangle, CalendarClock, ShieldCheck } from "lucide-react";
import { Trash2, Mail, MailX, MailOpen, Loader2, SearchX, AlertTriangle, CalendarClock, ShieldCheck } from "@/components/icons";
import { useState, useEffect, useRef, useCallback, useMemo } from "react";
import { Button } from "@/components/ui/button";
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
+1 -1
View File
@@ -82,7 +82,7 @@ import {
Link as LinkIcon,
Maximize2,
Minimize2,
} from "lucide-react";
} from "@/components/icons";
import { useTranslations } from "next-intl";
import { useRouter } from "@/i18n/navigation";
import type { Attachment as PostalMimeAttachment } from 'postal-mime';
+2 -2
View File
@@ -7,7 +7,7 @@
// DEFINITIONS (stores/message-list-tabs-store.ts) - no plugin iframe here.
import { useEffect, useRef } from 'react';
import { icons as lucideIcons, type LucideIcon } from 'lucide-react';
import { iconForName } from '@/components/icons';
import { useMessageListTabsStore } from '@/stores/message-list-tabs-store';
import { useEmailStore } from '@/stores/email-store';
import { useAuthStore } from '@/stores/auth-store';
@@ -75,7 +75,7 @@ export function MessageListTabs() {
>
{tabs.map((tab) => {
const Icon = tab.icon
? (lucideIcons[tab.icon as keyof typeof lucideIcons] as LucideIcon | undefined)
? iconForName(tab.icon)
: undefined;
const unread = tabCounts[tab.id] ?? 0;
const isActive = tab.id === activeTabId;
+1 -1
View File
@@ -1,7 +1,7 @@
'use client';
import { useState } from 'react';
import { MailCheck, Loader2, CheckCircle, X } from 'lucide-react';
import { MailCheck, Loader2, CheckCircle, X } from '@/components/icons';
import { useTranslations } from 'next-intl';
interface ReadReceiptBannerProps {
+1 -1
View File
@@ -2,7 +2,7 @@
import { useState, useRef, useEffect } from "react";
import { createPortal } from "react-dom";
import { Mail, Phone, Building, ExternalLink, Copy, Send, UserPlus } from "lucide-react";
import { Mail, Phone, Building, ExternalLink, Copy, Send, UserPlus } from "@/components/icons";
import { Avatar } from "@/components/ui/avatar";
import { cn } from "@/lib/utils";
import { MailtoLink } from "@/components/ui/mailto-link";
+1 -1
View File
@@ -49,7 +49,7 @@ import {
Trash2,
Rows3,
Columns3,
} from "lucide-react";
} from "@/components/icons";
export interface InlineImageUpload {
src: string;
+1 -1
View File
@@ -1,7 +1,7 @@
"use client";
import type { ComponentProps, MouseEvent } from "react";
import { Check } from "lucide-react";
import { Check } from "@/components/icons";
import { Avatar } from "@/components/ui/avatar";
import { cn } from "@/lib/utils";
+1 -1
View File
@@ -1,7 +1,7 @@
"use client";
import { useTranslations } from "next-intl";
import { X } from "lucide-react";
import { X } from "@/components/icons";
import { cn } from "@/lib/utils";
import { useKeywordFormat } from "@/hooks/use-keyword-format";
import { useShortenedText } from "@/hooks/use-shortened-text";
+1 -1
View File
@@ -2,7 +2,7 @@
import { useMemo, useState } from "react";
import { useTranslations } from "next-intl";
import { Check, Search } from "lucide-react";
import { Check, Search } from "@/components/icons";
import { cn } from "@/lib/utils";
import { useSettingsStore } from "@/stores/settings-store";
import { buildKeywordTree, type KeywordNode } from "@/lib/keyword-nesting";
@@ -31,7 +31,7 @@ import {
FileArchive,
File,
Eye,
} from "lucide-react";
} from "@/components/icons";
import { useTranslations } from "next-intl";
import { useSettingsStore } from "@/stores/settings-store";
import { useContactStore } from "@/stores/contact-store";
+1 -1
View File
@@ -6,7 +6,7 @@ import { formatDate } from "@/lib/utils";
import { Email } from "@/lib/jmap/types";
import { cn } from "@/lib/utils";
import { Avatar } from "@/components/ui/avatar";
import { Paperclip, Star, Circle, CheckSquare, Square, Reply, Forward } from "lucide-react";
import { Paperclip, Star, Circle, CheckSquare, Square, Reply, Forward } from "@/components/icons";
import { useEmailDrag } from "@/hooks/use-email-drag";
import { useLongPress } from "@/hooks/use-long-press";
import { useEmailStore } from "@/stores/email-store";
+4 -4
View File
@@ -7,8 +7,8 @@ import { cn } from "@/lib/utils";
import { AttachmentChips } from "./attachment-chips";
import type { Attachment } from "@/lib/jmap/types";
import { SelectableAvatar } from "@/components/email/selectable-avatar";
import { Paperclip, Star, Pin, Circle, ChevronRight, ChevronDown, Loader2, MessageSquare, CheckSquare, Square, Reply, Forward, CalendarClock, Folder, Archive, Trash2, MailOpen, ShieldAlert } from "lucide-react";
import type { LucideIcon } from "lucide-react";
import { Paperclip, Star, Pin, Circle, ChevronRight, ChevronDown, Loader2, MessageSquare, CheckSquare, Square, Reply, Forward, CalendarClock, Folder, Archive, Trash2, MailOpen, ShieldAlert } from "@/components/icons";
import type { AppIcon } from "@/components/icons";
import { useSettingsStore } from "@/stores/settings-store";
import { useUIStore } from "@/stores/ui-store";
import { useEmailStore } from "@/stores/email-store";
@@ -49,7 +49,7 @@ function UnreadDot({ density, compactAvatar }: { density: string; compactAvatar:
);
}
function StatusIcon({ icon: Icon, label, className }: { icon: LucideIcon; label: string; className: string }) {
function StatusIcon({ icon: Icon, label, className }: { icon: AppIcon; label: string; className: string }) {
return (
<>
<Icon className={className} />
@@ -147,7 +147,7 @@ interface SingleEmailItemProps {
// Visual + behaviour metadata for each mobile swipe action. Keyed by the
// SwipeAction values that map to a row callback ('none' is handled inline).
const SWIPE_ACTION_META: Record<Exclude<SwipeAction, 'none'>, { icon: LucideIcon; bg: string }> = {
const SWIPE_ACTION_META: Record<Exclude<SwipeAction, 'none'>, { icon: AppIcon; bg: string }> = {
archive: { icon: Archive, bg: 'bg-emerald-600' },
delete: { icon: Trash2, bg: 'bg-red-600' },
markRead: { icon: MailOpen, bg: 'bg-sky-600' },
+1 -1
View File
@@ -1,7 +1,7 @@
'use client';
import { useState, useRef, useEffect } from 'react';
import { Loader2, CheckCircle, AlertCircle } from 'lucide-react';
import { Loader2, CheckCircle, AlertCircle } from '@/components/icons';
import { useTranslations } from 'next-intl';
import { isValidUnsubscribeUrl, parseMailtoUrl } from '@/lib/validation';
import { ConfirmDialog } from '@/components/ui/confirm-dialog';
+1 -1
View File
@@ -1,6 +1,6 @@
"use client";
import { AlertCircle, RefreshCw, Inbox, Mail, Settings, FolderOpen } from "lucide-react";
import { AlertCircle, RefreshCw, Inbox, Mail, Settings, FolderOpen } from "@/components/icons";
import { Button } from "@/components/ui/button";
import type { FallbackProps } from "./error-boundary";
+1 -1
View File
@@ -1,7 +1,7 @@
"use client";
import { useTranslations } from "next-intl";
import { Paperclip, Download } from "lucide-react";
import { Paperclip, Download } from "@/components/icons";
import { sanitizeEmailHtmlForIframe } from "@/lib/email-sanitization";
import { getEffectiveTimeZone } from "@/lib/timezone";
+2 -2
View File
@@ -12,7 +12,7 @@ import {
FileArchive, FileSpreadsheet, Presentation, FileCode,
Box, PenTool, Terminal as TerminalIcon, Database, Type as TypeIcon,
Menu, Users, Share2, SquarePen,
} from "lucide-react";
} from "@/components/icons";
import { useIsDesktop } from "@/hooks/use-media-query";
import { Button } from "@/components/ui/button";
import { cn, formatFileSize } from "@/lib/utils";
@@ -1248,7 +1248,7 @@ export function FileBrowser({
// open so they can expand/collapse without dismissing.
const target = e.target as HTMLElement;
const btn = target.closest('button');
if (btn && !btn.querySelector('svg.lucide-chevron-right, svg.lucide-chevron-down')) {
if (btn && !btn.querySelector('svg.tabler-icon-chevron-right, svg.tabler-icon-chevron-down')) {
setNarrowSidebarOpen(false);
}
}}
+1 -1
View File
@@ -2,7 +2,7 @@
import { useState, useEffect, useRef } from "react";
import { useTranslations } from "next-intl";
import { X, Download, Loader2, ExternalLink } from "lucide-react";
import { X, Download, Loader2, ExternalLink } from "@/components/icons";
import { Button } from "@/components/ui/button";
import { getFilePreviewKind, isMimeTypeSafeForInlinePreview } from "@/lib/file-preview";
import dynamic from "next/dynamic";
+1 -1
View File
@@ -2,7 +2,7 @@
import { useCallback, useState } from "react";
import { useTranslations } from "next-intl";
import { Upload, FolderPlus, FilePlus } from "lucide-react";
import { Upload, FolderPlus, FilePlus } from "@/components/icons";
import { Button } from "@/components/ui/button";
import { getDroppedFilesAndFolders } from "@/lib/webdav/drop-utils";
+2 -2
View File
@@ -3,7 +3,7 @@
import { useState, useEffect, useRef, useCallback } from "react";
import { useRouter } from "@/i18n/navigation";
import { useTranslations } from "next-intl";
import { ArrowLeft } from "lucide-react";
import { ArrowLeft } from "@/components/icons";
import { Button } from "@/components/ui/button";
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
import { useConfirmDialog } from "@/hooks/use-confirm-dialog";
@@ -31,7 +31,7 @@ import { useWopiStatus, fileExtension } from "@/hooks/use-wopi-status";
import { loadFilesSettings } from "@/components/files/files-settings-dialog";
import type { FolderLayout } from "@/components/files/files-settings-dialog";
import { AppTopBannerSlot } from "@/components/plugins/app-top-banner-slot";
import { AlertTriangle, Loader2 } from "lucide-react";
import { AlertTriangle, Loader2 } from "@/components/icons";
import { isFilePreviewable } from "@/lib/file-preview";
import { appPath, buildFilesPath, parseFilesPath, type FilesDeepLink } from "@/lib/deep-links";
import { consumePendingDeepLinkEntry, subscribePendingDeepLink } from "@/lib/deep-link-handoff";
+1 -1
View File
@@ -2,7 +2,7 @@
import { useRef, useEffect } from "react";
import { useTranslations } from "next-intl";
import { X } from "lucide-react";
import { X } from "@/components/icons";
import { Button } from "@/components/ui/button";
import { SettingsSection, SettingItem, ToggleSwitch, RadioGroup } from "@/components/settings/settings-section";
+1 -1
View File
@@ -9,7 +9,7 @@ import {
ChevronDown,
Home,
Share2,
} from "lucide-react";
} from "@/components/icons";
import { cn } from "@/lib/utils";
import { useFileStore, type FileResource } from "@/stores/file-store";
+1 -1
View File
@@ -1,7 +1,7 @@
"use client";
import { useState, useEffect, useCallback } from "react";
import { X, Download, ZoomIn, ZoomOut, RotateCw } from "lucide-react";
import { X, Download, ZoomIn, ZoomOut, RotateCw } from "@/components/icons";
import { Button } from "@/components/ui/button";
import { useTranslations } from "next-intl";
+1 -1
View File
@@ -2,7 +2,7 @@
import { useEffect, useRef, useState } from "react";
import { useTranslations } from "next-intl";
import { Loader2, ExternalLink } from "lucide-react";
import { Loader2, ExternalLink } from "@/components/icons";
import { Button } from "@/components/ui/button";
import { liteMountedAssetUrl } from "@/lib/lite";
+1 -1
View File
@@ -2,7 +2,7 @@
import { useEffect, useRef, useState } from "react";
import { useTranslations } from "next-intl";
import { Loader2, X } from "lucide-react";
import { Loader2, X } from "@/components/icons";
import type { FileResource } from "@/stores/file-store";
interface WopiEditorProps {
+1 -1
View File
@@ -4,7 +4,7 @@ import { useState, useCallback, useMemo } from "react";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { X, Plus, Trash2 } from "lucide-react";
import { X, Plus, Trash2 } from "@/components/icons";
import { useFocusTrap } from "@/hooks/use-focus-trap";
import { toast } from "@/stores/toast-store";
import type {
+1 -1
View File
@@ -3,7 +3,7 @@
import { useState, useEffect, useRef, useCallback } from "react";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { X, AlertTriangle, CheckCircle, Loader2 } from "lucide-react";
import { X, AlertTriangle, CheckCircle, Loader2 } from "@/components/icons";
import { useFocusTrap } from "@/hooks/use-focus-trap";
interface SieveEditorModalProps {
@@ -1,7 +1,7 @@
"use client";
import { useEffect, useMemo, useRef } from "react";
import { AlertTriangle, Clock3, CornerDownLeft, Loader2, Search } from "lucide-react";
import { AlertTriangle, Clock3, CornerDownLeft, Loader2, Search } from "@/components/icons";
import { useTranslations } from "next-intl";
import { useRouter } from "@/i18n/navigation";
import { useGlobalSearch } from "@/hooks/use-global-search";
@@ -1,6 +1,6 @@
"use client";
import { CalendarDays, FileText, Folder, Repeat } from "lucide-react";
import { CalendarDays, FileText, Folder, Repeat } from "@/components/icons";
import { useTranslations } from "next-intl";
import { Avatar } from "@/components/ui/avatar";
import { parseSearchSnippet, type SnippetSegment } from "@/lib/search-snippet";
+1 -1
View File
@@ -1,6 +1,6 @@
"use client";
import { AlignLeft, Building2, CalendarDays, Clock, ExternalLink, FileText, Folder, HardDrive, Mail, MapPin, Phone, Repeat, Users } from "lucide-react";
import { AlignLeft, Building2, CalendarDays, Clock, ExternalLink, FileText, Folder, HardDrive, Mail, MapPin, Phone, Repeat, Users } from "@/components/icons";
import { useTranslations } from "next-intl";
import { Avatar } from "@/components/ui/avatar";
import { ProEmailView } from "@/components/pro/pro-email-tab-body";
@@ -1,6 +1,6 @@
"use client";
import { AlertTriangle, Loader2 } from "lucide-react";
import { AlertTriangle, Loader2 } from "@/components/icons";
import { useTranslations } from "next-intl";
import { compareByRecency } from "@/lib/global-search/rank";
import { hitKey, SEARCH_KINDS, type GlobalSearchHit, type SearchKind, type SearchOutcome } from "@/lib/global-search/types";
+1 -1
View File
@@ -1,7 +1,7 @@
"use client";
import { useEffect, useMemo, useState } from "react";
import { Loader2, Search } from "lucide-react";
import { Loader2, Search } from "@/components/icons";
import { useTranslations } from "next-intl";
import { useGlobalSearch } from "@/hooks/use-global-search";
import { openHit } from "@/lib/global-search/open-hit";
+62
View File
@@ -0,0 +1,62 @@
import { describe, it, expect } from 'vitest';
import { icons } from '@tabler/icons-react';
import {
LEGACY_LUCIDE,
POPULAR_ICON_NAMES,
iconForName,
toStoredIconName,
toTablerName,
} from '../named';
const componentKey = (name: string) =>
'Icon' + name.split('-').map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join('');
const exists = (name: string) => componentKey(name) in icons;
describe('toTablerName', () => {
it('reads the stored tabler: form', () => {
expect(toTablerName('tabler:inbox')).toBe('inbox');
expect(toTablerName(toStoredIconName('brand-github'))).toBe('brand-github');
});
it('translates Lucide names saved before the switch', () => {
expect(toTablerName('Globe')).toBe('world');
expect(toTablerName('Inbox')).toBe('inbox');
expect(toTablerName('Trash2')).toBe('trash');
expect(toTablerName('Gamepad2')).toBe('device-gamepad-2');
});
it('guesses unknown Lucide names by kebab-casing them', () => {
expect(toTablerName('CloudRain')).toBe('cloud-rain');
expect(toTablerName('Dice6')).toBe('dice-6');
});
it('accepts a bare Tabler name', () => {
expect(toTablerName('world')).toBe('world');
expect(toTablerName('a-b-2')).toBe('a-b-2');
});
it('rejects anything that is not a name', () => {
expect(toTablerName('')).toBeNull();
expect(toTablerName(null)).toBeNull();
expect(toTablerName('tabler:../etc')).toBeNull();
expect(toTablerName('Globe/../../x')).toBeNull();
});
});
describe('icon tables', () => {
it('maps every legacy Lucide name to an icon Tabler has', () => {
const missing = Object.entries(LEGACY_LUCIDE).filter(([, t]) => !exists(t));
expect(missing).toEqual([]);
});
it('only offers popular icons that exist', () => {
expect(POPULAR_ICON_NAMES.filter((n) => !exists(n))).toEqual([]);
});
it('returns a component for any usable name and nothing for junk', () => {
expect(iconForName('Globe')).toBe(icons.IconWorld);
expect(iconForName('tabler:zzz')).toBeDefined();
expect(iconForName('tabler:zzz')).toBe(iconForName('tabler:zzz'));
expect(iconForName('')).toBeUndefined();
});
});
+240
View File
@@ -0,0 +1,240 @@
// Bulwark's icon set. Every icon the UI draws is imported from here, so the
// pack behind it is a one-file decision. The export names are the app's own
// (they started out as Lucide names); the glyphs come from Tabler Icons (MIT).
//
// A few have no exact Tabler equivalent and use the closest glyph:
// BookPlus (book-2), FolderSync (folders), HardDrive (server-2), Mails (inbox),
// PowerOff (power), PlayCircle (player-play), ScrollText (file-text).
//
// Icons chosen by name at runtime (sidebar apps, message-list tabs) resolve
// through ./named instead.
export type { TablerIcon as AppIcon, IconProps as AppIconProps } from '@tabler/icons-react';
export { iconForName, toStoredIconName, toTablerName, POPULAR_ICON_NAMES, loadAllIconNames, TABLER_ICON_PREFIX } from './named';
export {
IconActivity as Activity,
IconAlertCircle as AlertCircle,
IconAlertTriangle as AlertTriangle,
IconAlignCenter as AlignCenter,
IconAlignLeft as AlignLeft,
IconAlignRight as AlignRight,
IconArchive as Archive,
IconArrowDown as ArrowDown,
IconSortAZ as ArrowDownAZ,
IconArrowLeft as ArrowLeft,
IconArrowLeftRight as ArrowLeftRight,
IconArrowRight as ArrowRight,
IconArrowUp as ArrowUp,
IconCircleArrowUp as ArrowUpCircle,
IconBan as Ban,
IconBaseline as Baseline,
IconBell as Bell,
IconBellOff as BellOff,
IconBold as Bold,
IconBook2 as Book,
IconBook as BookOpen,
IconBook2 as BookPlus,
IconAddressBook as BookUser,
IconBookmark as Bookmark,
IconBookmarkPlus as BookmarkPlus,
IconBox as Box,
IconBrain as Brain,
IconBriefcase as Briefcase,
IconBug as Bug,
IconBuilding as Building,
IconBuilding as Building2,
IconCake as Cake,
IconCalendar as Calendar,
IconCalendarUp as CalendarArrowUp,
IconCalendarCheck as CalendarCheck,
IconCalendarClock as CalendarClock,
IconCalendarMonth as CalendarDays,
IconCalendarX as CalendarX,
IconCamera as Camera,
IconCheck as Check,
IconChecks as CheckCheck,
IconCircleCheck as CheckCircle,
IconCircleCheck as CheckCircle2,
IconSquareCheck as CheckSquare,
IconChevronDown as ChevronDown,
IconChevronLeft as ChevronLeft,
IconChevronRight as ChevronRight,
IconChevronUp as ChevronUp,
IconChevronsLeft as ChevronsLeft,
IconChevronsRight as ChevronsRight,
IconCircle as Circle,
IconClipboard as Clipboard,
IconClipboardCopy as ClipboardCopy,
IconClock as Clock,
IconClockHour3 as Clock3,
IconCode as Code,
IconColumns3 as Columns3,
IconCopy as Copy,
IconCopyPlus as CopyPlus,
IconCornerDownLeft as CornerDownLeft,
IconDatabase as Database,
IconDownload as Download,
IconEdit as EditIcon,
IconDots as Ellipsis,
IconEraser as Eraser,
IconExternalLink as ExternalLink,
IconEye as Eye,
IconEyeOff as EyeOff,
IconFile as File,
IconFileZip as FileArchive,
IconFileMusic as FileAudio,
IconFileCode as FileCode,
IconPhoto as FileImage,
IconFilePlus as FilePlus,
IconFileSpreadsheet as FileSpreadsheet,
IconFileText as FileText,
IconVideo as FileVideo,
IconFilter as Filter,
IconFlag as Flag,
IconFlask as FlaskConical,
IconFolder as Folder,
IconFolderDown as FolderInput,
IconFolderOpen as FolderOpen,
IconFolderPlus as FolderPlus,
IconFolders as FolderSync,
IconFolderUp as FolderUp,
IconFolderX as FolderX,
IconArrowForwardUp as Forward,
IconWorld as Globe,
IconGripVertical as GripVertical,
IconServer2 as HardDrive,
IconH1 as Heading1,
IconH2 as Heading2,
IconHeart as Heart,
IconHelpCircle as HelpCircle,
IconHighlight as Highlighter,
IconHome as Home,
IconPhoto as Image,
IconPhoto as ImageIcon,
IconInbox as Inbox,
IconInfoCircle as Info,
IconItalic as Italic,
IconKey as Key,
IconKey as KeyRound,
IconKeyboard as Keyboard,
IconLanguage as Languages,
IconStack as Layers,
IconLayoutDashboard as LayoutDashboard,
IconLayoutGrid as LayoutGrid,
IconLayoutList as LayoutList,
IconBulb as Lightbulb,
IconLink as Link,
IconList as List,
IconListNumbers as ListOrdered,
IconChecklist as ListTodo,
IconLoader2 as Loader2,
IconLock as Lock,
IconLock as LockKeyhole,
IconLockOpen as LockOpen,
IconLogin as LogIn,
IconLogout as LogOut,
IconMail as Mail,
IconMailCheck as MailCheck,
IconMailOpened as MailOpen,
IconMailX as MailX,
IconInbox as Mails,
IconMapPin as MapPin,
IconMaximize as Maximize2,
IconMenu2 as Menu,
IconMessage as MessageSquare,
IconMessages as MessagesSquare,
IconMinimize as Minimize2,
IconMinus as Minus,
IconDeviceDesktop as Monitor,
IconMoon as Moon,
IconDots as MoreHorizontal,
IconDotsVertical as MoreVertical,
IconNotebook as NotebookPen,
IconPackage as Package,
IconPackage as PackageCheck,
IconPalette as Palette,
IconBeach as Palmtree,
IconBeach as PalmtreeIcon,
IconLayoutSidebarLeftCollapse as PanelLeftClose,
IconLayoutSidebarRight as PanelRight,
IconLayoutSidebarRightCollapse as PanelRightClose,
IconLayoutSidebarRightExpand as PanelRightOpen,
IconPaperclip as Paperclip,
IconPencil as PenLine,
IconEdit as PenSquare,
IconVector as PenTool,
IconPencil as Pencil,
IconPhone as Phone,
IconPinned as Pin,
IconPinnedOff as PinOff,
IconPlayerPlay as Play,
IconPlayerPlay as PlayCircle,
IconPlus as Plus,
IconPower as Power,
IconPower as PowerOff,
IconPresentation as Presentation,
IconPrinter as Printer,
IconPuzzle as Puzzle,
IconQrcode as QrCode,
IconQuote as Quote,
IconArrowForward as Redo,
IconRefresh as RefreshCw,
IconClearFormatting as RemoveFormatting,
IconRepeat as Repeat,
IconArrowBackUp as Reply,
IconArrowBackUpDouble as ReplyAll,
IconRotate as RotateCcw,
IconRotateClockwise as RotateCw,
IconLayoutRows as Rows3,
IconDeviceFloppy as Save,
IconScale as Scale,
IconScissors as Scissors,
IconFileText as ScrollText,
IconSearch as Search,
IconSearchOff as SearchX,
IconSend as Send,
IconServer as Server,
IconSettings as Settings,
IconShare2 as Share2,
IconShield as Shield,
IconShieldExclamation as ShieldAlert,
IconShieldCheck as ShieldCheck,
IconArrowsShuffle as Shuffle,
IconDeviceMobile as Smartphone,
IconSparkles as Sparkles,
IconSquare as Square,
IconEdit as SquarePen,
IconStar as Star,
IconNote as StickyNote,
IconBuildingStore as Store,
IconStrikethrough as Strikethrough,
IconSun as Sun,
IconColorSwatch as SwatchBook,
IconTable as Table,
IconTag as Tag,
IconTags as Tags,
IconTerminal as Terminal,
IconTrash as Trash,
IconTrash as Trash2,
IconAlertTriangle as TriangleAlert,
IconTypography as Type,
IconUnderline as Underline,
IconArrowBack as Undo,
IconLockOpen as Unlock,
IconUpload as Upload,
IconUser as User,
IconUserCircle as UserCircle,
IconUserMinus as UserMinus,
IconUserEdit as UserPen,
IconUserPlus as UserPlus,
IconUsers as Users,
IconUsers as UsersRound,
IconVideo as Video,
IconVolume2 as Volume2,
IconX as X,
IconCircleX as XCircle,
IconBolt as Zap,
IconZoomIn as ZoomIn,
IconZoomOut as ZoomOut,
} from '@tabler/icons-react';
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@
import { useEffect, useState, useCallback } from 'react';
import { useTranslations } from 'next-intl';
import { X, Mail, Pencil, Trash2, Plus, AlertTriangle, Star } from 'lucide-react';
import { X, Mail, Pencil, Trash2, Plus, AlertTriangle, Star } from '@/components/icons';
import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import { ConfirmDialog } from '@/components/ui/confirm-dialog';

Some files were not shown because too many files have changed in this diff Show More