90 lines
3.1 KiB
TypeScript
90 lines
3.1 KiB
TypeScript
import { useCutsceneStore, useSelectedCutscene } from '../../store/cutsceneStore';
|
|
import { useShallow } from 'zustand/react/shallow';
|
|
import styles from './RightPanel.module.css';
|
|
|
|
export default function SubtitleLines() {
|
|
const cutscene = useSelectedCutscene();
|
|
const { addLine, removeLine, moveLineUp, moveLineDown, updateLine } = useCutsceneStore(useShallow(s => ({
|
|
addLine: s.addLine,
|
|
removeLine: s.removeLine,
|
|
moveLineUp: s.moveLineUp,
|
|
moveLineDown: s.moveLineDown,
|
|
updateLine: s.updateLine,
|
|
})));
|
|
|
|
if (!cutscene) return null;
|
|
|
|
return (
|
|
<div className={styles.section}>
|
|
<div className={styles.sectionHeader}>
|
|
<div className={styles.sectionTitle}>Subtitle Lines</div>
|
|
<button className={styles.addBtn} onClick={addLine}>+ Add</button>
|
|
</div>
|
|
|
|
{cutscene.lines.length === 0 && (
|
|
<div className={styles.emptyLines}>No lines. Click + Add.</div>
|
|
)}
|
|
|
|
{cutscene.lines.map((line, i) => (
|
|
<div key={i} className={styles.lineCard}>
|
|
<div className={styles.lineHeader}>
|
|
<span className={styles.lineIndex}>#{i + 1}</span>
|
|
<button className={styles.iconBtn} onClick={() => moveLineUp(i)} disabled={i === 0} title="Move up">↑</button>
|
|
<button className={styles.iconBtn} onClick={() => moveLineDown(i)} disabled={i === cutscene.lines.length - 1} title="Move down">↓</button>
|
|
<button className={styles.iconBtnDanger} onClick={() => removeLine(i)} title="Remove">✕</button>
|
|
</div>
|
|
|
|
<div className={styles.field}>
|
|
<label>Speaker</label>
|
|
<input
|
|
type="text"
|
|
value={line.speaker}
|
|
onChange={e => updateLine(i, { speaker: e.target.value })}
|
|
placeholder="(none)"
|
|
/>
|
|
</div>
|
|
|
|
<div className={styles.field}>
|
|
<label>Text</label>
|
|
<textarea
|
|
value={line.text}
|
|
rows={2}
|
|
onChange={e => updateLine(i, { text: e.target.value })}
|
|
/>
|
|
</div>
|
|
|
|
<div className={styles.row2}>
|
|
<div className={styles.field}>
|
|
<label>Duration (ms)</label>
|
|
<input
|
|
type="number"
|
|
value={line.durationMs}
|
|
min={0}
|
|
onChange={e => updateLine(i, { durationMs: Number(e.target.value) })}
|
|
/>
|
|
</div>
|
|
<div className={styles.field}>
|
|
<label>Wait confirm</label>
|
|
<input
|
|
type="checkbox"
|
|
checked={line.waitForConfirm}
|
|
onChange={e => updateLine(i, { waitForConfirm: e.target.checked })}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className={styles.field}>
|
|
<label>Lua callback</label>
|
|
<input
|
|
type="text"
|
|
value={line.luaCallback}
|
|
onChange={e => updateLine(i, { luaCallback: e.target.value })}
|
|
placeholder="function name"
|
|
/>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|