canvasAssetLibraryExtension<TSourceDocument> function

EditorExtension<TSourceDocument> canvasAssetLibraryExtension<TSourceDocument>({
  1. required CanvasAssetLibrary library,
  2. required CanvasAssetLibrarySelectionPresenter presentSelection,
})

Creates an editor extension that inserts assets selected from library.

The host owns the catalog and selection UI. The extension owns the shared editor behavior after selection:

  • persists CanvasAssetLibraryItem.sourceRef, never thumbnailRef;
  • preserves the intrinsic aspect ratio for the initial image size;
  • places the image at the centre of the artboard;
  • inserts through editor history and selects the new node.

The action is hidden when library contains no items.

Implementation

EditorExtension<TSourceDocument> canvasAssetLibraryExtension<TSourceDocument>({
  required CanvasAssetLibrary library,
  required CanvasAssetLibrarySelectionPresenter presentSelection,
}) {
  final hasAssets = library.items.isNotEmpty;

  return StaticEditorExtension<TSourceDocument>(
    actionSpecs: <EditorActionSpec>[
      EditorActionSpec(
        id: const EditorActionId('assetLibrary.open'),
        section: EditorToolbarSection.add,
        labelBuilder: (_) => 'Assets',
        iconBuilder: (_) => Icons.collections_outlined,
        isEnabled: (_) => hasAssets,
        isVisible: (_) => hasAssets,
        priority: 85,
        invoke: (context) async {
          final selected = await presentSelection(
            context.buildContext,
            library,
          );

          if (selected == null || !context.buildContext.mounted) {
            return;
          }

          if (selected.sourceRef.trim().isEmpty) {
            context.ui.toast('Unable to add asset: missing image source.');
            return;
          }

          final artboard = context.editableScene.artboardSize;
          final size = _initialAssetSize(selected);
          final nodeId = _nextAssetNodeId();
          final assetId = nodeId;

          final node = ImageNode(
            id: nodeId,
            data: ImageData(
              assetId: assetId,
              size: size,
              fit: ImageFit.contain,
              align: const Vec2(0.5, 0.5),
            ),
            xf: Transform2D(position: Vec2(artboard.w * 0.5, artboard.h * 0.5)),
          );

          context.controller.applyEdit(
            EditorEdits.updateScene((scene) {
              final nextScene = scene.copyWith(
                assets: <CanvasAssetId, CanvasImageAsset>{
                  ...scene.assets,
                  assetId: CanvasImageAsset(
                    sourceRef: selected.sourceRef,
                    intrinsicSize: _validIntrinsicSize(selected.intrinsicSize),
                  ),
                },
              );

              return SceneTreeOps.addNode(nextScene, node);
            }),
          );
          context.selectItems([nodeId]);
        },
      ),
    ],
  );
}