npm package: @parent-tobias/filesystem-component
Description: A flexible, feature-rich filesystem web component with version control, multiple storage backends (IndexedDB, OPFS, File System Access API), and comprehensive theming support
Author: Tobias Parent
Latest version: 0.1.0

# Filesystem Component

A flexible, feature-rich filesystem web component built with Lit.js and TypeScript. This component provides a complete file management system with version control, multiple storage backends, and a clean UI.

## Features

- **Full Filesystem Operations**: Create, read, update, delete files and folders
- **Version Control**: Auto-save and named versions with history tracking
- **Multiple Storage Backends**:
  - **File System Access API** - Save to real, visible files in user-chosen directories ✨ NEW
  - OPFS (Origin Private File System) - Modern browser API
  - IndexedDB - Fallback for broader compatibility
  - Automatic fallback from OPFS to IndexedDB
  - Extensible for remote storage (API, Supabase, Firebase, etc.)
- **File References**: Files can exist in multiple folders (like tags/labels)
- **Folder Hierarchy**: Nested folders with breadcrumb navigation
- **Fork/Clone Files**: Create copies from any version
- **Custom Events**: React to user interactions (file selection, saves, etc.)
- **Public API**: Programmatic control via methods
- **Auto-save**: Debounced auto-saving with configurable timing
- **Clean UI**: Polished sidebar interface with Material Design inspired styling
- **Comprehensive Theming**: Full CSS custom properties support for colors, spacing, typography, and more

## Installation

```bash
npm install
npm run dev
```

## Usage

### Basic Setup

```html
<!DOCTYPE html>
<html>
  <head>
    <script type="module" src="/src/index.ts"></script>
  </head>
  <body>
    <filesystem-component></filesystem-component>
  </body>
</html>
```

### With TypeScript

```typescript
import { FileSystemComponent } from './filesystem-component';

const fileSystem = document.querySelector('filesystem-component') as FileSystemComponent;

// Create a file
const fileId = await fileSystem.createFile('hello.txt', 'Hello, World!');

// Load a file
const result = await fileSystem.loadFile(fileId);
console.log(result?.content); // "Hello, World!"

// Save a file (auto-save)
await fileSystem.saveFile(fileId, 'Updated content', { autoSave: true });

// Save a named version
await fileSystem.saveFile(fileId, 'Version 1.0 content', {
  autoSave: false,
  versionName: 'v1.0'
});
```

### Custom Events

```typescript
// Listen for file selection
fileSystem.addEventListener('file-selected', (e: Event) => {
  const detail = (e as CustomEvent).detail;
  console.log('Selected file:', detail.fileName);
  console.log('Breadcrumb:', detail.breadcrumb);
});

// Listen for folder selection
fileSystem.addEventListener('folder-selected', (e: Event) => {
  const detail = (e as CustomEvent).detail;
  console.log('Selected folder:', detail.folderName);
  console.log('Path:', detail.folderPath);
});

// Other events
fileSystem.addEventListener('file-created', (e) => { /* ... */ });
fileSystem.addEventListener('file-saved', (e) => { /* ... */ });
fileSystem.addEventListener('file-forked', (e) => { /* ... */ });
fileSystem.addEventListener('version-restored', (e) => { /* ... */ });
```

## Theming

The component supports comprehensive theming through CSS custom properties. You can customize colors, spacing, typography, and more to match your application's design system.

### Quick Example

```css
/* Override default theme */
filesystem-component {
  --fs-color-primary: #8b5cf6;
  --fs-color-bg-primary: #1a1a1a;
  --fs-color-text-primary: #ffffff;
  --fs-spacing-sm: 6px;
  --fs-font-family: 'Inter', sans-serif;
}
```

### Integrate with Other Components

```css
/* Define global theme variables */
:root {
  --app-color-primary: #0066cc;
  --app-color-bg: white;
  --app-spacing: 8px;
}

/* Apply to filesystem component */
filesystem-component {
  --fs-color-primary: var(--app-color-primary);
  --fs-color-bg-primary: var(--app-color-bg);
  --fs-spacing-sm: var(--app-spacing);
}

/* Apply to other components */
chordpro-editor,
chordpro-renderer {
  --chord-color-primary: var(--app-color-primary);
  --chord-color-bg: var(--app-color-bg);
}
```

### Available CSS Custom Properties

The component exposes 40+ CSS custom properties covering:
- **Colors**: Base backgrounds, text colors, borders, brand/action colors
- **Spacing**: From extra small (4px) to extra large (32px)
- **Typography**: Font family, sizes (11px-16px), weights
- **Effects**: Shadows, transitions, border radius
- **Dimensions**: Content max height

For complete documentation of all CSS custom properties, see [THEMING.md](./THEMING.md).

For a live demo, open [examples/theming-demo.html](./examples/theming-demo.html) in your browser.

## API Reference

### File Operations

#### `createFile(name: string, content: string, folderId?: string): Promise<string>`
Creates a new file with optional folder placement.

```typescript
const fileId = await fileSystem.createFile('document.txt', 'Initial content', folderId);
```

#### `loadFile(fileId: string, versionId?: string): Promise<{file: FileMetadata, content: string} | null>`
Loads a file's content. Optionally specify a version to load.

```typescript
const result = await fileSystem.loadFile(fileId);
const oldVersion = await fileSystem.loadFile(fileId, versionId);
```

#### `saveFile(fileId: string, content: string, options: SaveFileOptions): Promise<string>`
Saves a file, creating a new version.

```typescript
// Auto-save
await fileSystem.saveFile(fileId, content, { autoSave: true });

// Named version
await fileSystem.saveFile(fileId, content, {
  autoSave: false,
  versionName: 'Release v1.0'
});
```

#### `deleteFile(fileId: string): Promise<void>`
Deletes a file and all its versions.

#### `renameFile(fileId: string, newName: string): Promise<void>`
Renames a file.

#### `forkFile(fileId: string, versionId?: string, newName?: string): Promise<string>`
Creates a copy of a file from a specific version.

```typescript
const newFileId = await fileSystem.forkFile(fileId, versionId, 'Forked Document');
```

### Folder Operations

#### `createFolder(name: string, parentId?: string): Promise<string>`
Creates a new folder.

```typescript
const folderId = await fileSystem.createFolder('My Projects');
const subfolderId = await fileSystem.createFolder('Client Work', folderId);
```

#### `deleteFolder(folderId: string): Promise<void>`
Deletes a folder (files are unlinked, not deleted).

#### `renameFolder(folderId: string, newName: string): Promise<void>`
Renames a folder.

#### `moveFileToFolder(fileId: string, folderId: string): Promise<void>`
Links a file to a folder (file can exist in multiple folders).

### Version Control

#### `getVersionHistory(fileId: string): Promise<VersionInfo[]>`
Gets all versions of a file.

```typescript
const history = await fileSystem.getVersionHistory(fileId);
history.forEach(v => {
  console.log(v.name, v.timestamp, v.isAuto);
});
```

#### `restoreVersion(fileId: string, versionId: string): Promise<string>`
Restores an old version (creates a new version with old content).

```typescript
await fileSystem.restoreVersion(fileId, oldVersionId);
```

#### `pruneVersions(fileId: string, options: PruneOptions): Promise<void>`
Removes old auto-save versions.

```typescript
await fileSystem.pruneVersions(fileId, {
  keepLast: 10,
  keepNamed: true
});
```

### Query Operations

#### `getAllFiles(): Promise<FileMetadata[]>`
Gets all files in the system.

#### `getAllFolders(): Promise<FolderMetadata[]>`
Gets all folders in the system.

#### `refresh(): Promise<void>`
Refreshes the UI from storage.

## Storage Architecture

### Storage Options

The component supports **three storage backends**:

1. **OPFS (Origin Private File System)** - Modern, sandboxed browser storage
2. **IndexedDB** - Fallback for broader compatibility
3. **File System Access API** - Save to user-chosen folders as real files ✨ NEW

### Automatic Fallback
By default, the component tries OPFS first, then falls back to IndexedDB if OPFS is unavailable.

### Storage Types
```typescript
// Default: auto-fallback (OPFS → IndexedDB)
<filesystem-component></filesystem-component>

// Force IndexedDB
<filesystem-component storage-type="indexeddb"></filesystem-component>

// Force OPFS
<filesystem-component storage-type="opfs"></filesystem-component>

// Force File System Access (user picks directory)
<filesystem-component storage-type="filesystemaccess"></filesystem-component>
```

### File System Access API

**What makes it special:**
- ✅ Files saved as **real, visible files** in your chosen directory
- ✅ Access files outside the browser (using any text editor)
- ✅ Folder structure matches real directories
- ✅ Choose where to save your data
- ⚠️ Requires user permission (browser will prompt)
- ⚠️ Chrome/Edge 86+, not available in Firefox/Safari yet

**How to use:**

1. Click the **"📁 Pick Folder"** button in the component header
2. Choose a directory on your computer
3. Grant read/write permission
4. Files are now saved as real files!

**✨ Persistence:**
- Your directory selection is **automatically saved** to IndexedDB
- On page reload, the directory **auto-restores** if permission is still granted
- No need to pick the folder again after refreshing!
- See [PERSISTENCE.md](./PERSISTENCE.md) for details

**What gets created:**

```
your-chosen-folder/
├── .fs-meta/              # Hidden metadata directory
│   ├── files.json         # File metadata
│   ├── folders.json       # Folder structure
│   └── versions/          # Version history
│       ├── v1.json
│       └── v2.json
├── My Documents/          # Your actual folders
│   ├── report.txt         # Your actual files!
│   └── notes.txt
└── Projects/
    └── todo.md
```

**Programmatic usage:**

```typescript
const fileSystem = document.querySelector('filesystem-component');

// Switch to File System Access storage
await fileSystem.service.switchToFileSystemAccess();
// This prompts user to pick a directory

// Now all operations save to real files
await fileSystem.createFile('document.txt', 'Hello!');
```

### Custom Storage Adapter
Implement the `StorageAdapter` interface for custom backends:

```typescript
import { StorageAdapter } from './storage/storage-adapter';

class RemoteStorageAdapter implements StorageAdapter {
  async getFile(id: string) { /* API call */ }
  async saveFile(file: FileMetadata) { /* API call */ }
  // ... implement all methods
}
```

## Version Control Strategy

### Auto-Save Versions
- Created automatically after 2 seconds of inactivity
- Timestamped: `v_20231213_143022`
- Old versions auto-pruned (keeps last 10)

### Named Versions
- Created manually by user
- Custom names: "v1.0", "First Draft", etc.
- Never auto-pruned
- Permanent record of important states

### File Forking
Clone any version into a new file for experimentation.

## Events Reference

| Event | Detail | Description |
|-------|--------|-------------|
| `file-selected` | `FileSelectedDetail` | User clicked a file |
| `folder-selected` | `FolderSelectedDetail` | User clicked a folder |
| `file-created` | `{ fileName: string }` | New file created |
| `file-saved` | `{ fileId, versionId, isAutoSave }` | File saved |
| `file-deleted` | `{ fileId }` | File deleted |
| `file-forked` | `{ originalFileId, newFileId }` | File forked |
| `folder-created` | `{ folderName }` | Folder created |
| `folder-deleted` | `{ folderId }` | Folder deleted |
| `version-restored` | `{ fileId, versionId, newVersionId }` | Version restored |

## Development

```bash
# Install dependencies
npm install

# Run dev server
npm run dev

# Build
npm run build

# Preview build
npm run preview
```

## Browser Support

- **File System Access API**: Chrome 86+, Edge 86+ (real files in user directories)
- **OPFS**: Chrome 86+, Edge 86+, Opera 72+ (sandboxed browser storage)
- **IndexedDB**: All modern browsers (fallback)

## License

MIT

## Future Enhancements

- Remote storage adapters (Supabase, Firebase, custom API)
- Search functionality
- File tagging
- Drag-and-drop file organization
- Export/import functionality
- Collaborative editing support
- Conflict resolution for concurrent edits