npm package: @fde-desktop/fde-core
Description: Core library for building FDE Desktop environments
Author: Francisco Núñez Palomares
Latest version: 0.4.13

# @fde-desktop/fde-core

Core library for building FDE Desktop environments. This package provides everything needed to create desktop-like applications in React:

- **UI Components**: Desktop, Window, Taskbar, Launcher, ContextMenu, etc.
- **Core Apps**: Files, Settings, Notepad, Image Viewer, PDF Viewer, etc.
- **Stores**: Zustand stores for desktop, settings, custom apps
- **Hooks**: 30+ hooks for app management, notifications, icons, file system, etc.
- **Domain Entities**: createWindow, createDesktopIcon, createLauncherFolder
- **Infrastructure**: IndexedDBFileSystem, DockerFileSystemAdapter, FdeApiAdapter
- **Types & Utilities**: Full TypeScript types and helper functions

## Installation

```bash
npm install @fde-desktop/fde-core
# or
yarn add @fde-desktop/fde-core
# or
bun add @fde-desktop/fde-core
```

You also need to import the package styles in your entry point:

```tsx
import '@fde-desktop/fde-core/dist/index.css';
```

This single import includes all required styles (Mantine, component styles, etc.).

## Peer Dependencies

- `react` ^19.0.0
- `react-dom` ^19.0.0

## Quick Start

The simplest way is to use the `FdeDesktop` component:

```tsx
import { FdeDesktop, type AppRegistryEntry } from '@fde-desktop/fde-core';
import '@fde-desktop/fde-core/dist/index.css';

// Define your custom apps (optional)
const myApps: Record<string, AppRegistryEntry> = {
  'my-app': {
    loader: () => import('./MyApp'),
    appName: 'My App',
  },
};

function App() {
  return <FdeDesktop customApps={myApps} />;
}
```

Core apps (Files, Settings, Notepad, etc.) are loaded automatically. Your custom apps are merged with the core registry.

## FdeDesktop Props

```tsx
<FdeDesktop
  customApps={userApps}          // Optional: your custom apps
  defaultWallpaper={wallpaperUrl} // Optional: default wallpaper URL
  initialApp="welcome"            // Optional: app ID to open on start
  mode="dark"                     // Optional: force 'light' | 'dark' theme
  prefetchLoaders={[              // Optional: preload lazy apps
    { id: 'dos-emulator', loader: () => import('./DosEmulatorApp') },
  ]}
>
  {/* Optional: extra desktop content */}
</FdeDesktop>
```

## Creating a Custom App

#### Step 1: Create the component

Every app receives `WindowContentProps` — a `window` entity and a `notifyReady` callback:

```tsx
// MyApp.tsx
import { type FC, useEffect } from 'react';
import type { WindowContentProps } from '@fde-desktop/fde-core';

const MyApp: FC<WindowContentProps> = ({ window: win, notifyReady }) => {
  const { filePath } = (win?.contentData ?? {}) as { filePath?: string };

  useEffect(() => {
    notifyReady?.({});
  }, [win, notifyReady]);

  return (
    <div>
      <h1>My App</h1>
      {filePath && <p>Opening: {filePath}</p>}
    </div>
  );
};

export default MyApp;
```

#### Step 2: Register the app

```tsx
// App.tsx
import { FdeDesktop, type AppRegistryEntry } from '@fde-desktop/fde-core';

const myApps: Record<string, AppRegistryEntry> = {
  'my-app': {
    loader: () => import('./MyApp'),
    appName: 'My App',
    isLazy: true,
    defaultWidth: 800,
    defaultHeight: 600,
    minWidth: 640,
    minHeight: 480,
    fcIcon: 'FcFolder',
  },
};

function App() {
  return <FdeDesktop customApps={myApps} />;
}
```

#### Step 3: Open the app programmatically

```tsx
import { useOpenApp } from '@fde-desktop/fde-core';

function MyButton() {
  const openApp = useOpenApp();

  return (
    <button onClick={() => openApp('my-app', { contentData: { filePath: '/docs/readme.md' } })}>
      Open My App
    </button>
  );
}
```

#### Step 4: Add a menu bar (optional)

```tsx
// MyAppMenuBar.tsx
import { type FC, memo } from 'react';
import { AppMenuBar } from '@fde-desktop/fde-core';
import type { MenuBarComponentProps, AppMenuElement } from '@fde-desktop/fde-core';

export const MyAppMenuBar: FC<MenuBarComponentProps> = memo(({ windowId, onExit }) => {
  const elements: AppMenuElement[] = [
    {
      type: 'menu',
      label: 'File',
      items: [
        { type: 'item', label: 'Exit', icon: 'FcLeave', onClick: onExit },
      ],
    },
  ];

  return <AppMenuBar elements={elements} />;
});
MyAppMenuBar.displayName = 'MyAppMenuBar';
```

Register it alongside the component:

```tsx
const myApps: Record<string, AppRegistryEntry> = {
  'my-app': {
    loader: () => import('./MyApp'),
    menuBarLoader: () => import('./MyAppMenuBar').then(m => ({ default: m.MyAppMenuBar })),
    appName: 'My App',
    isLazy: true,
  },
};
```

## Exports

### UI Components

```tsx
import {
  FdeDesktop,
  Desktop,
  DesktopIcon,
  Window,
  AppMenuBar,
  DynamicMenuBarRenderer,
  Taskbar,
  Launcher,
  TaskbarContextMenu,
  LanguageSelector,
  ContextMenu,
  CreateItemContextMenu,
  ThemeProvider,
  useTheme,
  DEFAULT_FDE_THEME,
  FdeProvider,
  useFdeContext,
  AppReadyProvider,
  useAppReady,
  initRegistry,
  getAppComponent,
  getMenuBarComponent,
  getMenuBarBuilder,
  isLazyApp,
  getAppName,
  registerComponents,
} from '@fde-desktop/fde-core';
```

### Apps (Core Apps)

```tsx
import {
  FilesApp,
  SettingsApp,
  NotesApp,
  ImageViewerApp,
  PdfApp,
  UploaderApp,
  MenuEditApp,
  DeviceInfoApp,
  CalendarApp,
  TerminalApp,
  CodeServerApp,
  CORE_APPS,
  CORE_APP_IDS,
  DEFAULT_WINDOW_DIMENSIONS,
  // Menu bars
  NotesMenuBar,
  SettingsMenuBar,
  PdfMenuBar,
  ImageViewerMenuBar,
  MenuEditMenuBar,
  TerminalMenuBar,
} from '@fde-desktop/fde-core';
```

### Lazy-Loaded App Components

```tsx
import {
  FilesAppLazy,
  NotesAppLazy,
  ImageViewerAppLazy,
  PdfAppLazy,
  MenuEditAppLazy,
  UploaderAppLazy,
  SettingsAppLazy,
  DeviceInfoAppLazy,
  CalendarAppLazy,
  CreateItemAppLazy,
} from '@fde-desktop/fde-core';
```

### Shared Components

```tsx
import {
  AppIcon,
  VscIcon,
  AppLoading,
  AppEmptyState,
  LanguageFlag,
  CollapsibleSection,
  FileIcon,
  DirtyIndicator,
  ColorPicker,
  CreateItemApp,
  DownloadProgress,
  FilePickerApp,
  FilePickerModal,
  FolderPicker,
  IconColorPicker,
  SettingsSection,
  ExternalLinkApp,
  CustomAppLoader,
  CustomAppLoadingScreen,
  CustomAppNotSupported,
  CustomAppOffline,
  PortraitWarning,
} from '@fde-desktop/fde-core';
```

### Stores

```tsx
import {
  useDesktopStore,
  useSettingsStore,
  useCustomAppStore,
  useAppTempMemoryStore,
  useCloseModalStore,
  fileSystem,
  resetFileSystem,
  resetWindowManager,
  getPersistedThemeMode, // Read theme from localStorage before React mounts
} from '@fde-desktop/fde-core';

// Desktop state
const { windows, openWindow, closeWindow } = useDesktopStore();

// Settings
const { wallpaper, theme, language, setThemeMode } = useSettingsStore();

// Custom apps (Docker)
const { customApps, loadCustomApps } = useCustomAppStore();
```

### Hooks

```tsx
import {
  // App management
  useOpenApp,
  useAppName,
  useFdeApi,

  // UI
  useClock,
  useSystemTheme,
  useAnimationDuration,
  useAdaptiveDimensions,
  useTaskbarHeight,
  useTaskbarContextMenu,
  useContextMenu,
  useCollapsible,
  useSearchFilter,

  // Notifications
  useNotifications,

  // File system
  useDownload,
  useResolvedUrl,

  // Icons
  useFcIcon,
  useFcIconElement,
  useFiIcon,
  useFiIconElement,
  useVscIcon,
  useVscIconElement,

  // Drag & Drop
  DragDropProvider,
  useDroppableArea,
  useDraggableItem,
  useDragDropState,

  // Window
  useWindowButtonRegistry,

  // Theme & Language
  useApplyFont,
  useFontDownload,
  useLanguageSync,

  // State
  useIsDirty,
  useCloseInterceptor,

  // Device
  useDeviceDetection,

  // Updates
  useAppUpdate,

  // Custom apps
  useCustomAppHMR,
  useCustomAppLifecycle,

  // Lifecycle
  useFolderName,
} from '@fde-desktop/fde-core';
```

### Domain Entities

```tsx
import {
  createWindow,
  createDesktopIcon,
  createLauncherFolder,
  createPredefinedLauncherFolder,
  updateLauncherFolder,
  createFolder,
  createFile,
} from '@fde-desktop/fde-core';

const win = createWindow({
  title: 'My Window',
  appId: 'my-app',
  x: 100,
  y: 100,
  width: 800,
  height: 600,
});

const icon = createDesktopIcon({
  appId: 'files',
  x: 20,
  y: 20,
  name: 'Files',
  fcIcon: 'FcFolder',
});
```

### Infrastructure

```tsx
import {
  IndexedDBFileSystem,
  DockerFileSystemAdapter,
  FdeApiAdapter,
  initializeFdeApi,
  getFdeApi,
  WindowManagerAdapter,
  DefaultThemeProvider,
  toMantineTheme,
  JsDosAdapter,
  prefetchApps,
  migrateStorage,
  SUPPORTED_LANGUAGES,
  DEFAULT_LANGUAGE,
} from '@fde-desktop/fde-core';
```

### Types & Interfaces

```tsx
import type {
  // App types
  AppEntry,
  AppRegistryEntry,
  AppComponent,
  WindowContentProps,
  MenuBarBuilder,
  MenuBarComponent,
  MenuBarComponentProps,

  // Window types
  WindowEntity,
  WindowInput,
  WindowProps,

  // File system types
  FileSystemNode,
  FileNode,
  FolderNode,
  IFileSystem,

  // Desktop types
  DesktopIconEntity,
  DesktopIconInput,
  DesktopState,
  NotificationItem,
  ClipboardState,

  // Theme types
  Theme,
  ThemeMode,
  FdeTheme,
  CustomThemeColors,
  IThemeProvider,

  // Other
  AppMenuElement,
  MenuItem,
  CreateFolderOptions,
  FdeApi,
  LauncherFolder,
  LauncherFolderInput,
  SupportedLanguage,
} from '@fde-desktop/fde-core';
```

### Utilities

```tsx
import {
  // UUID
  generateUUID,
  uuidv4,

  // Runtime detection
  getRuntime,
  isDocker,
  isBrowser,
  isElectron,
  isDev,

  // Formatting
  formatBytes,
  getFileExtension,
  getMimeTypeFromExtension,
  getAppIdForMime,

  // File system
  sortNodes,
  sortDesktopIcons,
  buildBreadcrumbs,

  // Version
  isNewerVersion,

  // Browser
  clearBrowserData,

  // DOM
  waitForContainer,

  // URL
  getBaseUrl,
  resolveUrl,

  // Hash
  hashBlob,

  // Colors
  getFourRandomColors,
} from '@fde-desktop/fde-core';
```

### Constants

```tsx
import {
  DEFAULT_WINDOW_DIMENSIONS,
  ICON_COLUMN_WIDTH,
  ICON_ROW_HEIGHT,
  ICON_MARGIN,
  TASKBAR_HEIGHT,
  DEFAULT_VIEWPORT_WIDTH,
  DEFAULT_VIEWPORT_HEIGHT,
  BREAKPOINTS,
  ANIMATION_DURATION,
  AVAILABLE_FONTS,
  FONT_STACKS,
  GOOGLE_FONTS_HREF,
  PRESET_COLORS,
  PRESET_ICONS,
  DEFAULT_LAUNCHER_FOLDERS,
  CUSTOM_APPS_FOLDER_ID,
  APP_IDS,
  DEFAULT_FDE_THEME,
} from '@fde-desktop/fde-core';
```

### i18n

```tsx
import { i18n } from '@fde-desktop/fde-core';
import { I18nextProvider } from 'react-i18next';

function App() {
  return (
    <I18nextProvider i18n={i18n}>
      <FdeDesktop />
    </I18nextProvider>
  );
}
```

## Core Apps

| App ID         | Name         | Description             |
| -------------- | ------------ | ----------------------- |
| `files`        | Files        | File browser            |
| `settings`     | Settings     | System settings         |
| `notepad`      | Notepad      | Text editor (TipTap v3) |
| `image-viewer` | Image Viewer | Image viewer            |
| `pdf`          | PDF Viewer   | PDF viewer              |
| `uploader`     | Uploader     | File uploader           |
| `device-info`  | Device Info  | System information      |
| `calendar`     | Calendar     | Calendar widget         |
| `menuedit`     | MenuEdit     | Launcher folder editor  |
| `terminal`     | Terminal     | Terminal emulator       |
| `code-server`  | VS Code      | VS Code in browser      |

## Runtime Detection

```tsx
import { getRuntime, isDocker, isBrowser, isElectron, isDev } from '@fde-desktop/fde-core';

const runtime = getRuntime(); // 'browser' | 'docker' | 'electron'

if (isDocker()) {
  // Docker-specific code (API-based file system)
}

if (isBrowser()) {
  // Browser-only code (IndexedDB file system)
}

if (isElectron()) {
  // Electron desktop app code
}
```

## Docker Integration

For Docker-based deployments:

```typescript
import { DockerFileSystemAdapter, FdeApiAdapter, initializeFdeApi } from '@fde-desktop/fde-core';

initializeFdeApi();

const dockerFs = new DockerFileSystemAdapter();

// Custom app hot-reload
useCustomAppHMR();
```

## Example App

The main repository's `src/` directory contains a complete example application that demonstrates how to use this package. It includes:

- **WelcomeApp**: Personal portfolio/profile app
- **LinkedinApp** / **GithubApp**: External link apps
- **DosEmulatorApp**: DOS game emulator using js-dos
- **Custom App Registry**: Shows how to register custom apps

See the [main repository](https://github.com/frannunpal/fde-desktop) for the complete example.

## License

Apache-2.0