Overview

I enabled initial angle settings in the Mirador 4 plugin for rotating images at arbitrary angles.

The repository is here.

https://github.com/nakamura196/mirador-rotation-plugin

The demo page is here. You can rotate images with initial settings for angle and bounding box.

https://nakamura196.github.io/mirador-rotation-plugin/

Background

The following article explains this plugin.

However, there was an issue where initial angle values could not be provided.

As introduced in the following article, it appeared that Mirador 4’s standard functionality allows providing initial angle values.

Additionally, since the “mirador-image-tools” plugin had been changed from webpack to Vite, I decided to reflect this change in “mirador-rotation-plugin” as well.

https://github.com/ProjectMirador/mirador-image-tools

Publishing on GitHub Pages

For publishing on GitHub Pages, I modified the vite.config.js from “mirador-image-tools” as follows. This allows creating a directory for GitHub Pages deployment using npm run build:demo.

https://github.com/nakamura196/mirador-rotation-plugin/blob/main/vite.config.js

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import fs from 'fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'url';
import { globSync } from 'glob';
import pkg from './package.json';

/**
* Vite configuration
*/
export default defineConfig({

  base: process.env.GITHUB_PAGES ? (process.env.BASE_PATH || '/mirador-rotation-plugin/') : '/',

  ...(

    process.env.GITHUB_PAGES ? {
      build: {
        outDir: 'dist',
        emptyOutDir: true,
        rollupOptions: {
          external: ['__tests__/*', '__mocks__/*'],
          input: fileURLToPath(new URL('./demo/src/index.html', import.meta.url)),
        },
        sourcemap: true,
      },
    } :
    {
      build: {
        lib: {
          entry: './src/index.js',
          fileName: (format) => (format === 'umd' ? 'mirador-rotation.js' : 'mirador-rotation.es.js'),
          formats: ['es', 'umd'],
          name: 'MiradorDlPlugin',
        },
        rollupOptions: {
          external: [...Object.keys(pkg.peerDependencies || {}), '__tests__/*', '__mocks__/*'],
          output: {
            assetFileNames: 'mirador-rotation.[ext]',
            globals: {
              react: 'React',
              'react-dom': 'ReactDOM',
            },
          },
        },
        sourcemap: true,
      },
    }
  ),
  esbuild: {
    exclude: [],
    // Matches .js and .jsx in __tests__ and .jsx in src
    include: [/__tests__\/.*\.(js|jsx)$/, /src\/.*\.jsx?$/],
    loader: 'jsx',
  },
  optimizeDeps: {
    esbuildOptions: {
      plugins: [
        {
          name: 'load-js-files-as-jsx',
          // TODO: rename all our files to .jsx ...
          setup(build) {
            build.onLoad({ filter: /(src|__tests__)\/.*\.js$/ }, async (args) => ({
              contents: await fs.readFile(args.path, 'utf8'),
              loader: 'jsx',
            }));
          },
        },
      ],
    },
  },
  plugins: [
    react(),
    // Add custom plugin to fix directory structure
    {
      name: 'fix-output-structure',
      closeBundle: async () => {
        if (process.env.GITHUB_PAGES) {
          const distDir = path.resolve('dist');
          const demoSrcDir = path.resolve(distDir, 'demo', 'src');

          // Check if demo/src/ directory exists
          try {
            const demoSrcStats = await fs.stat(demoSrcDir);
            if (demoSrcStats.isDirectory()) {
              console.log('Moving files from demo/src to root directory...');

              // Get file list in demo/src
              const files = await fs.readdir(demoSrcDir);

              // Move each file to root directory
              for (const file of files) {
                const srcPath = path.join(demoSrcDir, file);
                const destPath = path.join(distDir, file);

                const stats = await fs.stat(srcPath);
                if (stats.isFile()) {
                  await fs.copyFile(srcPath, destPath);
                  console.log(`Copied: ${srcPath} -> ${destPath}`);
                }
              }

              console.log('Files moved successfully.');

              // Remove demo/src hierarchy (optional)
              // await fs.rm(demoSrcDir, { recursive: true, force: true });
              // await fs.rm(path.resolve(distDir, 'demo'), { recursive: true, force: true });
              // console.log('Removed original directory structure.');
            }
          } catch (err) {
            if (err.code !== 'ENOENT') {
              console.error('Error processing output files:', err);
            }
          }
        }
      }
    }
  ],
  resolve: {
    alias: {
      '@tests/': fileURLToPath(new URL('./__tests__', import.meta.url)),
    },
  },
  server: {
    open: '/demo/src/index.html',
    port: '4446',
  },
});

Summary

We hope this is helpful for Mirador 4 plugin development.

Note that Mirador 4 is progressing toward React 19 support, and the plugin will also need to be updated for React 19 in the future.

https://github.com/ProjectMirador/mirador/releases/tag/v4.0.0-alpha.16

Once development of Mirador and the mirador-image-tools plugin advances, I would like to update this module for React 19 support as well.