@sutrata/editor 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -178,15 +178,15 @@ function generateSceneMetadata(ai, sceneText) {
178
178
  * True when the provider can take calls; otherwise points the user at its
179
179
  * setup (if it has one) and returns false.
180
180
  */
181
- async function ensureAIReady(ai, showToast) {
181
+ async function ensureAIReady(ai, showToast, t) {
182
182
  if (await ai.isConfigured())
183
183
  return true;
184
184
  if (ai.openSetup) {
185
- showToast('AI must be set up to generate synopsis & duration. Opening Setup...', 'info');
185
+ showToast(t('navigator.toast.aiSetup'), 'info');
186
186
  ai.openSetup();
187
187
  }
188
188
  else {
189
- showToast(`${ai.displayName} is not available right now.`, 'warn');
189
+ showToast(t('ai.unavailable').replace('{name}', ai.displayName), 'warn');
190
190
  }
191
191
  return false;
192
192
  }
@@ -224,7 +224,7 @@ export function SceneNavigator() {
224
224
  setIsProcessing(true);
225
225
  setProgressText('Generating synopsis & estimating duration...');
226
226
  try {
227
- if (!ai || !(await ensureAIReady(ai, showToast)))
227
+ if (!ai || !(await ensureAIReady(ai, showToast, t)))
228
228
  return;
229
229
  const idx = scenes.findIndex(s => s.index === scene.index);
230
230
  if (idx === -1)
@@ -243,10 +243,10 @@ export function SceneNavigator() {
243
243
  nextText = updateSceneMetadataInText(nextText, scene.textOffset, 'est-duration', duration.trim());
244
244
  setText(nextText);
245
245
  }
246
- showToast(`Generated synopsis & duration for scene ${scene.id || scene.index}.`, 'success');
246
+ showToast(t('navigator.toast.generatedOne').replace('{scene}', String(scene.id || scene.index)), 'success');
247
247
  }
248
248
  catch (e) {
249
- showToast(`Scene metadata generation failed: ${e.message || String(e)}`, 'error');
249
+ showToast(t('navigator.toast.genFailed').replace('{error}', e.message || String(e)), 'error');
250
250
  }
251
251
  finally {
252
252
  setIsProcessing(false);
@@ -257,7 +257,7 @@ export function SceneNavigator() {
257
257
  setIsProcessing(true);
258
258
  setProgressText('Starting batch synopsis & duration generation...');
259
259
  try {
260
- if (!ai || !(await ensureAIReady(ai, showToast)))
260
+ if (!ai || !(await ensureAIReady(ai, showToast, t)))
261
261
  return;
262
262
  const pmView = getEditorView();
263
263
  const cmView = getSourceView();
@@ -265,7 +265,7 @@ export function SceneNavigator() {
265
265
  let currentScenes = buildSceneList(currentText);
266
266
  const missing = currentScenes.filter(s => !s.synopsis || !s.estDuration);
267
267
  if (missing.length === 0) {
268
- showToast('All scenes already have synopsis and duration.', 'info');
268
+ showToast(t('navigator.toast.allHave'), 'info');
269
269
  return;
270
270
  }
271
271
  let current = 0;
@@ -309,10 +309,10 @@ export function SceneNavigator() {
309
309
  console.warn(`Failed scene metadata for scene ${scene.index}:`, err);
310
310
  }
311
311
  }
312
- showToast(`Generated synopsis & duration for ${missing.length} scenes.`, 'success');
312
+ showToast(t('navigator.toast.generatedMany').replace('{count}', String(missing.length)), 'success');
313
313
  }
314
314
  catch (e) {
315
- showToast(`Batch generation failed: ${e.message || String(e)}`, 'error');
315
+ showToast(t('navigator.toast.batchFailed').replace('{error}', e.message || String(e)), 'error');
316
316
  }
317
317
  finally {
318
318
  setIsProcessing(false);
@@ -527,12 +527,12 @@ export function SceneNavigator() {
527
527
  const ids = next.map((n, i) => (n === list[i].id ? undefined : n));
528
528
  const changed = ids.filter(n => n !== undefined).length;
529
529
  if (changed === 0) {
530
- showToast('Scene numbers are already in order.', 'info');
530
+ showToast(t('navigator.toast.inOrder'), 'info');
531
531
  return;
532
532
  }
533
533
  applyTextEdit(setSceneIdsInText(current, ids), setText);
534
- showToast(`Renumbered ${changed} scene${changed === 1 ? '' : 's'}.`, 'success');
535
- }, [liveText, setText, showToast]);
534
+ showToast(t(changed === 1 ? 'navigator.toast.renumberedOne' : 'navigator.toast.renumberedMany').replace('{count}', String(changed)), 'success');
535
+ }, [liveText, setText, showToast, t]);
536
536
  const handleOmitToggle = useCallback((scene) => {
537
537
  const current = liveText();
538
538
  const idx = buildSceneList(current).findIndex(s => s.index === scene.index);
@@ -541,13 +541,13 @@ export function SceneNavigator() {
541
541
  const omitted = scene.status.toLowerCase() === 'omitted';
542
542
  applyTextEdit(omitted ? restoreSceneInText(current, idx) : omitSceneInText(current, idx), setText);
543
543
  }, [liveText, setText]);
544
- const renderSection = (section) => (_jsx("div", { role: "button", tabIndex: 0, className: "cs-nav-item cs-nav-section-anchor", "aria-label": `Jump to section ${section.heading}`, onClick: () => handleSectionJump(section), onKeyDown: (e) => {
544
+ const renderSection = (section) => (_jsx("div", { role: "button", tabIndex: 0, className: "cs-nav-item cs-nav-section-anchor", "aria-label": t('navigator.jumpSection').replace('{heading}', section.heading), onClick: () => handleSectionJump(section), onKeyDown: (e) => {
545
545
  if (e.key === 'Enter' || e.key === ' ') {
546
546
  e.preventDefault();
547
547
  handleSectionJump(section);
548
548
  }
549
549
  }, children: _jsx("div", { className: "cs-nav-scene-info", children: _jsx("div", { className: "cs-nav-heading", children: section.heading || '—' }) }) }, `section:${section.index}`));
550
- return (_jsxs("div", { className: "cs-navigator", role: "navigation", "aria-label": "Scene Navigator", children: [_jsxs("div", { className: "cs-nav-mobile-bar", children: [_jsx("span", { className: "cs-nav-mobile-title", children: t('navigator.title') }), _jsx("button", { type: "button", className: "cs-nav-mobile-close", "aria-label": "Close navigator", onClick: () => setNavVisible(false), children: _jsx(CloseIcon, { size: 18 }) })] }), _jsxs("div", { className: "cs-nav-list", children: [_jsx("button", { type: "button", className: "cs-nav-section-header cs-nav-frontmatter-btn", onClick: handleFrontmatter, title: t('navigator.frontmatter'), children: _jsx("span", { className: "cs-nav-title", children: t('navigator.frontmatter') }) }), _jsxs("div", { className: "cs-nav-section-header cs-nav-scenes-header", style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '8px' }, children: [_jsx("span", { className: "cs-nav-title", children: t('navigator.title') }), _jsx("span", { className: "cs-nav-count", children: scenes.length })] }), _jsxs("div", { style: { display: 'flex', gap: '4px', alignItems: 'center' }, onClick: e => e.stopPropagation(), children: [_jsx("button", { type: "button", className: "cs-nav-action-btn", onClick: () => setShowNavSynopsis(!showNavSynopsis), title: showNavSynopsis ? 'Hide Synopsis & Duration' : 'Show Synopsis & Duration', "aria-label": showNavSynopsis ? 'Hide Synopsis & Duration' : 'Show Synopsis & Duration', children: showNavSynopsis ? _jsx(EyeIcon, { size: 14 }) : _jsx(EyeOffIcon, { size: 14 }) }), ai && (_jsx("button", { type: "button", className: "cs-nav-action-btn", onClick: handleGenerateAllSceneMetadata, disabled: isProcessing, title: "Generate synopsis & estimate duration (all scenes)", "aria-label": "Generate synopsis & estimate duration (all scenes)", style: { opacity: isProcessing ? 0.5 : 1 }, children: _jsx(SparklesIcon, { size: 14 }) })), canEdit && (_jsx("button", { type: "button", className: "cs-nav-action-btn", onClick: handleRenumber, title: t('navigator.renumber'), "aria-label": t('navigator.renumber'), children: _jsx(RenumberIcon, { size: 14 }) }))] })] }), isProcessing && progressText && (_jsxs("div", { className: "cs-nav-progress", style: {
550
+ return (_jsxs("div", { className: "cs-navigator", role: "navigation", "aria-label": t('navigator.aria'), children: [_jsxs("div", { className: "cs-nav-mobile-bar", children: [_jsx("span", { className: "cs-nav-mobile-title", children: t('navigator.title') }), _jsx("button", { type: "button", className: "cs-nav-mobile-close", "aria-label": t('navigator.close'), onClick: () => setNavVisible(false), children: _jsx(CloseIcon, { size: 18 }) })] }), _jsxs("div", { className: "cs-nav-list", children: [_jsx("button", { type: "button", className: "cs-nav-section-header cs-nav-frontmatter-btn", onClick: handleFrontmatter, title: t('navigator.frontmatter'), children: _jsx("span", { className: "cs-nav-title", children: t('navigator.frontmatter') }) }), _jsxs("div", { className: "cs-nav-section-header cs-nav-scenes-header", style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '8px' }, children: [_jsx("span", { className: "cs-nav-title", children: t('navigator.title') }), _jsx("span", { className: "cs-nav-count", children: scenes.length })] }), _jsxs("div", { style: { display: 'flex', gap: '4px', alignItems: 'center' }, onClick: e => e.stopPropagation(), children: [_jsx("button", { type: "button", className: "cs-nav-action-btn", onClick: () => setShowNavSynopsis(!showNavSynopsis), title: showNavSynopsis ? t('navigator.hideSynopsis') : t('navigator.showSynopsis'), "aria-label": showNavSynopsis ? t('navigator.hideSynopsis') : t('navigator.showSynopsis'), children: showNavSynopsis ? _jsx(EyeIcon, { size: 14 }) : _jsx(EyeOffIcon, { size: 14 }) }), ai && (_jsx("button", { type: "button", className: "cs-nav-action-btn", onClick: handleGenerateAllSceneMetadata, disabled: isProcessing, title: t('navigator.genAll'), "aria-label": t('navigator.genAll'), style: { opacity: isProcessing ? 0.5 : 1 }, children: _jsx(SparklesIcon, { size: 14 }) })), canEdit && (_jsx("button", { type: "button", className: "cs-nav-action-btn", onClick: handleRenumber, title: t('navigator.renumber'), "aria-label": t('navigator.renumber'), children: _jsx(RenumberIcon, { size: 14 }) }))] })] }), isProcessing && progressText && (_jsxs("div", { className: "cs-nav-progress", style: {
551
551
  display: 'flex',
552
552
  alignItems: 'center',
553
553
  gap: '8px',
@@ -568,7 +568,7 @@ export function SceneNavigator() {
568
568
  const notes = note ? [note] : [];
569
569
  return (_jsxs(React.Fragment, { children: [leading.map(renderSection), _jsxs("div", {
570
570
  // Ids can repeat until the writer renumbers (§7.4), so the index keeps keys unique.
571
- role: "button", tabIndex: 0, "aria-label": `Jump to ${scene.heading}${notes.length ? ` (${notes.join('; ')})` : ''}`, onKeyDown: (e) => {
571
+ role: "button", tabIndex: 0, "aria-label": t('navigator.jumpScene').replace('{heading}', scene.heading) + (notes.length ? ` (${notes.join('; ')})` : ''), onKeyDown: (e) => {
572
572
  if (e.key === 'Enter' || e.key === ' ') {
573
573
  e.preventDefault();
574
574
  handleJump(scene);
@@ -595,20 +595,20 @@ export function SceneNavigator() {
595
595
  e.stopPropagation();
596
596
  setEditingSceneIdx(idx);
597
597
  setEditVal(scene.synopsis);
598
- }, title: "Click to edit synopsis", children: scene.synopsis })) : (_jsx("div", { className: "cs-nav-scene-synopsis cs-nav-synopsis-placeholder", onClick: e => {
598
+ }, title: t('navigator.editSynopsis'), children: scene.synopsis })) : (_jsx("div", { className: "cs-nav-scene-synopsis cs-nav-synopsis-placeholder", onClick: e => {
599
599
  e.stopPropagation();
600
600
  setEditingSceneIdx(idx);
601
601
  setEditVal('');
602
- }, role: "button", tabIndex: 0, "aria-label": "Add synopsis", onKeyDown: e => {
602
+ }, role: "button", tabIndex: 0, "aria-label": t('navigator.addSynopsis'), onKeyDown: e => {
603
603
  if (e.key === 'Enter' || e.key === ' ') {
604
604
  e.stopPropagation();
605
605
  e.preventDefault();
606
606
  setEditingSceneIdx(idx);
607
607
  setEditVal('');
608
608
  }
609
- }, children: "+ Synopsis" })) }), ai && _jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); void handleSceneMetadata(scene); }, disabled: isProcessing, title: "Generate synopsis & estimate duration", "aria-label": "Generate synopsis & estimate duration", className: "cs-nav-action-btn cs-nav-synopsis-ai-btn", style: { color: 'var(--cs-ui-accent, #c4760a)', ...(isProcessing ? { opacity: 0.5 } : {}) }, children: _jsx(SparklesIcon, { size: 12 }) })] })), (scene.status || (showNavSynopsis && scene.estDuration)) && (_jsxs("div", { style: { display: 'flex', flexWrap: 'wrap', gap: '6px', alignItems: 'center', marginTop: '6px' }, children: [scene.status && (_jsx("span", { className: `cs-nav-status cs-status-${scene.status
609
+ }, children: "+ Synopsis" })) }), ai && _jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); void handleSceneMetadata(scene); }, disabled: isProcessing, title: t('navigator.genOne'), "aria-label": t('navigator.genOne'), className: "cs-nav-action-btn cs-nav-synopsis-ai-btn", style: { color: 'var(--cs-ui-accent, #c4760a)', ...(isProcessing ? { opacity: 0.5 } : {}) }, children: _jsx(SparklesIcon, { size: 12 }) })] })), (scene.status || (showNavSynopsis && scene.estDuration)) && (_jsxs("div", { style: { display: 'flex', flexWrap: 'wrap', gap: '6px', alignItems: 'center', marginTop: '6px' }, children: [scene.status && (_jsx("span", { className: `cs-nav-status cs-status-${scene.status
610
610
  .toLowerCase()
611
- .replace(/\s+/g, '-')}`, children: scene.status })), showNavSynopsis && scene.estDuration && (_jsxs("span", { className: "cs-nav-status", title: "Estimated Onscreen Duration", style: {
611
+ .replace(/\s+/g, '-')}`, children: scene.status })), showNavSynopsis && scene.estDuration && (_jsxs("span", { className: "cs-nav-status", title: t('navigator.duration'), style: {
612
612
  background: 'rgba(217, 119, 6, 0.1)',
613
613
  color: 'var(--cs-ui-accent, #c4760a)',
614
614
  border: '1px solid rgba(217, 119, 6, 0.25)',
@@ -616,7 +616,7 @@ export function SceneNavigator() {
616
616
  }, children: ["\u23F1 ", scene.estDuration] }))] }))] }), _jsxs("div", { className: "cs-nav-scene-side", children: [scene.id ? (_jsx("div", { className: `cs-nav-scene-num${issue ? ' cs-nav-num-issue' : ''}`, title: issue ? issueLabel(issue) : undefined, children: scene.id })) : (_jsx("div", { className: "cs-nav-missing-id-icon", title: issue ? issueLabel(issue) : t('navigator.missingId'), "aria-hidden": "true", children: "\u26A0" })), canEdit && (_jsx("button", { type: "button", className: "cs-nav-action-btn cs-nav-omit-btn", title: omitted ? t('navigator.restoreScene') : t('navigator.omitScene'), "aria-label": `${omitted ? t('navigator.restoreScene') : t('navigator.omitScene')}: ${scene.heading}`, onClick: e => { e.stopPropagation(); handleOmitToggle(scene); }, children: omitted ? _jsx(RestoreIcon, { size: 12 }) : _jsx(OmitIcon, { size: 12 }) }))] })] })] }, `${idx}:${scene.id ?? ''}`));
617
617
  }), sections
618
618
  .filter(sec => scenes.length === 0 || sec.textOffset > scenes[scenes.length - 1].textOffset)
619
- .map(renderSection), scenes.length === 0 && sections.length === 0 && (_jsx("div", { className: "cs-nav-empty", children: t('navigator.empty') })), _jsx("div", { style: { marginTop: '20px', padding: '0 8px 10px', borderTop: '1px solid #eee', paddingTop: '15px' }, children: _jsxs("button", { type: "button", className: "cs-nav-section-header cs-nav-frontmatter-btn", onClick: () => setStatsOpen(true), style: { width: '100%', textAlign: 'left', display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }, children: [_jsx(StatsIcon, { size: 14 }), " ", _jsx("span", { className: "cs-nav-title", children: "Statistics" })] }) })] }), statsOpen && _jsx(StatisticsDialog, { onClose: () => setStatsOpen(false) })] }));
619
+ .map(renderSection), scenes.length === 0 && sections.length === 0 && (_jsx("div", { className: "cs-nav-empty", children: t('navigator.empty') })), _jsx("div", { style: { marginTop: '20px', padding: '0 8px 10px', borderTop: '1px solid #eee', paddingTop: '15px' }, children: _jsxs("button", { type: "button", className: "cs-nav-section-header cs-nav-frontmatter-btn", onClick: () => setStatsOpen(true), style: { width: '100%', textAlign: 'left', display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }, children: [_jsx(StatsIcon, { size: 14 }), " ", _jsx("span", { className: "cs-nav-title", children: t('navigator.stats') })] }) })] }), statsOpen && _jsx(StatisticsDialog, { onClose: () => setStatsOpen(false) })] }));
620
620
  }
621
621
  /**
622
622
  * Reorder scene blocks in the source text.
@@ -4,8 +4,10 @@ import { extractWorkflowData } from '../file/workflow-reports';
4
4
  import { getEditorView } from '../editor/editor-bus';
5
5
  import { schema } from '../editor/schema';
6
6
  import { useCanEdit } from '../extensions/session';
7
+ import { useTranslation } from '../i18n/useTranslation';
7
8
  export function StatisticsDialog({ onClose }) {
8
9
  const { ast, text, setText } = useDocument();
10
+ const { t } = useTranslation();
9
11
  const canEdit = useCanEdit();
10
12
  const { scenes, characters } = extractWorkflowData(ast);
11
13
  const handleUpdateActor = (charName, actorName) => {
@@ -82,13 +84,13 @@ export function StatisticsDialog({ onClose }) {
82
84
  return (_jsx("div", { className: "cs-dialog-overlay", role: "presentation", onClick: e => {
83
85
  if (e.target === e.currentTarget)
84
86
  onClose();
85
- }, children: _jsxs("div", { className: "cs-dialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "cs-stats-dialog-title", style: { maxWidth: '640px', width: '92%', maxHeight: '85vh', display: 'flex', flexDirection: 'column' }, children: [_jsxs("div", { className: "cs-panel-header", children: [_jsx("span", { id: "cs-stats-dialog-title", className: "cs-panel-title", children: "Script Statistics" }), _jsx("button", { className: "cs-panel-close", onClick: onClose, "aria-label": "Close", children: "\u00D7" })] }), _jsxs("div", { className: "cs-panel-body", style: { overflowY: 'auto', flex: 1, padding: '20px' }, children: [_jsxs("div", { className: "cs-stats-summary-grid", children: [_jsxs("div", { className: "cs-stats-card", children: [_jsx("div", { className: "cs-stats-card-val", children: scenes.length }), _jsx("div", { className: "cs-stats-card-lbl", children: "Scenes" })] }), _jsxs("div", { className: "cs-stats-card", children: [_jsx("div", { className: "cs-stats-card-val", children: wordCount.toLocaleString() }), _jsx("div", { className: "cs-stats-card-lbl", children: "Words" })] }), _jsxs("div", { className: "cs-stats-card", children: [_jsx("div", { className: "cs-stats-card-val", children: totalDurationLabel }), _jsx("div", { className: "cs-stats-card-lbl", children: "Est. Duration" })] })] }), _jsxs("div", { className: "cs-stats-section-hdr", children: ["Cast List & Occurrences (", characters.length, ")"] }), characters.length === 0 ? (_jsx("div", { style: { color: 'var(--cs-ui-text-light-muted, #8c897f)', fontStyle: 'italic', fontSize: '12px' }, children: "No character cues detected in the screenplay yet." })) : (_jsxs("table", { className: "cs-stats-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { style: { textAlign: 'left', width: '35%' }, children: "Character" }), _jsx("th", { style: { textAlign: 'left' }, children: "Actor" }), _jsx("th", { style: { textAlign: 'center', width: '15%' }, children: "Scenes" })] }) }), _jsx("tbody", { children: characters.map((c, i) => (_jsxs("tr", { children: [_jsx("td", { style: { fontWeight: '600' }, children: c.name }), _jsx("td", { children: _jsx("input", { type: "text", className: "cs-stats-actor-input", value: c.actor, onChange: (e) => handleUpdateActor(c.name, e.target.value), readOnly: !canEdit, placeholder: "Assign actor...", "aria-label": `Actor for ${c.name}` }) }), _jsx("td", { style: { textAlign: 'center', fontWeight: '500' }, children: c.sceneCount })] }, i))) })] }))] }), _jsx("div", { style: {
87
+ }, children: _jsxs("div", { className: "cs-dialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "cs-stats-dialog-title", style: { maxWidth: '640px', width: '92%', maxHeight: '85vh', display: 'flex', flexDirection: 'column' }, children: [_jsxs("div", { className: "cs-panel-header", children: [_jsx("span", { id: "cs-stats-dialog-title", className: "cs-panel-title", children: t('stats.title') }), _jsx("button", { className: "cs-panel-close", onClick: onClose, "aria-label": t('common.close'), children: "\u00D7" })] }), _jsxs("div", { className: "cs-panel-body", style: { overflowY: 'auto', flex: 1, padding: '20px' }, children: [_jsxs("div", { className: "cs-stats-summary-grid", children: [_jsxs("div", { className: "cs-stats-card", children: [_jsx("div", { className: "cs-stats-card-val", children: scenes.length }), _jsx("div", { className: "cs-stats-card-lbl", children: t('stats.scenes') })] }), _jsxs("div", { className: "cs-stats-card", children: [_jsx("div", { className: "cs-stats-card-val", children: wordCount.toLocaleString() }), _jsx("div", { className: "cs-stats-card-lbl", children: t('stats.words') })] }), _jsxs("div", { className: "cs-stats-card", children: [_jsx("div", { className: "cs-stats-card-val", children: totalDurationLabel }), _jsx("div", { className: "cs-stats-card-lbl", children: t('stats.duration') })] })] }), _jsxs("div", { className: "cs-stats-section-hdr", children: [t('stats.cast'), " (", characters.length, ")"] }), characters.length === 0 ? (_jsx("div", { style: { color: 'var(--cs-ui-text-light-muted, #8c897f)', fontStyle: 'italic', fontSize: '12px' }, children: t('stats.noCharacters') })) : (_jsxs("table", { className: "cs-stats-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { style: { textAlign: 'left', width: '35%' }, children: t('stats.character') }), _jsx("th", { style: { textAlign: 'left' }, children: t('stats.actor') }), _jsx("th", { style: { textAlign: 'center', width: '15%' }, children: t('stats.scenes') })] }) }), _jsx("tbody", { children: characters.map((c, i) => (_jsxs("tr", { children: [_jsx("td", { style: { fontWeight: '600' }, children: c.name }), _jsx("td", { children: _jsx("input", { type: "text", className: "cs-stats-actor-input", value: c.actor, onChange: (e) => handleUpdateActor(c.name, e.target.value), readOnly: !canEdit, placeholder: t('stats.assignActor'), "aria-label": t('stats.actorFor').replace('{name}', c.name) }) }), _jsx("td", { style: { textAlign: 'center', fontWeight: '500' }, children: c.sceneCount })] }, i))) })] }))] }), _jsx("div", { style: {
86
88
  borderTop: '1px solid var(--cs-ui-border-light-subtle, #eeece4)',
87
89
  display: 'flex',
88
90
  justifyContent: 'flex-end',
89
91
  padding: '12px 20px',
90
92
  background: 'var(--cs-ui-bg-panel-subtle, #f4f2ea)',
91
- }, children: _jsx("button", { type: "button", className: "cs-confirm-btn-primary", onClick: onClose, children: "Close" }) })] }) }));
93
+ }, children: _jsx("button", { type: "button", className: "cs-confirm-btn-primary", onClick: onClose, children: t('common.close') }) })] }) }));
92
94
  }
93
95
  function updateActorInText(text, charName, actorName) {
94
96
  const lines = text.split('\n');
@@ -48,8 +48,8 @@ export function AppBar() {
48
48
  }
49
49
  }
50
50
  catch (err) {
51
- showToast(err instanceof Error ? err.message : 'Failed to import the Word document.', 'error');
51
+ showToast(err instanceof Error ? err.message : t('appbar.importFailed'), 'error');
52
52
  }
53
53
  };
54
- return (_jsxs("header", { className: "cs-app-bar", role: "banner", "aria-label": "Application header", children: [_jsxs("div", { className: "cs-ab-group cs-ab-left", children: [_jsxs("div", { className: "cs-ab-brand", title: "Sutrata Multilingual Screenplay Editor", children: [_jsx(SutrataLogo, { size: 20, showText: false }), _jsx("span", { className: "cs-ab-brand-title", children: "Sutrata" })] }), _jsx("span", { className: "cs-ab-sep cs-ab-desktop-only" }), _jsx(AbBtn, { label: t('appbar.navigator'), shortcut: "Ctrl+Shift+B", active: navVisible, onClick: () => setNavVisible(!navVisible), children: _jsx(NavigatorIcon, { size: 18 }) }), canEdit && (_jsx("span", { className: "cs-ab-desktop-only", children: _jsx(AbBtn, { label: t('appbar.titlePage'), onClick: openTitlePageForm, children: _jsx(TitlePageIcon, { size: 18 }) }) })), _jsxs("div", { className: "cs-ab-file-pill", title: name, children: [_jsx("span", { className: "cs-ab-file-name", children: name }), isDirty && _jsx("span", { className: "cs-ab-dirty", title: "Unsaved edits" })] })] }), _jsx("span", { className: "cs-ab-spacer cs-ab-desktop-only" }), _jsxs("div", { className: "cs-ab-group cs-ab-desktop-group", children: [_jsx(AbBtn, { label: mode === 'formatted' ? t('appbar.switchToSource') : t('appbar.switchToFormatted'), shortcut: "Ctrl+Shift+E", onClick: () => setMode(nextMode), children: mode === 'formatted' ? _jsx(SourceIcon, { size: 18 }) : _jsx(FormattedIcon, { size: 18 }) }), canEdit && (_jsx(AbBtn, { label: ribbonVisible ? t('appbar.hideToolbar') : t('appbar.showToolbar'), shortcut: "Ctrl+Shift+H", active: ribbonVisible, onClick: () => setRibbonVisible(!ribbonVisible), children: _jsx(RibbonIcon, { size: 18 }) })), speech && (_jsx(AbBtn, { label: "Voice Dictation", shortcut: "Ctrl+Shift+V", active: voiceActive, onClick: () => setVoiceActive(!voiceActive), children: _jsx(MicIcon, { size: 18 }) })), _jsx(AbBtn, { label: metadataVisible ? 'Hide Metadata' : 'Show Metadata', active: metadataVisible, onClick: () => setMetadataVisible(!metadataVisible), children: _jsx("span", { style: { fontWeight: 700, fontSize: '13px' }, children: "&" }) })] }), _jsx("span", { className: "cs-ab-spacer cs-ab-desktop-only" }), _jsxs("div", { className: "cs-ab-group cs-ab-desktop-group", children: [canEdit && (_jsxs(_Fragment, { children: [localFiles && (_jsxs(_Fragment, { children: [_jsx(AbBtn, { label: t('appbar.new'), onClick: handleNew, children: _jsx(NewIcon, { size: 18 }) }), _jsx(AbBtn, { label: t('appbar.open'), shortcut: "Ctrl+O", onClick: () => { void openFile(); }, children: _jsx(OpenIcon, { size: 18 }) })] })), _jsx(AbBtn, { label: t('appbar.importDocx'), onClick: () => { void handleImportDocx(); }, children: _jsx(ImportDocxIcon, { size: 18 }) }), ai && (_jsx(AbBtn, { label: "Import with AI", onClick: () => setAiImportOpen(true), children: _jsx(SparklesIcon, { size: 18 }) })), _jsx("span", { className: "cs-ab-sep" }), _jsx(AbBtn, { label: t('appbar.save'), shortcut: "Ctrl+S", variant: "primary", onClick: () => { void saveFile(); }, children: _jsx(SaveIcon, { size: 17 }) }), localFiles && (_jsx(AbBtn, { label: t('appbar.saveAs'), shortcut: "Ctrl+Shift+S", onClick: () => { void saveFileAs(); }, children: _jsx(SaveAsIcon, { size: 17 }) })), _jsx("span", { className: "cs-ab-sep" })] })), _jsx(AbBtn, { label: t('appbar.export'), variant: "secondary", active: exportVisible, onClick: () => setExportVisible(!exportVisible), children: _jsx(ExportIcon, { size: 18 }) }), canEdit && (_jsx(AbBtn, { label: t('appbar.style'), active: styleVisible, onClick: () => setStyleVisible(!styleVisible), children: _jsx(StyleIcon, { size: 18 }) })), (appbarCommands.length > 0 || appbarPanels.length > 0) && _jsx("span", { className: "cs-ab-sep" }), appbarCommands.map(c => (_jsx(AbBtn, { label: c.label, shortcut: displayShortcut(c.shortcut ?? c.shortcutHint), onClick: () => c.run(commandContext()), children: c.icon ? c.icon(18) : _jsx("span", { className: "cs-ab-text-label", children: c.label }) }, c.id))), appbarPanels.map(p => (_jsx("span", { className: "cs-ab-panel", "aria-label": p.title, children: p.render(panelContext) }, p.id))), _jsx(AbBtn, { label: t('settings.title'), active: settingsVisible, onClick: () => setSettingsVisible(!settingsVisible), children: _jsx(SettingsIcon, { size: 18 }) })] }), _jsxs("div", { className: "cs-ab-group cs-ab-mobile-controls", children: [appbarPanels.map(p => (_jsx("span", { className: "cs-ab-panel", "aria-label": p.title, children: p.render(panelContext) }, p.id))), canEdit && (_jsx(AbBtn, { label: t('appbar.save'), variant: isDirty ? 'primary' : undefined, onClick: () => { void saveFile(); }, children: _jsx(SaveIcon, { size: 17 }) })), _jsx(AbBtn, { label: "Menu", active: mobileMenuOpen, onClick: () => setMobileMenuOpen(!mobileMenuOpen), children: mobileMenuOpen ? _jsx(CloseIcon, { size: 18 }) : _jsx(MenuIcon, { size: 18 }) })] }), mobileMenuOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "cs-mobile-menu-backdrop", "aria-hidden": "true", onClick: () => setMobileMenuOpen(false) }), _jsxs("div", { className: "cs-mobile-menu-drawer", role: "dialog", "aria-modal": "true", "aria-label": "Application Menu", children: [_jsxs("div", { className: "cs-mm-header", children: [_jsxs("div", { className: "cs-mm-brand", children: [_jsx(SutrataLogo, { size: 22, showText: false }), _jsx("span", { children: "Sutrata" })] }), _jsx("button", { type: "button", className: "cs-mm-close", "aria-label": "Close menu", onClick: () => setMobileMenuOpen(false), children: _jsx(CloseIcon, { size: 18 }) })] }), _jsxs("div", { className: "cs-mm-file-info", children: [_jsx("span", { className: "cs-mm-file-name", children: name }), isDirty && _jsx("span", { className: "cs-ab-dirty", title: "Unsaved edits" })] }), _jsxs("div", { className: "cs-mm-section", children: [_jsx("div", { className: "cs-mm-section-title", children: "Views & Tools" }), _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { setMode(nextMode); setMobileMenuOpen(false); }, children: [mode === 'formatted' ? _jsx(SourceIcon, { size: 18 }) : _jsx(FormattedIcon, { size: 18 }), _jsx("span", { children: mode === 'formatted' ? t('appbar.switchToSource') : t('appbar.switchToFormatted') })] }), _jsxs("button", { type: "button", className: `cs-mm-item${navVisible ? ' cs-mm-active' : ''}`, onClick: () => { setNavVisible(!navVisible); setMobileMenuOpen(false); }, children: [_jsx(NavigatorIcon, { size: 18 }), _jsx("span", { children: t('appbar.navigator') })] }), canEdit && _jsxs("button", { type: "button", className: `cs-mm-item${ribbonVisible ? ' cs-mm-active' : ''}`, onClick: () => { setRibbonVisible(!ribbonVisible); setMobileMenuOpen(false); }, children: [_jsx(RibbonIcon, { size: 18 }), _jsx("span", { children: ribbonVisible ? t('appbar.hideToolbar') : t('appbar.showToolbar') })] }), speech && _jsxs("button", { type: "button", className: `cs-mm-item${voiceActive ? ' cs-mm-active' : ''}`, onClick: () => { setVoiceActive(!voiceActive); setMobileMenuOpen(false); }, children: [_jsx(MicIcon, { size: 18 }), _jsx("span", { children: "Voice Dictation" })] }), _jsxs("button", { type: "button", className: `cs-mm-item${metadataVisible ? ' cs-mm-active' : ''}`, onClick: () => { setMetadataVisible(!metadataVisible); setMobileMenuOpen(false); }, children: [_jsx("span", { style: { fontWeight: 700, width: 18, textAlign: 'center' }, children: "&" }), _jsx("span", { children: metadataVisible ? 'Hide Metadata' : 'Show Metadata' })] })] }), canEdit && _jsxs("div", { className: "cs-mm-section", children: [_jsx("div", { className: "cs-mm-section-title", children: "File Operations" }), _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { openTitlePageForm(); setMobileMenuOpen(false); }, children: [_jsx(TitlePageIcon, { size: 18 }), _jsx("span", { children: t('appbar.titlePage') })] }), localFiles && _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { handleNew(); setMobileMenuOpen(false); }, children: [_jsx(NewIcon, { size: 18 }), _jsx("span", { children: t('appbar.new') })] }), localFiles && _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { void openFile(); setMobileMenuOpen(false); }, children: [_jsx(OpenIcon, { size: 18 }), _jsx("span", { children: t('appbar.open') })] }), _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { void handleImportDocx(); setMobileMenuOpen(false); }, children: [_jsx(ImportDocxIcon, { size: 18 }), _jsx("span", { children: t('appbar.importDocx') })] }), ai && _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { setAiImportOpen(true); setMobileMenuOpen(false); }, children: [_jsx(SparklesIcon, { size: 18 }), _jsx("span", { children: "Import with AI" })] }), _jsxs("button", { type: "button", className: "cs-mm-item cs-mm-primary", onClick: () => { void saveFile(); setMobileMenuOpen(false); }, children: [_jsx(SaveIcon, { size: 18 }), _jsx("span", { children: t('appbar.save') })] }), localFiles && _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { void saveFileAs(); setMobileMenuOpen(false); }, children: [_jsx(SaveAsIcon, { size: 18 }), _jsx("span", { children: t('appbar.saveAs') })] }), _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { setExportVisible(true); setMobileMenuOpen(false); }, children: [_jsx(ExportIcon, { size: 18 }), _jsx("span", { children: t('appbar.export') })] })] }), !canEdit && _jsx("div", { className: "cs-mm-section", children: _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { setExportVisible(true); setMobileMenuOpen(false); }, children: [_jsx(ExportIcon, { size: 18 }), _jsx("span", { children: t('appbar.export') })] }) }), appbarCommands.length > 0 && _jsx("div", { className: "cs-mm-section", children: appbarCommands.map(c => (_jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { c.run(commandContext()); setMobileMenuOpen(false); }, children: [c.icon?.(18), _jsx("span", { children: c.label })] }, c.id))) }), _jsxs("div", { className: "cs-mm-section", children: [_jsx("div", { className: "cs-mm-section-title", children: "Preferences" }), canEdit && _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { setStyleVisible(true); setMobileMenuOpen(false); }, children: [_jsx(StyleIcon, { size: 18 }), _jsx("span", { children: t('appbar.style') })] }), _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { setSettingsVisible(true); setMobileMenuOpen(false); }, children: [_jsx(SettingsIcon, { size: 18 }), _jsx("span", { children: t('settings.title') })] })] })] })] })), aiImportOpen && _jsx(AIImportDialog, { onClose: () => setAiImportOpen(false) })] }));
54
+ return (_jsxs("header", { className: "cs-app-bar", role: "banner", "aria-label": t('appbar.header'), children: [_jsxs("div", { className: "cs-ab-group cs-ab-left", children: [_jsxs("div", { className: "cs-ab-brand", title: t('appbar.brandTitle'), children: [_jsx(SutrataLogo, { size: 20, showText: false }), _jsx("span", { className: "cs-ab-brand-title", children: "Sutrata" })] }), _jsx("span", { className: "cs-ab-sep cs-ab-desktop-only" }), _jsx(AbBtn, { label: t('appbar.navigator'), shortcut: "Ctrl+Shift+B", active: navVisible, onClick: () => setNavVisible(!navVisible), children: _jsx(NavigatorIcon, { size: 18 }) }), canEdit && (_jsx("span", { className: "cs-ab-desktop-only", children: _jsx(AbBtn, { label: t('appbar.titlePage'), onClick: openTitlePageForm, children: _jsx(TitlePageIcon, { size: 18 }) }) })), _jsxs("div", { className: "cs-ab-file-pill", title: name, children: [_jsx("span", { className: "cs-ab-file-name", children: name }), isDirty && _jsx("span", { className: "cs-ab-dirty", title: t('appbar.unsaved') })] })] }), _jsx("span", { className: "cs-ab-spacer cs-ab-desktop-only" }), _jsxs("div", { className: "cs-ab-group cs-ab-desktop-group", children: [_jsx(AbBtn, { label: mode === 'formatted' ? t('appbar.switchToSource') : t('appbar.switchToFormatted'), shortcut: "Ctrl+Shift+E", onClick: () => setMode(nextMode), children: mode === 'formatted' ? _jsx(SourceIcon, { size: 18 }) : _jsx(FormattedIcon, { size: 18 }) }), canEdit && (_jsx(AbBtn, { label: ribbonVisible ? t('appbar.hideToolbar') : t('appbar.showToolbar'), shortcut: "Ctrl+Shift+H", active: ribbonVisible, onClick: () => setRibbonVisible(!ribbonVisible), children: _jsx(RibbonIcon, { size: 18 }) })), speech && (_jsx(AbBtn, { label: t('appbar.voice'), shortcut: "Ctrl+Shift+V", active: voiceActive, onClick: () => setVoiceActive(!voiceActive), children: _jsx(MicIcon, { size: 18 }) })), _jsx(AbBtn, { label: metadataVisible ? t('appbar.hideMetadata') : t('appbar.showMetadata'), active: metadataVisible, onClick: () => setMetadataVisible(!metadataVisible), children: _jsx("span", { style: { fontWeight: 700, fontSize: '13px' }, children: "&" }) })] }), _jsx("span", { className: "cs-ab-spacer cs-ab-desktop-only" }), _jsxs("div", { className: "cs-ab-group cs-ab-desktop-group", children: [canEdit && (_jsxs(_Fragment, { children: [localFiles && (_jsxs(_Fragment, { children: [_jsx(AbBtn, { label: t('appbar.new'), onClick: handleNew, children: _jsx(NewIcon, { size: 18 }) }), _jsx(AbBtn, { label: t('appbar.open'), shortcut: "Ctrl+O", onClick: () => { void openFile(); }, children: _jsx(OpenIcon, { size: 18 }) })] })), _jsx(AbBtn, { label: t('appbar.importDocx'), onClick: () => { void handleImportDocx(); }, children: _jsx(ImportDocxIcon, { size: 18 }) }), ai && (_jsx(AbBtn, { label: t('appbar.importAI'), onClick: () => setAiImportOpen(true), children: _jsx(SparklesIcon, { size: 18 }) })), _jsx("span", { className: "cs-ab-sep" }), _jsx(AbBtn, { label: t('appbar.save'), shortcut: "Ctrl+S", variant: "primary", onClick: () => { void saveFile(); }, children: _jsx(SaveIcon, { size: 17 }) }), localFiles && (_jsx(AbBtn, { label: t('appbar.saveAs'), shortcut: "Ctrl+Shift+S", onClick: () => { void saveFileAs(); }, children: _jsx(SaveAsIcon, { size: 17 }) })), _jsx("span", { className: "cs-ab-sep" })] })), _jsx(AbBtn, { label: t('appbar.export'), variant: "secondary", active: exportVisible, onClick: () => setExportVisible(!exportVisible), children: _jsx(ExportIcon, { size: 18 }) }), canEdit && (_jsx(AbBtn, { label: t('appbar.style'), active: styleVisible, onClick: () => setStyleVisible(!styleVisible), children: _jsx(StyleIcon, { size: 18 }) })), (appbarCommands.length > 0 || appbarPanels.length > 0) && _jsx("span", { className: "cs-ab-sep" }), appbarCommands.map(c => (_jsx(AbBtn, { label: c.label, shortcut: displayShortcut(c.shortcut ?? c.shortcutHint), onClick: () => c.run(commandContext()), children: c.icon ? c.icon(18) : _jsx("span", { className: "cs-ab-text-label", children: c.label }) }, c.id))), appbarPanels.map(p => (_jsx("span", { className: "cs-ab-panel", "aria-label": p.title, children: p.render(panelContext) }, p.id))), _jsx(AbBtn, { label: t('settings.title'), active: settingsVisible, onClick: () => setSettingsVisible(!settingsVisible), children: _jsx(SettingsIcon, { size: 18 }) })] }), _jsxs("div", { className: "cs-ab-group cs-ab-mobile-controls", children: [appbarPanels.map(p => (_jsx("span", { className: "cs-ab-panel", "aria-label": p.title, children: p.render(panelContext) }, p.id))), canEdit && (_jsx(AbBtn, { label: t('appbar.save'), variant: isDirty ? 'primary' : undefined, onClick: () => { void saveFile(); }, children: _jsx(SaveIcon, { size: 17 }) })), _jsx(AbBtn, { label: t('appbar.menu'), active: mobileMenuOpen, onClick: () => setMobileMenuOpen(!mobileMenuOpen), children: mobileMenuOpen ? _jsx(CloseIcon, { size: 18 }) : _jsx(MenuIcon, { size: 18 }) })] }), mobileMenuOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "cs-mobile-menu-backdrop", "aria-hidden": "true", onClick: () => setMobileMenuOpen(false) }), _jsxs("div", { className: "cs-mobile-menu-drawer", role: "dialog", "aria-modal": "true", "aria-label": t('appbar.appMenu'), children: [_jsxs("div", { className: "cs-mm-header", children: [_jsxs("div", { className: "cs-mm-brand", children: [_jsx(SutrataLogo, { size: 22, showText: false }), _jsx("span", { children: "Sutrata" })] }), _jsx("button", { type: "button", className: "cs-mm-close", "aria-label": t('appbar.closeMenu'), onClick: () => setMobileMenuOpen(false), children: _jsx(CloseIcon, { size: 18 }) })] }), _jsxs("div", { className: "cs-mm-file-info", children: [_jsx("span", { className: "cs-mm-file-name", children: name }), isDirty && _jsx("span", { className: "cs-ab-dirty", title: t('appbar.unsaved') })] }), _jsxs("div", { className: "cs-mm-section", children: [_jsx("div", { className: "cs-mm-section-title", children: t('appbar.viewsTools') }), _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { setMode(nextMode); setMobileMenuOpen(false); }, children: [mode === 'formatted' ? _jsx(SourceIcon, { size: 18 }) : _jsx(FormattedIcon, { size: 18 }), _jsx("span", { children: mode === 'formatted' ? t('appbar.switchToSource') : t('appbar.switchToFormatted') })] }), _jsxs("button", { type: "button", className: `cs-mm-item${navVisible ? ' cs-mm-active' : ''}`, onClick: () => { setNavVisible(!navVisible); setMobileMenuOpen(false); }, children: [_jsx(NavigatorIcon, { size: 18 }), _jsx("span", { children: t('appbar.navigator') })] }), canEdit && _jsxs("button", { type: "button", className: `cs-mm-item${ribbonVisible ? ' cs-mm-active' : ''}`, onClick: () => { setRibbonVisible(!ribbonVisible); setMobileMenuOpen(false); }, children: [_jsx(RibbonIcon, { size: 18 }), _jsx("span", { children: ribbonVisible ? t('appbar.hideToolbar') : t('appbar.showToolbar') })] }), speech && _jsxs("button", { type: "button", className: `cs-mm-item${voiceActive ? ' cs-mm-active' : ''}`, onClick: () => { setVoiceActive(!voiceActive); setMobileMenuOpen(false); }, children: [_jsx(MicIcon, { size: 18 }), _jsx("span", { children: t('appbar.voice') })] }), _jsxs("button", { type: "button", className: `cs-mm-item${metadataVisible ? ' cs-mm-active' : ''}`, onClick: () => { setMetadataVisible(!metadataVisible); setMobileMenuOpen(false); }, children: [_jsx("span", { style: { fontWeight: 700, width: 18, textAlign: 'center' }, children: "&" }), _jsx("span", { children: metadataVisible ? t('appbar.hideMetadata') : t('appbar.showMetadata') })] })] }), canEdit && _jsxs("div", { className: "cs-mm-section", children: [_jsx("div", { className: "cs-mm-section-title", children: t('appbar.fileOps') }), _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { openTitlePageForm(); setMobileMenuOpen(false); }, children: [_jsx(TitlePageIcon, { size: 18 }), _jsx("span", { children: t('appbar.titlePage') })] }), localFiles && _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { handleNew(); setMobileMenuOpen(false); }, children: [_jsx(NewIcon, { size: 18 }), _jsx("span", { children: t('appbar.new') })] }), localFiles && _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { void openFile(); setMobileMenuOpen(false); }, children: [_jsx(OpenIcon, { size: 18 }), _jsx("span", { children: t('appbar.open') })] }), _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { void handleImportDocx(); setMobileMenuOpen(false); }, children: [_jsx(ImportDocxIcon, { size: 18 }), _jsx("span", { children: t('appbar.importDocx') })] }), ai && _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { setAiImportOpen(true); setMobileMenuOpen(false); }, children: [_jsx(SparklesIcon, { size: 18 }), _jsx("span", { children: t('appbar.importAI') })] }), _jsxs("button", { type: "button", className: "cs-mm-item cs-mm-primary", onClick: () => { void saveFile(); setMobileMenuOpen(false); }, children: [_jsx(SaveIcon, { size: 18 }), _jsx("span", { children: t('appbar.save') })] }), localFiles && _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { void saveFileAs(); setMobileMenuOpen(false); }, children: [_jsx(SaveAsIcon, { size: 18 }), _jsx("span", { children: t('appbar.saveAs') })] }), _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { setExportVisible(true); setMobileMenuOpen(false); }, children: [_jsx(ExportIcon, { size: 18 }), _jsx("span", { children: t('appbar.export') })] })] }), !canEdit && _jsx("div", { className: "cs-mm-section", children: _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { setExportVisible(true); setMobileMenuOpen(false); }, children: [_jsx(ExportIcon, { size: 18 }), _jsx("span", { children: t('appbar.export') })] }) }), appbarCommands.length > 0 && _jsx("div", { className: "cs-mm-section", children: appbarCommands.map(c => (_jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { c.run(commandContext()); setMobileMenuOpen(false); }, children: [c.icon?.(18), _jsx("span", { children: c.label })] }, c.id))) }), _jsxs("div", { className: "cs-mm-section", children: [_jsx("div", { className: "cs-mm-section-title", children: t('appbar.preferences') }), canEdit && _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { setStyleVisible(true); setMobileMenuOpen(false); }, children: [_jsx(StyleIcon, { size: 18 }), _jsx("span", { children: t('appbar.style') })] }), _jsxs("button", { type: "button", className: "cs-mm-item", onClick: () => { setSettingsVisible(true); setMobileMenuOpen(false); }, children: [_jsx(SettingsIcon, { size: 18 }), _jsx("span", { children: t('settings.title') })] })] })] })] })), aiImportOpen && _jsx(AIImportDialog, { onClose: () => setAiImportOpen(false) })] }));
55
55
  }
@@ -40,5 +40,5 @@ export function AppShell() {
40
40
  window.addEventListener(OPEN_TITLE_PAGE_EVENT, open);
41
41
  return () => window.removeEventListener(OPEN_TITLE_PAGE_EVENT, open);
42
42
  }, []);
43
- return (_jsxs("div", { className: `cs-shell ${metadataVisible ? '' : 'cs-hide-metadata'}`, children: [_jsx(Watermark, {}), _jsx(AppBar, {}), voiceActive && speech && _jsx(VoiceToolbar, {}), ribbonVisible && canEdit && _jsx(ElementToolbar, {}), _jsxs("div", { className: "cs-shell-body", children: [navVisible && (_jsxs(_Fragment, { children: [_jsx("div", { className: "cs-nav-backdrop", "aria-hidden": "true", onClick: () => setNavVisible(false) }), sidebarPanels.length === 0 ? (_jsx(SceneNavigator, {})) : (_jsxs("div", { className: "cs-sidebar", children: [_jsx("div", { className: "cs-sidebar-tabs", role: "tablist", "aria-label": "Sidebar", children: [{ id: 'navigator', title: t('navigator.title') }, ...sidebarPanels].map(p => (_jsx("button", { type: "button", role: "tab", "aria-selected": (activeSidebar?.id ?? 'navigator') === p.id, className: `cs-sidebar-tab${(activeSidebar?.id ?? 'navigator') === p.id ? ' cs-sidebar-tab-active' : ''}`, onClick: () => setSidebarTab(p.id), children: p.title }, p.id))) }), activeSidebar ? (_jsx("div", { className: "cs-sidebar-panel", role: "tabpanel", "aria-label": activeSidebar.title, children: activeSidebar.render(panelContext) })) : (_jsx(SceneNavigator, {}))] }))] })), _jsx("div", { className: "cs-editor-area", children: _jsx("main", { className: "cs-main", children: mode === 'formatted' ? _jsx(EditorView, {}) : _jsx(SourceView, {}) }) }), inspectorPanels.length > 0 && (_jsx("aside", { className: "cs-inspector", "aria-label": "Inspector", children: inspectorPanels.map(p => (_jsxs("section", { className: "cs-inspector-section", "aria-label": p.title, children: [_jsx("div", { className: "cs-inspector-title", children: p.title }), p.render(panelContext)] }, p.id))) }))] }), _jsx(StatusBar, {}), _jsx(FindReplace, {}), exportVisible && _jsx(ExportDialog, {}), titlePageVisible && canEdit && _jsx(TitlePageDialog, { onClose: () => setTitlePageVisible(false) }), settingsVisible && _jsx(SettingsDialog, { onClose: () => setSettingsVisible(false) }), styleVisible && canEdit && _jsx(StyleDialog, { onClose: () => setStyleVisible(false) }), confirmModal && _jsx(ConfirmDialog, { options: confirmModal, onClose: closeConfirm }), _jsx(ToastContainer, { toasts: toasts, onDismiss: dismissToast }), canEdit && _jsx(SelectionMenu, {})] }));
43
+ return (_jsxs("div", { className: `cs-shell ${metadataVisible ? '' : 'cs-hide-metadata'}`, children: [_jsx(Watermark, {}), _jsx(AppBar, {}), voiceActive && speech && _jsx(VoiceToolbar, {}), ribbonVisible && canEdit && _jsx(ElementToolbar, {}), _jsxs("div", { className: "cs-shell-body", children: [navVisible && (_jsxs(_Fragment, { children: [_jsx("div", { className: "cs-nav-backdrop", "aria-hidden": "true", onClick: () => setNavVisible(false) }), sidebarPanels.length === 0 ? (_jsx(SceneNavigator, {})) : (_jsxs("div", { className: "cs-sidebar", children: [_jsx("div", { className: "cs-sidebar-tabs", role: "tablist", "aria-label": t('shell.sidebar'), children: [{ id: 'navigator', title: t('navigator.title') }, ...sidebarPanels].map(p => (_jsx("button", { type: "button", role: "tab", "aria-selected": (activeSidebar?.id ?? 'navigator') === p.id, className: `cs-sidebar-tab${(activeSidebar?.id ?? 'navigator') === p.id ? ' cs-sidebar-tab-active' : ''}`, onClick: () => setSidebarTab(p.id), children: p.title }, p.id))) }), activeSidebar ? (_jsx("div", { className: "cs-sidebar-panel", role: "tabpanel", "aria-label": activeSidebar.title, children: activeSidebar.render(panelContext) })) : (_jsx(SceneNavigator, {}))] }))] })), _jsx("div", { className: "cs-editor-area", children: _jsx("main", { className: "cs-main", children: mode === 'formatted' ? _jsx(EditorView, {}) : _jsx(SourceView, {}) }) }), inspectorPanels.length > 0 && (_jsx("aside", { className: "cs-inspector", "aria-label": t('shell.inspector'), children: inspectorPanels.map(p => (_jsxs("section", { className: "cs-inspector-section", "aria-label": p.title, children: [_jsx("div", { className: "cs-inspector-title", children: p.title }), p.render(panelContext)] }, p.id))) }))] }), _jsx(StatusBar, {}), _jsx(FindReplace, {}), exportVisible && _jsx(ExportDialog, {}), titlePageVisible && canEdit && _jsx(TitlePageDialog, { onClose: () => setTitlePageVisible(false) }), settingsVisible && _jsx(SettingsDialog, { onClose: () => setSettingsVisible(false) }), styleVisible && canEdit && _jsx(StyleDialog, { onClose: () => setStyleVisible(false) }), confirmModal && _jsx(ConfirmDialog, { options: confirmModal, onClose: closeConfirm }), _jsx(ToastContainer, { toasts: toasts, onDismiss: dismissToast }), canEdit && _jsx(SelectionMenu, {})] }));
44
44
  }
@@ -1,6 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTranslation } from '../i18n/useTranslation';
2
3
  import { useEffect, useRef } from 'react';
3
4
  export function ConfirmDialog({ options, onClose }) {
5
+ const { t } = useTranslation();
4
6
  const confirmBtnRef = useRef(null);
5
7
  useEffect(() => {
6
8
  confirmBtnRef.current?.focus();
@@ -16,8 +18,8 @@ export function ConfirmDialog({ options, onClose }) {
16
18
  return (_jsx("div", { className: "cs-confirm-overlay", role: "presentation", onClick: () => { options.onCancel?.(); onClose(); }, children: _jsxs("div", { className: "cs-confirm-dialog", role: "alertdialog", "aria-modal": "true", "aria-labelledby": "cs-confirm-title", "aria-describedby": "cs-confirm-desc", onClick: (e) => e.stopPropagation(), children: [_jsx("div", { id: "cs-confirm-title", className: "cs-confirm-title", children: options.title }), _jsx("div", { id: "cs-confirm-desc", className: "cs-confirm-msg", children: options.message }), _jsxs("div", { className: "cs-confirm-actions", children: [_jsx("button", { type: "button", className: "cs-confirm-btn-cancel", onClick: () => {
17
19
  options.onCancel?.();
18
20
  onClose();
19
- }, children: options.cancelLabel ?? 'Cancel' }), _jsx("button", { ref: confirmBtnRef, type: "button", className: options.destructive ? 'cs-confirm-btn-danger' : 'cs-confirm-btn-primary', onClick: () => {
21
+ }, children: options.cancelLabel ?? t('common.cancel') }), _jsx("button", { ref: confirmBtnRef, type: "button", className: options.destructive ? 'cs-confirm-btn-danger' : 'cs-confirm-btn-primary', onClick: () => {
20
22
  options.onConfirm();
21
23
  onClose();
22
- }, children: options.confirmLabel ?? 'Confirm' })] })] }) }));
24
+ }, children: options.confirmLabel ?? t('common.confirm') })] })] }) }));
23
25
  }
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import React, { useEffect, useState } from 'react';
3
+ import { useTranslation } from '../i18n/useTranslation';
3
4
  import { ToolbarButton } from './ToolbarButton';
4
5
  import { subscribe } from '../editor/editor-bus';
5
6
  import { OverflowIcon, CloseIcon } from './icons';
@@ -21,6 +22,7 @@ function groupsOf(commands) {
21
22
  * separator between groups.
22
23
  */
23
24
  export function ElementToolbar() {
25
+ const { t } = useTranslation();
24
26
  const [, setTick] = useState(0);
25
27
  const [overflowOpen, setOverflowOpen] = useState(false);
26
28
  const commands = useCommands().filter(c => c.menu === 'toolbar');
@@ -33,8 +35,8 @@ export function ElementToolbar() {
33
35
  const elements = commands.filter(c => c.group === 'elements');
34
36
  const others = commands.filter(c => !['marks', 'history', 'elements'].includes(c.group ?? ''));
35
37
  const run = (c) => { c.run(context()); setOverflowOpen(false); };
36
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "cs-element-toolbar", role: "toolbar", "aria-label": "Editing toolbar", children: [groups.map((group, gi) => (_jsxs(React.Fragment, { children: [gi > 0 && _jsx("span", { className: "cs-tb-sep" }), group.map(c => (_jsx(ToolbarButton, { label: c.label, sigil: c.sigil, shortcut: displayShortcut(c.shortcut ?? c.shortcutHint), active: c.isActive?.(ctx) ?? false, onClick: () => c.run(context()), children: c.icon ? c.icon(20) : _jsx("span", { className: "cs-tb-text-label", children: c.label }) }, c.id)))] }, group[0].group ?? `g${gi}`))), _jsx("button", { type: "button", className: `cs-tb-overflow${overflowOpen ? ' cs-tb-overflow-active' : ''}`, "aria-label": "More", "aria-expanded": overflowOpen, onClick: () => setOverflowOpen(!overflowOpen), children: _jsx(OverflowIcon, { size: 18 }) })] }), overflowOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "cs-tb-overflow-backdrop", "aria-hidden": "true", onClick: () => setOverflowOpen(false) }), _jsxs("div", { className: "cs-tb-overflow-sheet", role: "dialog", "aria-modal": "true", "aria-label": "Screenplay Elements & Formatting", children: [_jsxs("div", { className: "cs-tb-overflow-header", children: [_jsx("span", { className: "cs-tb-overflow-title", children: "Formatting & Elements" }), _jsx("button", { type: "button", className: "cs-tb-overflow-close", "aria-label": "Close", onClick: () => setOverflowOpen(false), children: _jsx(CloseIcon, { size: 18 }) })] }), _jsx("div", { className: "cs-tb-overflow-section-label", children: "Inline Formatting" }), _jsx("div", { className: "cs-tb-overflow-inline-grid", children: inline.map(c => (_jsxs("button", { type: "button", className: "cs-tb-overflow-item", onClick: () => run(c), children: [c.icon?.(18), _jsx("span", { children: c.label })] }, c.id))) }), _jsx("div", { className: "cs-tb-overflow-section-label", children: "Screenplay Elements" }), _jsx("div", { className: "cs-tb-overflow-list", children: [...elements, ...others].map(c => {
38
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "cs-element-toolbar", role: "toolbar", "aria-label": t('tb.editing'), children: [groups.map((group, gi) => (_jsxs(React.Fragment, { children: [gi > 0 && _jsx("span", { className: "cs-tb-sep" }), group.map(c => (_jsx(ToolbarButton, { label: c.label, sigil: c.sigil, shortcut: displayShortcut(c.shortcut ?? c.shortcutHint), active: c.isActive?.(ctx) ?? false, onClick: () => c.run(context()), children: c.icon ? c.icon(20) : _jsx("span", { className: "cs-tb-text-label", children: c.label }) }, c.id)))] }, group[0].group ?? `g${gi}`))), _jsx("button", { type: "button", className: `cs-tb-overflow${overflowOpen ? ' cs-tb-overflow-active' : ''}`, "aria-label": t('tb.more'), "aria-expanded": overflowOpen, onClick: () => setOverflowOpen(!overflowOpen), children: _jsx(OverflowIcon, { size: 18 }) })] }), overflowOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "cs-tb-overflow-backdrop", "aria-hidden": "true", onClick: () => setOverflowOpen(false) }), _jsxs("div", { className: "cs-tb-overflow-sheet", role: "dialog", "aria-modal": "true", "aria-label": t('tb.sheetLabel'), children: [_jsxs("div", { className: "cs-tb-overflow-header", children: [_jsx("span", { className: "cs-tb-overflow-title", children: t('tb.sheetTitle') }), _jsx("button", { type: "button", className: "cs-tb-overflow-close", "aria-label": t('common.close'), onClick: () => setOverflowOpen(false), children: _jsx(CloseIcon, { size: 18 }) })] }), _jsx("div", { className: "cs-tb-overflow-section-label", children: t('tb.inline') }), _jsx("div", { className: "cs-tb-overflow-inline-grid", children: inline.map(c => (_jsxs("button", { type: "button", className: "cs-tb-overflow-item", onClick: () => run(c), children: [c.icon?.(18), _jsx("span", { children: c.label })] }, c.id))) }), _jsx("div", { className: "cs-tb-overflow-section-label", children: t('tb.elements') }), _jsx("div", { className: "cs-tb-overflow-list", children: [...elements, ...others].map(c => {
37
39
  const active = c.isActive?.(ctx) ?? false;
38
- return (_jsxs("button", { type: "button", className: `cs-tb-overflow-list-item${active ? ' cs-tb-overflow-item-active' : ''}`, onClick: () => run(c), children: [_jsx("div", { className: "cs-tb-overflow-list-icon", children: c.icon?.(18) }), _jsxs("div", { className: "cs-tb-overflow-list-text", children: [_jsx("span", { className: "cs-tb-overflow-list-name", children: c.label }), c.sigil && _jsx("span", { className: "cs-tb-overflow-list-sigil", children: c.sigil })] }), active && _jsx("span", { className: "cs-tb-overflow-current-badge", children: "Active" })] }, c.id));
40
+ return (_jsxs("button", { type: "button", className: `cs-tb-overflow-list-item${active ? ' cs-tb-overflow-item-active' : ''}`, onClick: () => run(c), children: [_jsx("div", { className: "cs-tb-overflow-list-icon", children: c.icon?.(18) }), _jsxs("div", { className: "cs-tb-overflow-list-text", children: [_jsx("span", { className: "cs-tb-overflow-list-name", children: c.label }), c.sigil && _jsx("span", { className: "cs-tb-overflow-list-sigil", children: c.sigil })] }), active && _jsx("span", { className: "cs-tb-overflow-current-badge", children: t('tb.active') })] }, c.id));
39
41
  }) })] })] }))] }));
40
42
  }
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState, useEffect, useRef } from 'react';
3
3
  import { useLanguage } from '../i18n/LanguageContext';
4
+ import { useTranslation } from '../i18n/useTranslation';
4
5
  import { useDocument } from '../context/DocumentContext';
5
6
  import { getEditorView } from '../editor/editor-bus';
6
7
  import { useCanEdit } from '../extensions/session';
@@ -18,6 +19,7 @@ const LANG_NAMES = {
18
19
  si: 'Sinhala',
19
20
  };
20
21
  export function LanguageStatus() {
22
+ const { t } = useTranslation();
21
23
  const { currentLanguage } = useLanguage();
22
24
  const { text, setText } = useDocument();
23
25
  const canEdit = useCanEdit();
@@ -61,7 +63,7 @@ export function LanguageStatus() {
61
63
  }
62
64
  setMenuOpen(false);
63
65
  };
64
- return (_jsxs("div", { className: "cs-lang-status", ref: menuRef, children: [_jsxs("button", { type: "button", className: "cs-lang-status-btn cs-lang-pill", title: `Script language at caret: ${langName} (${currentLanguage.toUpperCase()})`, "aria-label": `Script language: ${langName}`, "aria-haspopup": "menu", "aria-expanded": menuOpen, onClick: () => setMenuOpen(!menuOpen), children: [_jsx("span", { className: "cs-lang-pill-script", children: "\u0905/A" }), _jsx("span", { className: "cs-lang-code", children: currentLanguage.toUpperCase() })] }), menuOpen && (_jsx("div", { className: "cs-lang-menu", role: "menu", "aria-label": "Script languages", children: Object.entries(LANG_NAMES).map(([code, name]) => {
66
+ return (_jsxs("div", { className: "cs-lang-status", ref: menuRef, children: [_jsxs("button", { type: "button", className: "cs-lang-status-btn cs-lang-pill", title: t('lang.tooltip').replace('{name}', langName).replace('{code}', currentLanguage.toUpperCase()), "aria-label": t('lang.aria').replace('{name}', langName), "aria-haspopup": "menu", "aria-expanded": menuOpen, onClick: () => setMenuOpen(!menuOpen), children: [_jsx("span", { className: "cs-lang-pill-script", children: "\u0905/A" }), _jsx("span", { className: "cs-lang-code", children: currentLanguage.toUpperCase() })] }), menuOpen && (_jsx("div", { className: "cs-lang-menu", role: "menu", "aria-label": t('lang.menu'), children: Object.entries(LANG_NAMES).map(([code, name]) => {
65
67
  const isActive = currentLanguage === code;
66
68
  return (_jsxs("button", { type: "button", role: "menuitem", className: `cs-lang-option${isActive ? ' cs-lang-active' : ''}`, onClick: (e) => {
67
69
  e.stopPropagation();
@@ -55,5 +55,5 @@ export function StatusBar() {
55
55
  const estPages = Math.max(1, Math.round(lines / 54));
56
56
  return { wordCount: words, pageEstimate: estPages };
57
57
  }, [text]);
58
- return (_jsxs(_Fragment, { children: [_jsxs("footer", { className: "cs-status-bar", role: "contentinfo", "aria-label": "Status bar", children: [_jsxs("div", { className: "cs-sb-left", children: [_jsxs("span", { className: "cs-sb-metric cs-sb-metric-scenes", title: t('statusbar.scenesTooltip'), children: [_jsx("strong", { children: scenes.length }), " ", _jsx("span", { className: "cs-sb-metric-label", children: scenes.length === 1 ? t('statusbar.scene') : t('statusbar.scenes') })] }), _jsx("span", { className: "cs-sb-sep", children: "\u2022" }), _jsxs("span", { className: "cs-sb-metric cs-sb-metric-pages", title: t('statusbar.pagesTooltip'), children: [_jsxs("strong", { children: ["~", pageEstimate] }), " ", _jsx("span", { className: "cs-sb-metric-label", children: pageEstimate === 1 ? t('statusbar.page') : t('statusbar.pages') })] }), _jsx("span", { className: "cs-sb-sep cs-sb-words-sep", children: "\u2022" }), _jsxs("span", { className: "cs-sb-metric cs-sb-metric-words", title: t('statusbar.wordsTooltip'), children: [_jsx("strong", { children: wordCount.toLocaleString() }), " ", _jsx("span", { className: "cs-sb-metric-label", children: t('statusbar.words') })] })] }), _jsx("div", { className: "cs-sb-center", children: _jsxs("div", { className: "cs-sb-save", role: "status", "aria-live": "polite", title: saveTooltip, children: [_jsx("span", { className: `cs-sb-dot ${isDirty ? 'cs-sb-dot-dirty' : 'cs-sb-dot-clean'}` }), _jsx("span", { className: isDirty ? 'cs-sb-save-dirty' : 'cs-sb-save-clean', children: saveLabel })] }) }), _jsxs("div", { className: "cs-sb-right", children: [_jsx(AIStatusBarStatus, {}), canEdit && filePath && versions.length > 0 && (_jsxs("button", { type: "button", className: "cs-nav-action-btn", onClick: () => setVersionHistoryVisible(true), title: `${versions.length} version${versions.length === 1 ? '' : 's'} saved`, style: { fontSize: '11px', gap: '4px', padding: '2px 6px' }, children: [_jsx(HistoryIcon, { size: 13 }), _jsx("span", { children: versions.length })] })), _jsxs("button", { type: "button", className: "cs-nav-action-btn", onClick: () => setStatsOpen(true), title: t('statusbar.statsTooltip'), style: { fontSize: '11px', gap: '4px', padding: '2px 6px' }, children: [_jsx(StatsIcon, { size: 13 }), _jsx("span", { children: t('statusbar.statsButton') })] }), _jsx(LanguageStatus, {})] })] }), statsOpen && _jsx(StatisticsDialog, { onClose: () => setStatsOpen(false) }), canEdit && filePath && (_jsx(VersionHistory, { versions: versions, filePath: filePath, onRestore: restoreVersion, onClearAll: clearVersionHistory, visible: versionHistoryVisible, onClose: () => setVersionHistoryVisible(false) }))] }));
58
+ return (_jsxs(_Fragment, { children: [_jsxs("footer", { className: "cs-status-bar", role: "contentinfo", "aria-label": t('statusbar.label'), children: [_jsxs("div", { className: "cs-sb-left", children: [_jsxs("span", { className: "cs-sb-metric cs-sb-metric-scenes", title: t('statusbar.scenesTooltip'), children: [_jsx("strong", { children: scenes.length }), " ", _jsx("span", { className: "cs-sb-metric-label", children: scenes.length === 1 ? t('statusbar.scene') : t('statusbar.scenes') })] }), _jsx("span", { className: "cs-sb-sep", children: "\u2022" }), _jsxs("span", { className: "cs-sb-metric cs-sb-metric-pages", title: t('statusbar.pagesTooltip'), children: [_jsxs("strong", { children: ["~", pageEstimate] }), " ", _jsx("span", { className: "cs-sb-metric-label", children: pageEstimate === 1 ? t('statusbar.page') : t('statusbar.pages') })] }), _jsx("span", { className: "cs-sb-sep cs-sb-words-sep", children: "\u2022" }), _jsxs("span", { className: "cs-sb-metric cs-sb-metric-words", title: t('statusbar.wordsTooltip'), children: [_jsx("strong", { children: wordCount.toLocaleString() }), " ", _jsx("span", { className: "cs-sb-metric-label", children: t('statusbar.words') })] })] }), _jsx("div", { className: "cs-sb-center", children: _jsxs("div", { className: "cs-sb-save", role: "status", "aria-live": "polite", title: saveTooltip, children: [_jsx("span", { className: `cs-sb-dot ${isDirty ? 'cs-sb-dot-dirty' : 'cs-sb-dot-clean'}` }), _jsx("span", { className: isDirty ? 'cs-sb-save-dirty' : 'cs-sb-save-clean', children: saveLabel })] }) }), _jsxs("div", { className: "cs-sb-right", children: [_jsx(AIStatusBarStatus, {}), canEdit && filePath && versions.length > 0 && (_jsxs("button", { type: "button", className: "cs-nav-action-btn", onClick: () => setVersionHistoryVisible(true), title: `${versions.length} version${versions.length === 1 ? '' : 's'} saved`, style: { fontSize: '11px', gap: '4px', padding: '2px 6px' }, children: [_jsx(HistoryIcon, { size: 13 }), _jsx("span", { children: versions.length })] })), _jsxs("button", { type: "button", className: "cs-nav-action-btn", onClick: () => setStatsOpen(true), title: t('statusbar.statsTooltip'), style: { fontSize: '11px', gap: '4px', padding: '2px 6px' }, children: [_jsx(StatsIcon, { size: 13 }), _jsx("span", { children: t('statusbar.statsButton') })] }), _jsx(LanguageStatus, {})] })] }), statsOpen && _jsx(StatisticsDialog, { onClose: () => setStatsOpen(false) }), canEdit && filePath && (_jsx(VersionHistory, { versions: versions, filePath: filePath, onRestore: restoreVersion, onClearAll: clearVersionHistory, visible: versionHistoryVisible, onClose: () => setVersionHistoryVisible(false) }))] }));
59
59
  }
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useLayoutEffect, useMemo, useRef, useState } from 'react';
3
3
  import { diffWordsWithSpace } from 'diff';
4
+ import { useTranslation } from '../i18n/useTranslation';
4
5
  // Word-level diff over the full text (not line-bucketed): if only one word in
5
6
  // a sentence changed, only that word is marked added/removed — the rest of
6
7
  // the sentence comes back as an unchanged, unstyled Change chunk. Whitespace
@@ -17,6 +18,7 @@ function formatTimestamp(ts) {
17
18
  });
18
19
  }
19
20
  export function VersionHistory({ versions, filePath, onRestore, onClearAll, visible, onClose }) {
21
+ const { t } = useTranslation();
20
22
  const [selectedId, setSelectedId] = useState(null);
21
23
  const diffScrollRef = useRef(null);
22
24
  // Maps a diff-part index (only added/removed parts) to its rendered <span>,
@@ -75,7 +77,7 @@ export function VersionHistory({ versions, filePath, onRestore, onClearAll, visi
75
77
  if (!visible || !filePath)
76
78
  return null;
77
79
  return (_jsx("div", { className: "cs-dialog-overlay", role: "presentation", onClick: e => { if (e.target === e.currentTarget)
78
- onClose(); }, children: _jsxs("div", { className: "cs-dialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "cs-version-dialog-title", style: { maxWidth: '620px', width: '92%', maxHeight: '80vh', display: 'flex', flexDirection: 'column' }, children: [_jsxs("div", { className: "cs-panel-header", children: [_jsx("span", { id: "cs-version-dialog-title", className: "cs-panel-title", children: "Version History" }), _jsx("button", { className: "cs-panel-close", onClick: onClose, "aria-label": "Close", children: "\u00D7" })] }), _jsxs("div", { className: "cs-version-body", children: [_jsx("div", { className: "cs-version-list", children: versions.length === 0 ? (_jsx("div", { className: "cs-version-empty", children: "No versions saved yet" })) : (versions.map((v, idx) => (_jsxs("button", { type: "button", className: `cs-version-list-item${selectedId === v.id ? ' cs-version-list-item-active' : ''}`, onClick: () => setSelectedId(v.id), children: [_jsx("div", { className: "cs-version-list-item-label", children: idx === 0 ? 'Current' : formatTimestamp(v.timestamp) }), idx !== 0 && _jsx("div", { className: "cs-version-list-item-date", children: formatTimestamp(v.timestamp) })] }, v.id)))) }), _jsxs("div", { className: "cs-version-diff-wrap", children: [_jsx("div", { className: "cs-version-diff", ref: diffScrollRef, children: !selectedVersion ? (_jsx("div", { className: "cs-version-empty", children: "Select a version to compare against the current document" })) : selectedVersion.id === currentVersion?.id ? (_jsx("div", { className: "cs-version-empty", children: "This is the current version" })) : diff ? (diff.every(part => !part.added && !part.removed) ? (_jsx("div", { className: "cs-version-empty", children: "No differences from the current version" })) : (_jsx("div", { className: "cs-version-diff-content", children: diff.map((part, i) => {
80
+ onClose(); }, children: _jsxs("div", { className: "cs-dialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "cs-version-dialog-title", style: { maxWidth: '620px', width: '92%', maxHeight: '80vh', display: 'flex', flexDirection: 'column' }, children: [_jsxs("div", { className: "cs-panel-header", children: [_jsx("span", { id: "cs-version-dialog-title", className: "cs-panel-title", children: t('version.title') }), _jsx("button", { className: "cs-panel-close", onClick: onClose, "aria-label": t('common.close'), children: "\u00D7" })] }), _jsxs("div", { className: "cs-version-body", children: [_jsx("div", { className: "cs-version-list", children: versions.length === 0 ? (_jsx("div", { className: "cs-version-empty", children: t('version.empty') })) : (versions.map((v, idx) => (_jsxs("button", { type: "button", className: `cs-version-list-item${selectedId === v.id ? ' cs-version-list-item-active' : ''}`, onClick: () => setSelectedId(v.id), children: [_jsx("div", { className: "cs-version-list-item-label", children: idx === 0 ? t('version.current') : formatTimestamp(v.timestamp) }), idx !== 0 && _jsx("div", { className: "cs-version-list-item-date", children: formatTimestamp(v.timestamp) })] }, v.id)))) }), _jsxs("div", { className: "cs-version-diff-wrap", children: [_jsx("div", { className: "cs-version-diff", ref: diffScrollRef, children: !selectedVersion ? (_jsx("div", { className: "cs-version-empty", children: t('version.select') })) : selectedVersion.id === currentVersion?.id ? (_jsx("div", { className: "cs-version-empty", children: t('version.isCurrent') })) : diff ? (diff.every(part => !part.added && !part.removed) ? (_jsx("div", { className: "cs-version-empty", children: t('version.noDiff') })) : (_jsx("div", { className: "cs-version-diff-content", children: diff.map((part, i) => {
79
81
  const isChange = part.added || part.removed;
80
82
  return (_jsx("span", { ref: isChange ? (el) => {
81
83
  if (el)
@@ -85,5 +87,5 @@ export function VersionHistory({ versions, filePath, onRestore, onClearAll, visi
85
87
  } : undefined, className: part.added ? 'cs-version-diff-added'
86
88
  : part.removed ? 'cs-version-diff-removed'
87
89
  : undefined, children: part.value }, i));
88
- }) }))) : null }), _jsx("div", { className: "cs-version-diff-minimap", "aria-hidden": "true", children: markers.map((m, i) => (_jsx("button", { type: "button", className: `cs-version-diff-marker cs-version-diff-marker-${m.type}`, style: { top: `${m.position * 100}%` }, title: m.type === 'added' ? 'Jump to added text' : 'Jump to removed text', onClick: () => scrollToMarker(m.position) }, i))) })] })] }), _jsxs("div", { className: "cs-version-footer", children: [_jsx("button", { type: "button", className: "cs-confirm-btn-cancel cs-version-clear-btn", onClick: () => void onClearAll(), disabled: versions.length === 0, children: "Delete Version History" }), _jsxs("div", { style: { display: 'flex', gap: '10px' }, children: [_jsx("button", { type: "button", className: "cs-confirm-btn-cancel", onClick: onClose, children: "Close" }), selectedVersion && selectedVersion.id !== currentVersion?.id && (_jsx("button", { type: "button", className: "cs-confirm-btn-primary", onClick: () => onRestore(selectedVersion), children: "Restore This Version" }))] })] })] }) }));
90
+ }) }))) : null }), _jsx("div", { className: "cs-version-diff-minimap", "aria-hidden": "true", children: markers.map((m, i) => (_jsx("button", { type: "button", className: `cs-version-diff-marker cs-version-diff-marker-${m.type}`, style: { top: `${m.position * 100}%` }, title: m.type === 'added' ? t('version.jumpAdded') : t('version.jumpRemoved'), onClick: () => scrollToMarker(m.position) }, i))) })] })] }), _jsxs("div", { className: "cs-version-footer", children: [_jsx("button", { type: "button", className: "cs-confirm-btn-cancel cs-version-clear-btn", onClick: () => void onClearAll(), disabled: versions.length === 0, children: t('version.delete') }), _jsxs("div", { style: { display: 'flex', gap: '10px' }, children: [_jsx("button", { type: "button", className: "cs-confirm-btn-cancel", onClick: onClose, children: t('common.close') }), selectedVersion && selectedVersion.id !== currentVersion?.id && (_jsx("button", { type: "button", className: "cs-confirm-btn-primary", onClick: () => onRestore(selectedVersion), children: t('version.restore') }))] })] })] }) }));
89
91
  }
@@ -80,12 +80,12 @@ export function ExportDialog() {
80
80
  setWarnings(output && !(output instanceof Blob) ? output.warnings ?? [] : []);
81
81
  if (blob)
82
82
  downloadBlob(selected.fileName(ctx), blob);
83
- showToast(selected.successMessage ?? `Exported ${selected.label}`, blob ? 'success' : 'info');
83
+ showToast(selected.successMessage ?? t('export.exported').replace('{label}', selected.label), blob ? 'success' : 'info');
84
84
  }
85
85
  catch (err) {
86
86
  console.error('Export failed', err);
87
87
  setWarnings(['An error occurred during export. Please check the console.']);
88
- showToast('Export failed. Check console for details.', 'error');
88
+ showToast(t('export.failed'), 'error');
89
89
  }
90
90
  finally {
91
91
  setIsExporting(false);
@@ -102,14 +102,14 @@ export function ExportDialog() {
102
102
  return (_jsx("div", { className: "cs-export-overlay", role: "presentation", onClick: e => {
103
103
  if (e.target === e.currentTarget)
104
104
  setExportVisible(false);
105
- }, children: _jsxs("div", { className: "cs-export-dialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "cs-export-title", style: { maxHeight: '90vh', maxWidth: '540px', width: '92%', display: 'flex', flexDirection: 'column' }, children: [_jsxs("div", { className: "cs-panel-header", children: [_jsx("span", { id: "cs-export-title", className: "cs-panel-title", children: t('export.title') }), _jsx("button", { className: "cs-panel-close", onClick: () => setExportVisible(false), "aria-label": "Close", children: "\u00D7" })] }), _jsxs("div", { className: "cs-panel-body", style: { overflowY: 'auto', flex: 1, padding: '16px' }, children: [_jsxs("div", { className: "cs-export-segmented", role: "tablist", "aria-label": "Export category", children: [_jsx("button", { type: "button", role: "tab", "aria-selected": tab === 'screenplay', className: `cs-export-seg-btn${tab === 'screenplay' ? ' cs-export-seg-btn-active' : ''}`, onClick: () => {
105
+ }, children: _jsxs("div", { className: "cs-export-dialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "cs-export-title", style: { maxHeight: '90vh', maxWidth: '540px', width: '92%', display: 'flex', flexDirection: 'column' }, children: [_jsxs("div", { className: "cs-panel-header", children: [_jsx("span", { id: "cs-export-title", className: "cs-panel-title", children: t('export.title') }), _jsx("button", { className: "cs-panel-close", onClick: () => setExportVisible(false), "aria-label": t('common.close'), children: "\u00D7" })] }), _jsxs("div", { className: "cs-panel-body", style: { overflowY: 'auto', flex: 1, padding: '16px' }, children: [_jsxs("div", { className: "cs-export-segmented", role: "tablist", "aria-label": t('export.category'), children: [_jsx("button", { type: "button", role: "tab", "aria-selected": tab === 'screenplay', className: `cs-export-seg-btn${tab === 'screenplay' ? ' cs-export-seg-btn-active' : ''}`, onClick: () => {
106
106
  setTab('screenplay');
107
107
  if (selected?.group !== 'screenplay')
108
108
  setFormatId(screenplayFormats[0]?.id ?? '');
109
- }, children: "Screenplay Formats" }), reportFormats.length > 0 && _jsx("button", { type: "button", role: "tab", "aria-selected": tab === 'reports', className: `cs-export-seg-btn${tab === 'reports' ? ' cs-export-seg-btn-active' : ''}`, onClick: () => {
109
+ }, children: t('export.tab.screenplay') }), reportFormats.length > 0 && _jsx("button", { type: "button", role: "tab", "aria-selected": tab === 'reports', className: `cs-export-seg-btn${tab === 'reports' ? ' cs-export-seg-btn-active' : ''}`, onClick: () => {
110
110
  setTab('reports');
111
111
  if (selected?.group !== 'report')
112
112
  setFormatId(reportFormats[0]?.id ?? '');
113
- }, children: "Production Reports" })] }), tab === 'screenplay' ? (_jsxs("div", { children: [_jsx("div", { className: "cs-export-card-grid", children: screenplayFormats.map(f => (_jsxs("div", { className: `cs-export-format-card${selected?.id === f.id ? ' cs-export-format-card-selected' : ''}`, onClick: () => setFormatId(f.id), role: "radio", "aria-checked": selected?.id === f.id, tabIndex: 0, onKeyDown: e => { if (e.key === 'Enter' || e.key === ' ')
114
- setFormatId(f.id); }, children: [_jsx("input", { type: "radio", name: "format", value: f.id, checked: selected?.id === f.id, onChange: () => setFormatId(f.id), style: { marginTop: '2px' } }), _jsxs("div", { children: [_jsx("strong", { style: { fontSize: '13px', display: 'block', color: 'var(--cs-ui-text-light-primary, #1c1b18)' }, children: f.label }), f.description && (_jsx("span", { style: { fontSize: '11px', color: 'var(--cs-ui-text-light-secondary, #59574f)' }, children: f.description }))] })] }, f.id))) }), showStylePicker && (_jsxs("div", { className: "cs-settings-row", style: { marginTop: '14px', padding: '10px', background: 'var(--cs-ui-bg-panel-subtle, #f4f2ea)', borderRadius: '6px' }, children: [_jsx("label", { className: "cs-settings-label", htmlFor: "cs-export-style-select", style: { fontSize: '12px' }, children: t('export.style') }), _jsxs("select", { id: "cs-export-style-select", className: "cs-settings-select", value: styleOverride, onChange: e => setStyleOverride(e.target.value), children: [_jsxs("option", { value: "", children: [t('export.styleDefault'), " (", resolvedStyle.name, ")"] }), styleChoices.map(s => (_jsx("option", { value: s.id, children: s.name }, s.id)))] })] })), showSkipNotes && (_jsxs("div", { className: "cs-settings-row", style: { marginTop: '8px', padding: '10px', background: 'var(--cs-ui-bg-panel-subtle, #f4f2ea)', borderRadius: '6px', display: 'flex', alignItems: 'center', gap: '8px' }, children: [_jsx("input", { id: "cs-export-skip-notes", type: "checkbox", checked: skipNotes, onChange: e => setSkipNotes(e.target.checked) }), _jsx("label", { htmlFor: "cs-export-skip-notes", className: "cs-settings-label", style: { fontSize: '12px', margin: 0 }, children: "Skip notes ([[ ]])" })] })), showRomanize && (_jsxs("div", { className: "cs-settings-row", style: { marginTop: '8px', padding: '10px', background: 'var(--cs-ui-bg-panel-subtle, #f4f2ea)', borderRadius: '6px', flexWrap: 'wrap', gap: '8px' }, children: [_jsx("label", { className: "cs-settings-label", htmlFor: "cs-export-romanize-select", style: { fontSize: '12px' }, children: "Romanized copy (ISO 15919)" }), _jsxs("select", { id: "cs-export-romanize-select", className: "cs-settings-select", value: romanizeScope, onChange: e => setRomanizeScope(e.target.value), children: [_jsx("option", { value: "off", children: "Off (native script)" }), _jsx("option", { value: "all", children: "All text" }), _jsx("option", { value: "dialogue", children: "Dialogue only" })] }), romanizeScope !== 'off' && (_jsxs("select", { "aria-label": "Romanization variant", className: "cs-settings-select", value: romanizeVariant, onChange: e => setRomanizeVariant(e.target.value), children: [_jsx("option", { value: "strict", children: "Strict ISO 15919" }), _jsx("option", { value: "colloquial", children: "Casual (no diacritics)" })] })), romanizeScope !== 'off' && (_jsx("span", { style: { fontSize: '11px', color: 'var(--cs-ui-text-light-secondary, #59574f)', flexBasis: '100%' }, children: "Your script is not changed. Page numbers in the romanized copy differ from the original." }))] }))] })) : (_jsxs("div", { children: [reportFamilies.map(([family, formats]) => (_jsxs("div", { className: "cs-export-report-box", children: [_jsxs("div", { className: "cs-export-report-header", children: [_jsx("span", { className: "cs-export-report-title", children: family }), _jsx("div", { className: "cs-export-chips", children: formats.map(f => (_jsx("button", { type: "button", className: `cs-export-chip${selected?.id === f.id ? ' cs-export-chip-active' : ''}`, onClick: () => setFormatId(f.id), children: f.formatLabel ?? f.label }, f.id))) })] }), formats[0].description && (_jsx("div", { style: { fontSize: '11px', color: 'var(--cs-ui-text-light-secondary, #59574f)' }, children: formats[0].description }))] }, family))), showReportRomanize && (_jsxs("div", { className: "cs-settings-row", style: { marginTop: '8px', padding: '10px', background: 'var(--cs-ui-bg-panel-subtle, #f4f2ea)', borderRadius: '6px', flexWrap: 'wrap', gap: '8px' }, children: [_jsx("label", { className: "cs-settings-label", htmlFor: "cs-export-romanize-reports", style: { fontSize: '12px' }, children: "Romanized copy (ISO 15919)" }), _jsxs("select", { id: "cs-export-romanize-reports", className: "cs-settings-select", value: romanizeReports ? 'on' : 'off', onChange: e => setRomanizeReports(e.target.value === 'on'), children: [_jsx("option", { value: "off", children: "Off (native script)" }), _jsx("option", { value: "on", children: "Romanize all text" })] }), romanizeReports && (_jsxs("select", { "aria-label": "Romanization variant", className: "cs-settings-select", value: romanizeVariant, onChange: e => setRomanizeVariant(e.target.value), children: [_jsx("option", { value: "strict", children: "Strict ISO 15919" }), _jsx("option", { value: "colloquial", children: "Casual (no diacritics)" })] }))] }))] })), warnings.length > 0 && (_jsxs("div", { className: "cs-export-warnings", style: { background: '#fffbeb', border: '1px solid #fcd34d', padding: '10px', borderRadius: '4px', marginTop: '14px' }, children: [_jsx("div", { className: "cs-export-warn-title", style: { fontWeight: 600, color: '#b45309', fontSize: '12px', marginBottom: '4px' }, children: t('export.warnings') }), warnings.map((w, i) => (_jsxs("div", { className: "cs-export-warn-item", style: { fontSize: '11px', color: '#59574f' }, children: ["\u2022 ", w] }, i)))] }))] }), _jsxs("div", { className: "cs-export-actions", style: { borderTop: '1px solid var(--cs-ui-border-light-subtle, #eeece4)', padding: '12px 16px', display: 'flex', justifyContent: 'flex-end', gap: '8px', background: 'var(--cs-ui-bg-panel-subtle, #f4f2ea)' }, children: [_jsx("button", { type: "button", className: "cs-confirm-btn-cancel", onClick: () => setExportVisible(false), children: t('export.cancel') }), _jsx("button", { type: "button", className: "cs-confirm-btn-primary", onClick: doExport, disabled: isExporting || !selected, children: isExporting ? 'Exporting...' : t('export.download') })] })] }) }));
113
+ }, children: t('export.tab.reports') })] }), tab === 'screenplay' ? (_jsxs("div", { children: [_jsx("div", { className: "cs-export-card-grid", children: screenplayFormats.map(f => (_jsxs("div", { className: `cs-export-format-card${selected?.id === f.id ? ' cs-export-format-card-selected' : ''}`, onClick: () => setFormatId(f.id), role: "radio", "aria-checked": selected?.id === f.id, tabIndex: 0, onKeyDown: e => { if (e.key === 'Enter' || e.key === ' ')
114
+ setFormatId(f.id); }, children: [_jsx("input", { type: "radio", name: "format", value: f.id, checked: selected?.id === f.id, onChange: () => setFormatId(f.id), style: { marginTop: '2px' } }), _jsxs("div", { children: [_jsx("strong", { style: { fontSize: '13px', display: 'block', color: 'var(--cs-ui-text-light-primary, #1c1b18)' }, children: f.label }), f.description && (_jsx("span", { style: { fontSize: '11px', color: 'var(--cs-ui-text-light-secondary, #59574f)' }, children: f.description }))] })] }, f.id))) }), showStylePicker && (_jsxs("div", { className: "cs-settings-row", style: { marginTop: '14px', padding: '10px', background: 'var(--cs-ui-bg-panel-subtle, #f4f2ea)', borderRadius: '6px' }, children: [_jsx("label", { className: "cs-settings-label", htmlFor: "cs-export-style-select", style: { fontSize: '12px' }, children: t('export.style') }), _jsxs("select", { id: "cs-export-style-select", className: "cs-settings-select", value: styleOverride, onChange: e => setStyleOverride(e.target.value), children: [_jsxs("option", { value: "", children: [t('export.styleDefault'), " (", resolvedStyle.name, ")"] }), styleChoices.map(s => (_jsx("option", { value: s.id, children: s.name }, s.id)))] })] })), showSkipNotes && (_jsxs("div", { className: "cs-settings-row", style: { marginTop: '8px', padding: '10px', background: 'var(--cs-ui-bg-panel-subtle, #f4f2ea)', borderRadius: '6px', display: 'flex', alignItems: 'center', gap: '8px' }, children: [_jsx("input", { id: "cs-export-skip-notes", type: "checkbox", checked: skipNotes, onChange: e => setSkipNotes(e.target.checked) }), _jsx("label", { htmlFor: "cs-export-skip-notes", className: "cs-settings-label", style: { fontSize: '12px', margin: 0 }, children: t('export.skipNotes') })] })), showRomanize && (_jsxs("div", { className: "cs-settings-row", style: { marginTop: '8px', padding: '10px', background: 'var(--cs-ui-bg-panel-subtle, #f4f2ea)', borderRadius: '6px', flexWrap: 'wrap', gap: '8px' }, children: [_jsx("label", { className: "cs-settings-label", htmlFor: "cs-export-romanize-select", style: { fontSize: '12px' }, children: t('export.romanized') }), _jsxs("select", { id: "cs-export-romanize-select", className: "cs-settings-select", value: romanizeScope, onChange: e => setRomanizeScope(e.target.value), children: [_jsx("option", { value: "off", children: t('export.roman.off') }), _jsx("option", { value: "all", children: t('export.roman.all') }), _jsx("option", { value: "dialogue", children: t('export.roman.dialogue') })] }), romanizeScope !== 'off' && (_jsxs("select", { "aria-label": t('export.roman.variant'), className: "cs-settings-select", value: romanizeVariant, onChange: e => setRomanizeVariant(e.target.value), children: [_jsx("option", { value: "strict", children: t('export.roman.strict') }), _jsx("option", { value: "colloquial", children: t('export.roman.colloquial') })] })), romanizeScope !== 'off' && (_jsx("span", { style: { fontSize: '11px', color: 'var(--cs-ui-text-light-secondary, #59574f)', flexBasis: '100%' }, children: t('export.roman.note') }))] }))] })) : (_jsxs("div", { children: [reportFamilies.map(([family, formats]) => (_jsxs("div", { className: "cs-export-report-box", children: [_jsxs("div", { className: "cs-export-report-header", children: [_jsx("span", { className: "cs-export-report-title", children: family }), _jsx("div", { className: "cs-export-chips", children: formats.map(f => (_jsx("button", { type: "button", className: `cs-export-chip${selected?.id === f.id ? ' cs-export-chip-active' : ''}`, onClick: () => setFormatId(f.id), children: f.formatLabel ?? f.label }, f.id))) })] }), formats[0].description && (_jsx("div", { style: { fontSize: '11px', color: 'var(--cs-ui-text-light-secondary, #59574f)' }, children: formats[0].description }))] }, family))), showReportRomanize && (_jsxs("div", { className: "cs-settings-row", style: { marginTop: '8px', padding: '10px', background: 'var(--cs-ui-bg-panel-subtle, #f4f2ea)', borderRadius: '6px', flexWrap: 'wrap', gap: '8px' }, children: [_jsx("label", { className: "cs-settings-label", htmlFor: "cs-export-romanize-reports", style: { fontSize: '12px' }, children: t('export.romanized') }), _jsxs("select", { id: "cs-export-romanize-reports", className: "cs-settings-select", value: romanizeReports ? 'on' : 'off', onChange: e => setRomanizeReports(e.target.value === 'on'), children: [_jsx("option", { value: "off", children: t('export.roman.off') }), _jsx("option", { value: "on", children: t('export.roman.allReports') })] }), romanizeReports && (_jsxs("select", { "aria-label": t('export.roman.variant'), className: "cs-settings-select", value: romanizeVariant, onChange: e => setRomanizeVariant(e.target.value), children: [_jsx("option", { value: "strict", children: t('export.roman.strict') }), _jsx("option", { value: "colloquial", children: t('export.roman.colloquial') })] }))] }))] })), warnings.length > 0 && (_jsxs("div", { className: "cs-export-warnings", style: { background: '#fffbeb', border: '1px solid #fcd34d', padding: '10px', borderRadius: '4px', marginTop: '14px' }, children: [_jsx("div", { className: "cs-export-warn-title", style: { fontWeight: 600, color: '#b45309', fontSize: '12px', marginBottom: '4px' }, children: t('export.warnings') }), warnings.map((w, i) => (_jsxs("div", { className: "cs-export-warn-item", style: { fontSize: '11px', color: '#59574f' }, children: ["\u2022 ", w] }, i)))] }))] }), _jsxs("div", { className: "cs-export-actions", style: { borderTop: '1px solid var(--cs-ui-border-light-subtle, #eeece4)', padding: '12px 16px', display: 'flex', justifyContent: 'flex-end', gap: '8px', background: 'var(--cs-ui-bg-panel-subtle, #f4f2ea)' }, children: [_jsx("button", { type: "button", className: "cs-confirm-btn-cancel", onClick: () => setExportVisible(false), children: t('export.cancel') }), _jsx("button", { type: "button", className: "cs-confirm-btn-primary", onClick: doExport, disabled: isExporting || !selected, children: isExporting ? t('export.exporting') : t('export.download') })] })] }) }));
115
115
  }