@patch-adams/ui
v1.0.2
Published
React UI for Rise course package patching - drag-and-drop ZIP patching in the browser
Downloads
280
Maintainers
Readme
@patch-adams/ui
React UI for patching Rise course packages in the browser. Drag-and-drop ZIP files to inject remote CSS/JS with local fallback.
Features
- Drag-and-drop interface: Drop multiple ZIP files at once
- Browser-based patching: No server required, runs entirely client-side
- Progress tracking: Real-time progress for each file
- Format detection: Auto-detects SCORM, cmi5, xAPI packages
- Instant download: Download patched files immediately
Installation
npm install @patch-adams/uiQuick Start
Option 1: Deploy as Standalone App
Clone and build the UI:
git clone <repo>
cd patch.adams/packages/ui
npm install
npm run buildThe dist/ folder contains static files ready for deployment to any web server or CDN.
Option 2: Run Development Server
cd patch.adams/packages/ui
npm install
npm run devOpen http://localhost:5173 in your browser.
Option 3: Embed in Your React App
import { usePatcher } from '@patch-adams/ui/hooks/usePatcher';
function MyPatcher() {
const { patch, isProcessing, progress, error } = usePatcher();
const handleFile = async (file: File) => {
const result = await patch(file);
// result.blob contains the patched ZIP
// Trigger download or upload to server
};
return (
<div>
<input
type="file"
accept=".zip"
onChange={(e) => e.target.files?.[0] && handleFile(e.target.files[0])}
/>
{isProcessing && <p>Processing: {progress}%</p>}
{error && <p>Error: {error}</p>}
</div>
);
}Configuration
The patcher configuration is defined in src/hooks/usePatcher.ts. Edit this file to customize:
const CONFIG: PatcherConfig = {
// Remote domain where your CSS/JS override files are hosted
remoteDomain: 'https://cdn.example.com/rise-overrides',
// HTML classes for specificity and loading state
htmlClass: 'pa-patched',
loadingClass: 'pa-loading',
// CSS loaded at start of <head> - BLOCKING
cssBefore: {
filename: 'before.css',
enabled: true,
},
// CSS loaded at end of <head> - ASYNC with fallback
cssAfter: {
filename: 'after.css',
enabled: true,
timeout: 5000,
},
// JS loaded at start of <head> - BLOCKING
jsBefore: {
filename: 'before.js',
enabled: true,
},
// JS loaded at end of <body> - ASYNC with fallback
jsAfter: {
filename: 'after.js',
enabled: true,
timeout: 5000,
},
// Folder names for local fallback files
localFolders: {
css: 'css',
js: 'js',
},
updateManifests: true,
};How It Works
- User drops a Rise course ZIP file
- UI reads the ZIP using JSZip (browser-based)
- Detects package format (SCORM, cmi5, xAPI)
- Injects CSS/JS loaders into
scormcontent/index.html - Adds local fallback files to
css/andjs/folders - Generates patched ZIP for download
Components
DropZone
Drag-and-drop area for ZIP files.
import { DropZone } from '@patch-adams/ui/components';
<DropZone
onFilesAdded={(files) => handleFiles(files)}
disabled={isProcessing}
/>FileList
Displays processed files with download buttons.
import { FileList } from '@patch-adams/ui/components';
<FileList
files={processedFiles}
onRemove={(id) => removeFile(id)}
/>ProcessingStatus
Shows progress during patching.
import { ProcessingStatus } from '@patch-adams/ui/components';
<ProcessingStatus
progress={75}
fileName="course.zip"
/>usePatcher Hook
Core patching logic.
import { usePatcher } from '@patch-adams/ui/hooks/usePatcher';
const {
patch, // (file: File) => Promise<ProcessedFile>
isProcessing, // boolean
progress, // number (0-100)
error, // string | null
config // PatcherConfig
} = usePatcher();Types
interface ProcessedFile {
id: string;
originalName: string;
patchedName: string;
originalSize: number;
patchedSize: number;
format: string;
formatDisplayName: string;
blob: Blob;
timestamp: Date;
}
interface PatcherConfig {
remoteDomain: string;
htmlClass: string;
loadingClass: string;
cssBefore: { filename: string; enabled: boolean };
cssAfter: { filename: string; enabled: boolean; timeout: number };
jsBefore: { filename: string; enabled: boolean };
jsAfter: { filename: string; enabled: boolean; timeout: number };
localFolders: { css: string; js: string };
updateManifests: boolean;
}Deployment
Static Hosting (Recommended)
Build and deploy to any static host:
npm run build
# Upload dist/ to your CDN, S3, Netlify, Vercel, etc.Docker
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80Environment-Specific Config
For different environments, modify src/hooks/usePatcher.ts before building, or use environment variables with Vite:
const CONFIG: PatcherConfig = {
remoteDomain: import.meta.env.VITE_REMOTE_DOMAIN || 'https://cdn.example.com',
// ...
};Dependencies
- React 18+
- JSZip (browser ZIP handling)
- react-dropzone (drag-and-drop)
- file-saver (download files)
- Tailwind CSS (styling)
License
MIT
