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.

/en/posts/de5a391597b8a5/

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.

/en/posts/9e70d8f8af0283/

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.