mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(sandbox): add README and sync script (#673)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
12277fdac8
commit
877e4d8b6c
@@ -0,0 +1,57 @@
|
||||
# @videojs/sandbox
|
||||
|
||||
Vite-based playground for testing and developing Video.js 10 integrations. Each sandbox is a standalone entry point that demonstrates a different platform or scenario.
|
||||
|
||||
## Sandboxes
|
||||
|
||||
| Name | Description |
|
||||
| ------------------- | -------------------------------------- |
|
||||
| `core` | Framework-agnostic core API |
|
||||
| `html` | Web player with HTML custom elements |
|
||||
| `html-background` | Full-screen background video (HTML) |
|
||||
| `react` | React player with skin switching |
|
||||
| `react-tailwind` | React player styled with Tailwind CSS |
|
||||
| `react-background` | Full-screen background video (React) |
|
||||
|
||||
## Getting started
|
||||
|
||||
```bash
|
||||
# From the repo root
|
||||
pnpm dev
|
||||
# Or just the sandbox
|
||||
pnpm -F sandbox dev
|
||||
```
|
||||
|
||||
This runs `setup.ts` first, which mirrors any missing files from `templates/` into `src/`, then starts the Vite dev server. Open the root URL to see links to all sandboxes.
|
||||
|
||||
## How it works
|
||||
|
||||
The package has two parallel directories:
|
||||
|
||||
- **`templates/`** — Checked into git. The source of truth for each sandbox's starting point.
|
||||
- **`src/`** — Gitignored (except `index.html`). Your working copy where you freely edit, experiment, and break things.
|
||||
|
||||
On `pnpm dev`, `setup.ts` copies any file from `templates/` that doesn't already exist in `src/`. Existing files in `src/` are never overwritten, so your local changes are preserved across restarts.
|
||||
|
||||
## Syncing changes back to templates
|
||||
|
||||
When you've made improvements in `src/` that should become the new baseline:
|
||||
|
||||
```bash
|
||||
pnpm -F sandbox sync
|
||||
```
|
||||
|
||||
This shows a colored diff of every changed file, then prompts for confirmation before copying `src/` changes into `templates/`. Files that only exist in `templates/` are left untouched.
|
||||
|
||||
Sync when:
|
||||
|
||||
- You've fixed a bug or improved a sandbox and want to preserve it for others.
|
||||
- You're preparing a commit — templates are what gets checked in.
|
||||
|
||||
## Adding a new sandbox
|
||||
|
||||
1. Create a directory in `templates/` (e.g. `templates/my-feature/`).
|
||||
2. Add an `index.html` entry point and a `main.ts` or `main.tsx`.
|
||||
3. Add a link to your sandbox in `templates/index.html`.
|
||||
4. Register the entry in `vite.config.ts` under `rollupOptions.input`.
|
||||
5. Run `pnpm dev` — `setup.ts` mirrors the new template into `src/` automatically.
|
||||
@@ -6,7 +6,8 @@
|
||||
"scripts": {
|
||||
"dev": "tsx setup.ts && vite",
|
||||
"build": "tsx setup.ts && vite build",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"sync": "tsx scripts/sync.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@videojs/core": "workspace:*",
|
||||
@@ -17,13 +18,20 @@
|
||||
"@videojs/utils": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.2.0",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/prompts": "^2.4.9",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"@tailwindcss/vite": "^4.2.0",
|
||||
"@vitejs/plugin-react": "^5.1.4",
|
||||
"tailwindcss": "^4.2.0",
|
||||
"chalk": "^5.6.2",
|
||||
"diff": "^8.0.3",
|
||||
"dir-compare": "^5.0.0",
|
||||
"fs-extra": "^11.3.3",
|
||||
"prompts": "^2.4.2",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"tailwindcss": "^4.2.0",
|
||||
"tsx": "^4.0.0",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^6.0.0"
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import path from 'node:path';
|
||||
import chalk from 'chalk';
|
||||
import { createTwoFilesPatch } from 'diff';
|
||||
import { compareSync } from 'dir-compare';
|
||||
import fs from 'fs-extra';
|
||||
import prompts from 'prompts';
|
||||
|
||||
const SOURCE = './src';
|
||||
const TEMPLATES = './templates';
|
||||
|
||||
function colorizePatch(patch: string): string {
|
||||
return patch
|
||||
.split('\n')
|
||||
.map((line) => {
|
||||
if (line.startsWith('---') || line.startsWith('+++')) return chalk.dim(line);
|
||||
if (line.startsWith('-')) return chalk.red(line);
|
||||
if (line.startsWith('+')) return chalk.green(line);
|
||||
if (line.startsWith('@@')) return chalk.cyan(line);
|
||||
return chalk.gray(line);
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
function printDiff(oldPath: string, newPath: string, label: string): void {
|
||||
const oldContent = fs.existsSync(oldPath) ? fs.readFileSync(oldPath, 'utf8') : '';
|
||||
const newContent = fs.existsSync(newPath) ? fs.readFileSync(newPath, 'utf8') : '';
|
||||
const patch = createTwoFilesPatch(label, label, oldContent, newContent, undefined, undefined, { context: 3 });
|
||||
|
||||
// Strip the first two header lines (Index + ===) that createTwoFilesPatch adds
|
||||
const lines = patch.split('\n');
|
||||
const body = lines.slice(2).join('\n');
|
||||
|
||||
console.log(colorizePatch(body));
|
||||
}
|
||||
|
||||
console.log(chalk.bold(`\nComparing ${SOURCE} → ${TEMPLATES}\n`));
|
||||
|
||||
const res = compareSync(SOURCE, TEMPLATES, { compareContent: true });
|
||||
|
||||
const changes = (res.diffSet ?? []).filter(
|
||||
(d) => d.state !== 'equal' && d.type1 !== 'directory' && d.type2 !== 'directory'
|
||||
);
|
||||
|
||||
if (changes.length === 0) {
|
||||
console.log(chalk.gray('No differences found. Directories are in sync.'));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
for (const diff of changes) {
|
||||
const name = diff.name1 ?? diff.name2;
|
||||
if (!name) continue;
|
||||
|
||||
const filePath = path.join(diff.relativePath, name);
|
||||
|
||||
if (diff.state === 'left') {
|
||||
console.log(chalk.green(` + ${filePath} (new in src)`));
|
||||
printDiff('', path.join(SOURCE, diff.relativePath, name), filePath);
|
||||
}
|
||||
|
||||
if (diff.state === 'right') {
|
||||
console.log(chalk.red(` - ${filePath} (only in templates)`));
|
||||
}
|
||||
|
||||
if (diff.state === 'distinct') {
|
||||
console.log(chalk.yellow(` ~ ${filePath} (modified)`));
|
||||
printDiff(path.join(TEMPLATES, diff.relativePath, name), path.join(SOURCE, diff.relativePath, name), filePath);
|
||||
}
|
||||
}
|
||||
|
||||
console.log();
|
||||
console.log(chalk.bold(`${changes.length} change(s) found.`));
|
||||
console.log();
|
||||
|
||||
const { ok } = await prompts({
|
||||
type: 'confirm',
|
||||
name: 'ok',
|
||||
message: `Copy all changes from ${SOURCE} to ${TEMPLATES}?`,
|
||||
initial: false,
|
||||
});
|
||||
|
||||
if (!ok) {
|
||||
console.log(chalk.gray('\nAborted. No files were copied.'));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Only copy changed/new files from src — skip 'right' entries (only in templates)
|
||||
for (const diff of changes) {
|
||||
if (diff.state === 'right') continue; // don't delete files that only exist in templates
|
||||
|
||||
if (!diff.name1) continue;
|
||||
|
||||
const relPath = path.join(diff.relativePath, diff.name1);
|
||||
const src = path.join(SOURCE, relPath);
|
||||
const dest = path.join(TEMPLATES, relPath);
|
||||
|
||||
await fs.copy(src, dest, { overwrite: true });
|
||||
console.log(chalk.green(` ✔ Copied ${relPath}`));
|
||||
}
|
||||
|
||||
console.log(chalk.bold.green('\nDone!\n'));
|
||||
@@ -6,5 +6,5 @@
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src"]
|
||||
"include": ["src", "scripts"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user