Commit Graph

7 Commits

Author SHA1 Message Date
Saul-Mirone
cb550b7b21 refactor(editor): extract mobile extension builder (#12239)
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Introduced a new mobile view extension that activates mobile-specific UI features based on the runtime environment.
  - Automatically enables mobile keyboard toolbar and linked document menu features in mobile contexts.

- **Improvements**
  - Simplified code and paragraph block configurations on mobile, including disabling line numbers and adjusting placeholders.
  - Enhanced configuration chaining to include mobile-specific settings by default.
  - Improved extension registration flow with method chaining support.

- **Refactor**
  - Removed deprecated mobile patch classes and configurations, consolidating mobile logic into dedicated extensions.
  - Streamlined mobile-related code for better maintainability and performance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-05-12 12:54:51 +00:00
Saul-Mirone
41d404f7f8 refactor(editor): improve implementation of lit adapter (#12101)
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Improved mobile experience by disabling certain toolbars and slash menu features on mobile devices.
  - Introduced new modular extension classes for editor and view customization, enabling more flexible configuration of themes, AI features, and editor enhancements.
  - Added clipboard adapter configurations for a wide range of data types, improving clipboard compatibility.
  - Added a new theme extension specifically for preview scenarios.
  - Provided new hooks for block scope management in document modules.

- **Refactor**
  - Streamlined editor extension setup, consolidating options and reducing complexity for better maintainability.
  - Reorganized mobile-specific extension exports for clearer usage.
  - Refined React-to-Lit rendering API by introducing a typed alias and updating related function signatures.
  - Simplified extension registration by splitting monolithic view extension into separate common and editor view extensions.

- **Bug Fixes**
  - Corrected naming inconsistencies in internal effect tracking.

- **Chores**
  - Updated type exports and documentation comments for improved clarity and consistency.
  - Removed unused or redundant exports and functions to clean up the codebase.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-05-01 14:29:11 +00:00
Saul-Mirone
e57222b89a feat(editor): surface ref extension (#11902)
Closes: BS-3203
2025-04-23 01:16:07 +00:00
Saul-Mirone
99ad4e871e feat(editor): use migrated extensions (#11886)
Closes: BS-3283
2025-04-22 15:51:22 +00:00
Saul-Mirone
6d6504e2af feat(editor): replace spec provider with extension manager (#11861)
Closes: BS-3273
2025-04-22 07:40:41 +00:00
Saul-Mirone
a6d0894ce1 feat(editor): bookmark and attachment extension (#11824)
Closes: BS-3188
Closes: BS-3189
2025-04-21 04:27:18 +00:00
Saul-Mirone
70ddae15ef feat(editor): affine extension provider and manager (#11822)
Closes: BS-3186

# @blocksuite/affine-ext-loader

Blocksuite extension loader system for AFFiNE, providing a structured way to manage and load extensions in different contexts.

## Usage

### Basic Extension Provider

```typescript
import { BaseExtensionProvider } from '@blocksuite/affine-ext-loader';
import { z } from 'zod';

// Create a custom provider with options
class MyProvider extends BaseExtensionProvider<'my-scope', { enabled: boolean }> {
  name = 'MyProvider';

  schema = z.object({
    enabled: z.boolean(),
  });

  setup(context: Context<'my-scope'>, options?: { enabled: boolean }) {
    super.setup(context, options);
    // Custom setup logic
  }
}
```

### Store Extensions

```typescript
import { StoreExtensionProvider, StoreExtensionManager } from '@blocksuite/affine-ext-loader';
import { z } from 'zod';

// Create a store provider with custom options
class MyStoreProvider extends StoreExtensionProvider<{ cacheSize: number }> {
  override name = 'MyStoreProvider';

  override schema = z.object({
    cacheSize: z.number().min(0),
  });

  override setup(context: StoreExtensionContext, options?: { cacheSize: number }) {
    super.setup(context, options);
    context.register([Ext1, Ext2, Ext3]);
  }
}

// Create and use the store extension manager
const manager = new StoreExtensionManager([MyStoreProvider]);
manager.configure(MyStoreProvider, { cacheSize: 100 });
const extensions = manager.get('store');
```

### View Extensions

```typescript
import { ViewExtensionProvider, ViewExtensionManager } from '@blocksuite/affine-ext-loader';
import { z } from 'zod';

// Create a view provider with custom options
class MyViewProvider extends ViewExtensionProvider<{ theme: string }> {
  override name = 'MyViewProvider';

  override schema = z.object({
    theme: z.enum(['light', 'dark']),
  });

  override setup(context: ViewExtensionContext, options?: { theme: string }) {
    super.setup(context, options);

    context.register([CommonExt]);
    if (context.scope === 'page') {
      context.register([PageExt]);
    } else if (context.scope === 'edgeless') {
      context.register([EdgelessExt]);
    }
    if (options?.theme === 'dark') {
      context.register([DarkModeExt]);
    }
  }

  // Override effect to run one-time initialization logic
  override effect() {
    // This will only run once per provider class
    console.log('Initializing MyViewProvider');
    // Register lit elements
    this.registerLitElements();
  }
}

// Create and use the view extension manager
const manager = new ViewExtensionManager([MyViewProvider]);
manager.configure(MyViewProvider, { theme: 'dark' });

// Get extensions for different view scopes
const pageExtensions = manager.get('page');
const edgelessExtensions = manager.get('edgeless');
```

### One-time Initialization with Effect

View extensions support one-time initialization through the `effect` method. This method is called automatically during setup, but only once per provider class. It's useful for:

- Initializing global state
- Registering lit elements
- Setting up shared resources

```typescript
class MyViewProvider extends ViewExtensionProvider {
  override effect() {
    // This will only run once, even if multiple instances are created
    initializeGlobalState();
    registerLitElements();
    setupGlobalEventListeners();
  }
}
```

### Available View Scopes

The view extension system supports the following scopes:

- `page` - Standard page view
- `edgeless` - Edgeless (whiteboard) view
- `preview-page` - Page preview view
- `preview-edgeless` - Edgeless preview view
- `mobile-page` - Mobile page view
- `mobile-edgeless` - Mobile edgeless view

### Extension Configuration

Extensions can be configured using the `configure` method:

```typescript
// Set configuration directly
manager.configure(MyProvider, { enabled: true });

// Update configuration using a function
manager.configure(MyProvider, prev => {
  if (!prev) return prev;
  return {
    ...prev,
    enabled: !prev.enabled,
  };
});

// Remove configuration
manager.configure(MyProvider, undefined);
```

### Dependency Injection

Both store and view extension managers support dependency injection:

```typescript
// Access the manager through the di container
const viewManager = std.get(ViewExtensionManagerIdentifier);
const pagePreviewExtension = viewManager.get('preview-page');
```
2025-04-21 04:27:18 +00:00