- Module C: BC+PPO training v5/v6 done; eval results in experiments/eval_intervention_v{5,6}.json
- Reward: v5 label-aligned constrained reward (code/src/rl/reward.py)
- Ablations: Module B (history_r, response_only, full) + Module C (wo_category_reward)
- SOTA baselines: WildGuard and ShieldGemma2b eval scripts and results
- Paper: update sections 05–08 (Module B/C description, experiments table, discussion)
- Docs: add record.md (change log), update state.md and exp.md; retire change.md
- Tools: add html-to-ppt utilities and run_shieldgemma2b.sh
- Configs: add ablation YAML configs for Module B and C
- Cleanup: remove stale reference/ PNG screenshots
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
76 lines
2.5 KiB
JavaScript
76 lines
2.5 KiB
JavaScript
const fs = require('fs/promises');
|
|
const path = require('path');
|
|
const { chromium } = require('playwright');
|
|
const pptxgen = require('pptxgenjs');
|
|
|
|
const inputUrl = process.argv[2];
|
|
const imageDir = process.argv[3];
|
|
const outputPptx = process.argv[4];
|
|
|
|
async function extractNotes(url) {
|
|
const launchOptions = { headless: true };
|
|
if (process.env.BROWSER_EXE) {
|
|
launchOptions.executablePath = process.env.BROWSER_EXE;
|
|
}
|
|
|
|
const browser = await chromium.launch(launchOptions);
|
|
const page = await browser.newPage({ viewport: { width: 1920, height: 1080 } });
|
|
const baseUrl = url.includes('#') ? url.slice(0, url.indexOf('#')) : url;
|
|
await page.goto(`${baseUrl}#1`, { waitUntil: 'load', timeout: 30000 });
|
|
await page.waitForSelector('#speaker-notes', { state: 'attached', timeout: 10000 });
|
|
const notes = await page.evaluate(() => JSON.parse(document.getElementById('speaker-notes').textContent));
|
|
await browser.close();
|
|
return notes;
|
|
}
|
|
|
|
(async () => {
|
|
if (!inputUrl || !imageDir || !outputPptx) {
|
|
throw new Error('Usage: node html_to_ppt_with_notes.js <file-url> <image-dir> <output-pptx>');
|
|
}
|
|
|
|
const notes = await extractNotes(inputUrl);
|
|
const images = (await fs.readdir(imageDir))
|
|
.filter((name) => /^slide_\d+\.png$/i.test(name))
|
|
.sort()
|
|
.map((name) => path.join(imageDir, name));
|
|
|
|
if (notes.length !== images.length) {
|
|
throw new Error(`Notes count (${notes.length}) does not match image count (${images.length})`);
|
|
}
|
|
|
|
await fs.mkdir(path.dirname(outputPptx), { recursive: true });
|
|
await fs.writeFile(
|
|
path.join(path.dirname(outputPptx), 'Generative_Image_Dynamics.notes.json'),
|
|
JSON.stringify(notes.map((text, index) => ({ slide: index + 1, text })), null, 2),
|
|
'utf8',
|
|
);
|
|
|
|
const pptx = new pptxgen();
|
|
pptx.layout = 'LAYOUT_WIDE';
|
|
pptx.author = 'Codex';
|
|
pptx.subject = 'Generative Image Dynamics — CVPR 2024';
|
|
pptx.title = 'Generative Image Dynamics';
|
|
pptx.company = '';
|
|
pptx.lang = 'en-US';
|
|
pptx.theme = {
|
|
headFontFace: 'Aptos',
|
|
bodyFontFace: 'Aptos',
|
|
lang: 'en-US',
|
|
};
|
|
|
|
for (let i = 0; i < images.length; i += 1) {
|
|
const slide = pptx.addSlide();
|
|
slide.background = { color: '000000' };
|
|
slide.addImage({ path: images[i], x: 0, y: 0, w: 13.333333, h: 7.5 });
|
|
slide.addNotes(notes[i]);
|
|
}
|
|
|
|
await pptx.writeFile({ fileName: outputPptx });
|
|
console.log(`pptx=${outputPptx}`);
|
|
console.log(`slides=${images.length}`);
|
|
console.log(`notes=${notes.length}`);
|
|
})().catch((err) => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
});
|