@sutrata/editor 0.1.1 → 0.1.2
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.
- package/README.md +87 -0
- package/dist/ai/generated-providers.json +1 -1
- package/package.json +30 -3
- package/scripts/copy-assets.mjs +0 -16
- package/scripts/generate-providers.mjs +0 -127
- package/src/ai/OnboardingDialog.tsx +0 -390
- package/src/ai/VoiceConfirmDialog.tsx +0 -137
- package/src/ai/VoiceToolbar.tsx +0 -321
- package/src/ai/ai-client.ts +0 -596
- package/src/ai/commands.ts +0 -164
- package/src/ai/generated-providers.json +0 -3696
- package/src/ai/prompts.ts +0 -62
- package/src/ai/voice-service.ts +0 -96
- package/src/autocomplete/AutocompletePlugin.ts +0 -21
- package/src/autocomplete/suggestions.ts +0 -147
- package/src/context/DocumentContext.tsx +0 -648
- package/src/desktop-api.d.ts +0 -40
- package/src/editor/EditorView.tsx +0 -82
- package/src/editor/SelectionMenu.tsx +0 -152
- package/src/editor/attr-pencil-plugin.ts +0 -172
- package/src/editor/character-decoration-plugin.ts +0 -104
- package/src/editor/editor-bus.ts +0 -51
- package/src/editor/find-highlight-plugin.ts +0 -68
- package/src/editor/frontmatter-field-view.ts +0 -52
- package/src/editor/frontmatter-field.ts +0 -115
- package/src/editor/grapheme-cursor.ts +0 -99
- package/src/editor/ime-plugin.ts +0 -85
- package/src/editor/insert-helper.ts +0 -43
- package/src/editor/keymap.ts +0 -124
- package/src/editor/language-class-plugin.ts +0 -91
- package/src/editor/plugins.ts +0 -26
- package/src/editor/prosemirror-to-sutra.ts +0 -192
- package/src/editor/scene-heading-view.ts +0 -147
- package/src/editor/schema.ts +0 -156
- package/src/editor/sutra-to-prosemirror.ts +0 -156
- package/src/extensions/storage-adapter.ts +0 -65
- package/src/extensions/types.ts +0 -60
- package/src/file/docx-exporter.ts +0 -585
- package/src/file/docx-importer.ts +0 -313
- package/src/file/print-paginator.ts +0 -301
- package/src/file/templates.ts +0 -73
- package/src/file/workflow-reports.ts +0 -820
- package/src/findreplace/FindReplace.tsx +0 -242
- package/src/findreplace/find-engine.ts +0 -97
- package/src/findreplace/scroll-to-match.ts +0 -89
- package/src/fonts/tofu-detect.ts +0 -53
- package/src/i18n/LanguageContext.tsx +0 -207
- package/src/i18n/language-detect.ts +0 -73
- package/src/i18n/useTranslation.tsx +0 -74
- package/src/index.ts +0 -27
- package/src/locales/bn.json +0 -143
- package/src/locales/en.json +0 -143
- package/src/locales/gu.json +0 -143
- package/src/locales/hi.json +0 -143
- package/src/locales/kn.json +0 -143
- package/src/locales/ml.json +0 -143
- package/src/locales/mr.json +0 -143
- package/src/locales/or.json +0 -143
- package/src/locales/pa.json +0 -143
- package/src/locales/si.json +0 -143
- package/src/locales/ta.json +0 -143
- package/src/locales/te.json +0 -143
- package/src/navigator/SceneNavigator.tsx +0 -844
- package/src/navigator/StatisticsDialog.tsx +0 -249
- package/src/navigator/scene-list.ts +0 -84
- package/src/settings/SettingsDialog.tsx +0 -303
- package/src/shell/AppBar.tsx +0 -354
- package/src/shell/AppShell.tsx +0 -71
- package/src/shell/ConfirmDialog.tsx +0 -71
- package/src/shell/ElementToolbar.tsx +0 -175
- package/src/shell/LanguageStatus.tsx +0 -101
- package/src/shell/StatusBar.tsx +0 -135
- package/src/shell/SutrataLogo.tsx +0 -89
- package/src/shell/Toast.tsx +0 -46
- package/src/shell/ToolbarButton.tsx +0 -30
- package/src/shell/VersionHistory.tsx +0 -212
- package/src/shell/Watermark.tsx +0 -9
- package/src/shell/fonts/fonts.css +0 -1133
- package/src/shell/icons.tsx +0 -335
- package/src/shell/toolbar-actions.ts +0 -59
- package/src/source/SourceView.tsx +0 -111
- package/src/source/find-highlight-source.ts +0 -57
- package/src/source/sutra-lang.ts +0 -121
- package/src/spellcheck/dictionaries.ts +0 -21
- package/src/spellcheck/hunspell.ts +0 -110
- package/src/spellcheck/spellcheck-plugin.ts +0 -83
- package/src/styles/StyleDialog.tsx +0 -148
- package/src/styles/builtin-styles.ts +0 -139
- package/src/styles/css-adapter.ts +0 -76
- package/src/styles/docx-adapter.ts +0 -109
- package/src/styles/embed-fonts.ts +0 -81
- package/src/styles/pagination.ts +0 -27
- package/src/styles/print-adapter.ts +0 -288
- package/src/styles/registry.ts +0 -20
- package/src/styles/screenplay.css +0 -3006
- package/src/styles/script-ranges.ts +0 -17
- package/src/styles/types.ts +0 -104
- package/src/styles/validate.ts +0 -114
- package/src/templates/templates.ts +0 -194
- package/src/titlepage/ExportDialog.tsx +0 -494
- package/src/titlepage/TitlePageDialog.tsx +0 -312
- package/src/titlepage/frontmatter-form.ts +0 -280
- package/src/titlepage/languages.ts +0 -37
- package/src/types.ts +0 -7
- package/tests/AppBar.test.tsx +0 -35
- package/tests/AppShell.test.tsx +0 -22
- package/tests/ElementToolbar.test.tsx +0 -36
- package/tests/LanguageStatus.test.tsx +0 -129
- package/tests/SelectionMenu.test.tsx +0 -71
- package/tests/ToolbarButton.test.tsx +0 -24
- package/tests/ai-onboarding.test.ts +0 -102
- package/tests/ai-status.test.tsx +0 -72
- package/tests/ai-structured.test.ts +0 -94
- package/tests/autocomplete/AutocompletePlugin.test.ts +0 -20
- package/tests/autocomplete/suggestions.test.ts +0 -106
- package/tests/document-context.test.tsx +0 -565
- package/tests/docx-importer.test.ts +0 -160
- package/tests/editor/character-decoration-plugin.test.ts +0 -101
- package/tests/editor/character-extension.test.ts +0 -44
- package/tests/editor/grapheme-cursor.test.ts +0 -118
- package/tests/editor/ime-plugin.test.ts +0 -72
- package/tests/editor/inline-note-bridge.test.ts +0 -32
- package/tests/editor/language-class-plugin.test.ts +0 -265
- package/tests/editor/title-page.test.ts +0 -124
- package/tests/embed-fonts.test.ts +0 -17
- package/tests/exporters.test.ts +0 -365
- package/tests/findreplace/FindReplace.test.tsx +0 -391
- package/tests/findreplace/find-engine.test.ts +0 -86
- package/tests/findreplace/scroll-to-match.test.ts +0 -50
- package/tests/fonts/tofu-detect.test.ts +0 -122
- package/tests/i18n/LanguageContext.test.tsx +0 -146
- package/tests/i18n/language-detect.test.ts +0 -54
- package/tests/i18n/locale-parity.test.ts +0 -25
- package/tests/i18n/useTranslation.test.tsx +0 -45
- package/tests/keymap.test.ts +0 -18
- package/tests/navigator/SceneNavigator.test.tsx +0 -177
- package/tests/navigator/StatisticsDialog.test.tsx +0 -63
- package/tests/navigator/scene-list.test.ts +0 -71
- package/tests/print-paginator.test.ts +0 -82
- package/tests/shell/VersionHistory.test.tsx +0 -129
- package/tests/spellcheck/hunspell.test.ts +0 -82
- package/tests/spellcheck/spellcheck-plugin.test.ts +0 -56
- package/tests/styles.test.ts +0 -315
- package/tests/templates/templates.test.ts +0 -83
- package/tests/titlepage/TitlePageDialog.test.tsx +0 -90
- package/tests/titlepage/apply-title-page.test.ts +0 -61
- package/tests/titlepage/frontmatter-form.test.ts +0 -115
- package/tests/toolbar-actions.test.ts +0 -93
- package/tests/voice-commands.test.ts +0 -58
- package/tsconfig.json +0 -17
- package/vitest.config.ts +0 -11
- package/vitest.setup.ts +0 -65
|
@@ -1,494 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react'
|
|
2
|
-
import { useDocument } from '../context/DocumentContext'
|
|
3
|
-
import { exportFountain, parse, romanizeSutra } from '@sutrata/parser'
|
|
4
|
-
import type { DocumentNode, RomanizeVariant } from '@sutrata/parser'
|
|
5
|
-
import { useTranslation } from '../i18n/useTranslation'
|
|
6
|
-
import { exportToDocx } from '../file/docx-exporter'
|
|
7
|
-
import {
|
|
8
|
-
extractWorkflowData,
|
|
9
|
-
exportOneLinerCsv,
|
|
10
|
-
exportShotListCsv,
|
|
11
|
-
exportCastReportCsv,
|
|
12
|
-
exportOneLinerDocx,
|
|
13
|
-
exportShotListDocx,
|
|
14
|
-
exportCastReportDocx,
|
|
15
|
-
openPrintPreview,
|
|
16
|
-
openScreenplayPrintPreview,
|
|
17
|
-
} from '../file/workflow-reports'
|
|
18
|
-
import { allStyles } from '../styles/registry'
|
|
19
|
-
|
|
20
|
-
type ExportFormat =
|
|
21
|
-
| 'fountain'
|
|
22
|
-
| 'docx'
|
|
23
|
-
| 'pdf'
|
|
24
|
-
| 'oneliner-print'
|
|
25
|
-
| 'oneliner-docx'
|
|
26
|
-
| 'oneliner-csv'
|
|
27
|
-
| 'shotlist-docx'
|
|
28
|
-
| 'shotlist-csv'
|
|
29
|
-
| 'cast-docx'
|
|
30
|
-
| 'cast-csv'
|
|
31
|
-
|
|
32
|
-
export function ExportDialog() {
|
|
33
|
-
const { ast, text, filePath, setExportVisible, resolvedStyle, customStyles, showToast } = useDocument()
|
|
34
|
-
const { t } = useTranslation()
|
|
35
|
-
const [tab, setTab] = useState<'screenplay' | 'reports'>('screenplay')
|
|
36
|
-
const [format, setFormat] = useState<ExportFormat>('pdf')
|
|
37
|
-
const [warnings, setWarnings] = useState<string[]>([])
|
|
38
|
-
const [isExporting, setIsExporting] = useState(false)
|
|
39
|
-
const [styleOverride, setStyleOverride] = useState('')
|
|
40
|
-
const [romanizeScope, setRomanizeScope] = useState<'off' | 'all' | 'dialogue'>('off')
|
|
41
|
-
const [romanizeVariant, setRomanizeVariant] = useState<RomanizeVariant>('strict')
|
|
42
|
-
const [romanizeReports, setRomanizeReports] = useState(false)
|
|
43
|
-
const [skipNotes, setSkipNotes] = useState(false)
|
|
44
|
-
const hasIndicText = /[\u0900-\u0DFF]/.test(text)
|
|
45
|
-
const styleChoices = allStyles(customStyles)
|
|
46
|
-
const showStylePicker = tab === 'screenplay' && (format === 'docx' || format === 'pdf')
|
|
47
|
-
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
const onKey = (e: KeyboardEvent) => {
|
|
50
|
-
if (e.key === 'Escape') setExportVisible(false)
|
|
51
|
-
}
|
|
52
|
-
document.addEventListener('keydown', onKey)
|
|
53
|
-
return () => document.removeEventListener('keydown', onKey)
|
|
54
|
-
}, [setExportVisible])
|
|
55
|
-
|
|
56
|
-
const baseName = (filePath ?? 'screenplay').replace(/\.[^.]+$/, '')
|
|
57
|
-
|
|
58
|
-
/** The AST handed to the PDF/DOCX exporters: the document itself, or a romanized copy
|
|
59
|
-
* (Indic → ISO 15919, export only; the .sutra text is never changed). */
|
|
60
|
-
function screenplayAst(): { doc: DocumentNode; suffix: string } {
|
|
61
|
-
if (romanizeScope === 'off' || !hasIndicText) return { doc: ast, suffix: '' }
|
|
62
|
-
const romanized = romanizeSutra(text, { scope: romanizeScope, variant: romanizeVariant })
|
|
63
|
-
return { doc: parse(romanized), suffix: '_romanized' }
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** Workflow-report data, from a fully romanized copy when that option is on. */
|
|
67
|
-
function reportData(): { data: ReturnType<typeof extractWorkflowData>; suffix: string } {
|
|
68
|
-
if (!romanizeReports || !hasIndicText) return { data: extractWorkflowData(ast), suffix: '' }
|
|
69
|
-
const romanized = romanizeSutra(text, { scope: 'all', variant: romanizeVariant, notice: false })
|
|
70
|
-
return { data: extractWorkflowData(parse(romanized)), suffix: '_romanized' }
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async function doExport() {
|
|
74
|
-
setIsExporting(true)
|
|
75
|
-
try {
|
|
76
|
-
const { data, suffix: reportSuffix } = reportData()
|
|
77
|
-
const reportTitleSuffix = reportSuffix ? ' (ROMANIZED)' : ''
|
|
78
|
-
|
|
79
|
-
if (format === 'fountain') {
|
|
80
|
-
const result = exportFountain(ast)
|
|
81
|
-
setWarnings(result.warnings)
|
|
82
|
-
download(`${baseName}.fountain`, result.text, 'text/plain')
|
|
83
|
-
showToast('Exported Fountain screenplay', 'success')
|
|
84
|
-
} else if (format === 'docx') {
|
|
85
|
-
setWarnings([])
|
|
86
|
-
const { doc, suffix } = screenplayAst()
|
|
87
|
-
const blob = await exportToDocx(doc, styleOverride || undefined, customStyles, skipNotes)
|
|
88
|
-
downloadBlob(`${baseName}${suffix}.docx`, blob)
|
|
89
|
-
showToast('Exported Word document (.docx)', 'success')
|
|
90
|
-
} else if (format === 'pdf') {
|
|
91
|
-
setWarnings([])
|
|
92
|
-
const { doc, suffix } = screenplayAst()
|
|
93
|
-
const title = `${baseName.toUpperCase()} - SCREENPLAY${suffix ? ' (ROMANIZED)' : ''}`
|
|
94
|
-
await openScreenplayPrintPreview(doc, title, styleOverride || undefined, customStyles, skipNotes)
|
|
95
|
-
showToast('Opened Screenplay Print Preview', 'info')
|
|
96
|
-
} else if (format === 'oneliner-print') {
|
|
97
|
-
setWarnings([])
|
|
98
|
-
await openPrintPreview(data.scenes, `${baseName.toUpperCase()} - ONE-LINER SCHEDULE${reportTitleSuffix}`)
|
|
99
|
-
showToast('Opened One-Liner Schedule', 'info')
|
|
100
|
-
} else if (format === 'oneliner-docx') {
|
|
101
|
-
setWarnings([])
|
|
102
|
-
const blob = await exportOneLinerDocx(data.scenes)
|
|
103
|
-
downloadBlob(`${baseName}_one_liner${reportSuffix}.docx`, blob)
|
|
104
|
-
showToast('Exported One-Liner DOCX', 'success')
|
|
105
|
-
} else if (format === 'oneliner-csv') {
|
|
106
|
-
setWarnings([])
|
|
107
|
-
const csv = exportOneLinerCsv(data.scenes)
|
|
108
|
-
download(`${baseName}_one_liner${reportSuffix}.csv`, csv, 'text/csv')
|
|
109
|
-
showToast('Exported One-Liner CSV', 'success')
|
|
110
|
-
} else if (format === 'shotlist-docx') {
|
|
111
|
-
setWarnings([])
|
|
112
|
-
const blob = await exportShotListDocx(data.scenes)
|
|
113
|
-
downloadBlob(`${baseName}_shot_list${reportSuffix}.docx`, blob)
|
|
114
|
-
showToast('Exported Shot List DOCX', 'success')
|
|
115
|
-
} else if (format === 'shotlist-csv') {
|
|
116
|
-
setWarnings([])
|
|
117
|
-
const csv = exportShotListCsv(data.scenes)
|
|
118
|
-
download(`${baseName}_shot_list${reportSuffix}.csv`, csv, 'text/csv')
|
|
119
|
-
showToast('Exported Shot List CSV', 'success')
|
|
120
|
-
} else if (format === 'cast-docx') {
|
|
121
|
-
setWarnings([])
|
|
122
|
-
const blob = await exportCastReportDocx(data.characters)
|
|
123
|
-
downloadBlob(`${baseName}_cast_report${reportSuffix}.docx`, blob)
|
|
124
|
-
showToast('Exported Cast Report DOCX', 'success')
|
|
125
|
-
} else if (format === 'cast-csv') {
|
|
126
|
-
setWarnings([])
|
|
127
|
-
const csv = exportCastReportCsv(data.characters)
|
|
128
|
-
download(`${baseName}_cast_report${reportSuffix}.csv`, csv, 'text/csv')
|
|
129
|
-
showToast('Exported Cast Report CSV', 'success')
|
|
130
|
-
}
|
|
131
|
-
} catch (err) {
|
|
132
|
-
console.error('Export failed', err)
|
|
133
|
-
setWarnings(['An error occurred during export. Please check the console.'])
|
|
134
|
-
showToast('Export failed. Check console for details.', 'error')
|
|
135
|
-
} finally {
|
|
136
|
-
setIsExporting(false)
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function download(filename: string, content: string, mimeType: string) {
|
|
141
|
-
const data = mimeType === 'text/csv' ? ['\uFEFF', content] : [content]
|
|
142
|
-
const blob = new Blob(data, { type: `${mimeType};charset=utf-8` })
|
|
143
|
-
downloadBlob(filename, blob)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function downloadBlob(filename: string, blob: Blob) {
|
|
147
|
-
const url = URL.createObjectURL(blob)
|
|
148
|
-
const a = document.createElement('a')
|
|
149
|
-
a.href = url
|
|
150
|
-
a.download = filename
|
|
151
|
-
a.click()
|
|
152
|
-
URL.revokeObjectURL(url)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return (
|
|
156
|
-
<div
|
|
157
|
-
className="cs-export-overlay"
|
|
158
|
-
role="presentation"
|
|
159
|
-
onClick={e => {
|
|
160
|
-
if (e.target === e.currentTarget) setExportVisible(false)
|
|
161
|
-
}}
|
|
162
|
-
>
|
|
163
|
-
<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' }}>
|
|
164
|
-
<div className="cs-panel-header">
|
|
165
|
-
<span id="cs-export-title" className="cs-panel-title">{t('export.title')}</span>
|
|
166
|
-
<button className="cs-panel-close" onClick={() => setExportVisible(false)} aria-label="Close">
|
|
167
|
-
×
|
|
168
|
-
</button>
|
|
169
|
-
</div>
|
|
170
|
-
|
|
171
|
-
<div className="cs-panel-body" style={{ overflowY: 'auto', flex: 1, padding: '16px' }}>
|
|
172
|
-
{/* Top Segmented Tabs: Screenplay vs Production Reports */}
|
|
173
|
-
<div className="cs-export-segmented" role="tablist" aria-label="Export category">
|
|
174
|
-
<button
|
|
175
|
-
type="button"
|
|
176
|
-
role="tab"
|
|
177
|
-
aria-selected={tab === 'screenplay'}
|
|
178
|
-
className={`cs-export-seg-btn${tab === 'screenplay' ? ' cs-export-seg-btn-active' : ''}`}
|
|
179
|
-
onClick={() => {
|
|
180
|
-
setTab('screenplay')
|
|
181
|
-
if (!['fountain', 'docx', 'pdf'].includes(format)) setFormat('pdf')
|
|
182
|
-
}}
|
|
183
|
-
>
|
|
184
|
-
Screenplay Formats
|
|
185
|
-
</button>
|
|
186
|
-
<button
|
|
187
|
-
type="button"
|
|
188
|
-
role="tab"
|
|
189
|
-
aria-selected={tab === 'reports'}
|
|
190
|
-
className={`cs-export-seg-btn${tab === 'reports' ? ' cs-export-seg-btn-active' : ''}`}
|
|
191
|
-
onClick={() => {
|
|
192
|
-
setTab('reports')
|
|
193
|
-
if (['fountain', 'docx', 'pdf'].includes(format)) setFormat('oneliner-print')
|
|
194
|
-
}}
|
|
195
|
-
>
|
|
196
|
-
Production Reports
|
|
197
|
-
</button>
|
|
198
|
-
</div>
|
|
199
|
-
|
|
200
|
-
{tab === 'screenplay' ? (
|
|
201
|
-
<div>
|
|
202
|
-
<div className="cs-export-card-grid">
|
|
203
|
-
{/* PDF / Print */}
|
|
204
|
-
<div
|
|
205
|
-
className={`cs-export-format-card${format === 'pdf' ? ' cs-export-format-card-selected' : ''}`}
|
|
206
|
-
onClick={() => setFormat('pdf')}
|
|
207
|
-
role="radio"
|
|
208
|
-
aria-checked={format === 'pdf'}
|
|
209
|
-
tabIndex={0}
|
|
210
|
-
onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') setFormat('pdf') }}
|
|
211
|
-
>
|
|
212
|
-
<input
|
|
213
|
-
type="radio"
|
|
214
|
-
name="format"
|
|
215
|
-
value="pdf"
|
|
216
|
-
checked={format === 'pdf'}
|
|
217
|
-
onChange={() => setFormat('pdf')}
|
|
218
|
-
style={{ marginTop: '2px' }}
|
|
219
|
-
/>
|
|
220
|
-
<div>
|
|
221
|
-
<strong style={{ fontSize: '13px', display: 'block', color: 'var(--cs-ui-text-light-primary, #1c1b18)' }}>
|
|
222
|
-
PDF / Print Preview
|
|
223
|
-
</strong>
|
|
224
|
-
<span style={{ fontSize: '11px', color: 'var(--cs-ui-text-light-secondary, #59574f)' }}>
|
|
225
|
-
Print-ready industry screenplay layout styled according to your active visual template.
|
|
226
|
-
</span>
|
|
227
|
-
</div>
|
|
228
|
-
</div>
|
|
229
|
-
|
|
230
|
-
{/* Microsoft Word */}
|
|
231
|
-
<div
|
|
232
|
-
className={`cs-export-format-card${format === 'docx' ? ' cs-export-format-card-selected' : ''}`}
|
|
233
|
-
onClick={() => setFormat('docx')}
|
|
234
|
-
role="radio"
|
|
235
|
-
aria-checked={format === 'docx'}
|
|
236
|
-
tabIndex={0}
|
|
237
|
-
onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') setFormat('docx') }}
|
|
238
|
-
>
|
|
239
|
-
<input
|
|
240
|
-
type="radio"
|
|
241
|
-
name="format"
|
|
242
|
-
value="docx"
|
|
243
|
-
checked={format === 'docx'}
|
|
244
|
-
onChange={() => setFormat('docx')}
|
|
245
|
-
style={{ marginTop: '2px' }}
|
|
246
|
-
/>
|
|
247
|
-
<div>
|
|
248
|
-
<strong style={{ fontSize: '13px', display: 'block', color: 'var(--cs-ui-text-light-primary, #1c1b18)' }}>
|
|
249
|
-
Microsoft Word (.docx)
|
|
250
|
-
</strong>
|
|
251
|
-
<span style={{ fontSize: '11px', color: 'var(--cs-ui-text-light-secondary, #59574f)' }}>
|
|
252
|
-
{t('export.docxDesc')}
|
|
253
|
-
</span>
|
|
254
|
-
</div>
|
|
255
|
-
</div>
|
|
256
|
-
|
|
257
|
-
{/* Fountain */}
|
|
258
|
-
<div
|
|
259
|
-
className={`cs-export-format-card${format === 'fountain' ? ' cs-export-format-card-selected' : ''}`}
|
|
260
|
-
onClick={() => setFormat('fountain')}
|
|
261
|
-
role="radio"
|
|
262
|
-
aria-checked={format === 'fountain'}
|
|
263
|
-
tabIndex={0}
|
|
264
|
-
onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') setFormat('fountain') }}
|
|
265
|
-
>
|
|
266
|
-
<input
|
|
267
|
-
type="radio"
|
|
268
|
-
name="format"
|
|
269
|
-
value="fountain"
|
|
270
|
-
checked={format === 'fountain'}
|
|
271
|
-
onChange={() => setFormat('fountain')}
|
|
272
|
-
style={{ marginTop: '2px' }}
|
|
273
|
-
/>
|
|
274
|
-
<div>
|
|
275
|
-
<strong style={{ fontSize: '13px', display: 'block', color: 'var(--cs-ui-text-light-primary, #1c1b18)' }}>
|
|
276
|
-
Fountain (.fountain)
|
|
277
|
-
</strong>
|
|
278
|
-
<span style={{ fontSize: '11px', color: 'var(--cs-ui-text-light-secondary, #59574f)' }}>
|
|
279
|
-
{t('export.fountainDesc')}
|
|
280
|
-
</span>
|
|
281
|
-
</div>
|
|
282
|
-
</div>
|
|
283
|
-
</div>
|
|
284
|
-
|
|
285
|
-
{showStylePicker && (
|
|
286
|
-
<div className="cs-settings-row" style={{ marginTop: '14px', padding: '10px', background: 'var(--cs-ui-bg-panel-subtle, #f4f2ea)', borderRadius: '6px' }}>
|
|
287
|
-
<label className="cs-settings-label" htmlFor="cs-export-style-select" style={{ fontSize: '12px' }}>
|
|
288
|
-
{t('export.style')}
|
|
289
|
-
</label>
|
|
290
|
-
<select
|
|
291
|
-
id="cs-export-style-select"
|
|
292
|
-
className="cs-settings-select"
|
|
293
|
-
value={styleOverride}
|
|
294
|
-
onChange={e => setStyleOverride(e.target.value)}
|
|
295
|
-
>
|
|
296
|
-
<option value="">{t('export.styleDefault')} ({resolvedStyle.name})</option>
|
|
297
|
-
{styleChoices.map(s => (
|
|
298
|
-
<option key={s.id} value={s.id}>{s.name}</option>
|
|
299
|
-
))}
|
|
300
|
-
</select>
|
|
301
|
-
</div>
|
|
302
|
-
)}
|
|
303
|
-
|
|
304
|
-
{showStylePicker && (
|
|
305
|
-
<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' }}>
|
|
306
|
-
<input
|
|
307
|
-
id="cs-export-skip-notes"
|
|
308
|
-
type="checkbox"
|
|
309
|
-
checked={skipNotes}
|
|
310
|
-
onChange={e => setSkipNotes(e.target.checked)}
|
|
311
|
-
/>
|
|
312
|
-
<label htmlFor="cs-export-skip-notes" className="cs-settings-label" style={{ fontSize: '12px', margin: 0 }}>
|
|
313
|
-
Skip notes ([[ ]])
|
|
314
|
-
</label>
|
|
315
|
-
</div>
|
|
316
|
-
)}
|
|
317
|
-
|
|
318
|
-
{showStylePicker && hasIndicText && (
|
|
319
|
-
<div className="cs-settings-row" style={{ marginTop: '8px', padding: '10px', background: 'var(--cs-ui-bg-panel-subtle, #f4f2ea)', borderRadius: '6px', flexWrap: 'wrap', gap: '8px' }}>
|
|
320
|
-
<label className="cs-settings-label" htmlFor="cs-export-romanize-select" style={{ fontSize: '12px' }}>
|
|
321
|
-
Romanized copy (ISO 15919)
|
|
322
|
-
</label>
|
|
323
|
-
<select
|
|
324
|
-
id="cs-export-romanize-select"
|
|
325
|
-
className="cs-settings-select"
|
|
326
|
-
value={romanizeScope}
|
|
327
|
-
onChange={e => setRomanizeScope(e.target.value as 'off' | 'all' | 'dialogue')}
|
|
328
|
-
>
|
|
329
|
-
<option value="off">Off (native script)</option>
|
|
330
|
-
<option value="all">All text</option>
|
|
331
|
-
<option value="dialogue">Dialogue only</option>
|
|
332
|
-
</select>
|
|
333
|
-
{romanizeScope !== 'off' && (
|
|
334
|
-
<select
|
|
335
|
-
aria-label="Romanization variant"
|
|
336
|
-
className="cs-settings-select"
|
|
337
|
-
value={romanizeVariant}
|
|
338
|
-
onChange={e => setRomanizeVariant(e.target.value as RomanizeVariant)}
|
|
339
|
-
>
|
|
340
|
-
<option value="strict">Strict ISO 15919</option>
|
|
341
|
-
<option value="colloquial">Casual (no diacritics)</option>
|
|
342
|
-
</select>
|
|
343
|
-
)}
|
|
344
|
-
{romanizeScope !== 'off' && (
|
|
345
|
-
<span style={{ fontSize: '11px', color: 'var(--cs-ui-text-light-secondary, #59574f)', flexBasis: '100%' }}>
|
|
346
|
-
Your script is not changed. Page numbers in the romanized copy differ from the original.
|
|
347
|
-
</span>
|
|
348
|
-
)}
|
|
349
|
-
</div>
|
|
350
|
-
)}
|
|
351
|
-
</div>
|
|
352
|
-
) : (
|
|
353
|
-
<div>
|
|
354
|
-
{/* One-Liner Box */}
|
|
355
|
-
<div className="cs-export-report-box">
|
|
356
|
-
<div className="cs-export-report-header">
|
|
357
|
-
<span className="cs-export-report-title">One-Liner Schedule</span>
|
|
358
|
-
<div className="cs-export-chips">
|
|
359
|
-
<button
|
|
360
|
-
type="button"
|
|
361
|
-
className={`cs-export-chip${format === 'oneliner-print' ? ' cs-export-chip-active' : ''}`}
|
|
362
|
-
onClick={() => setFormat('oneliner-print')}
|
|
363
|
-
>
|
|
364
|
-
Print View
|
|
365
|
-
</button>
|
|
366
|
-
<button
|
|
367
|
-
type="button"
|
|
368
|
-
className={`cs-export-chip${format === 'oneliner-docx' ? ' cs-export-chip-active' : ''}`}
|
|
369
|
-
onClick={() => setFormat('oneliner-docx')}
|
|
370
|
-
>
|
|
371
|
-
Word (.docx)
|
|
372
|
-
</button>
|
|
373
|
-
<button
|
|
374
|
-
type="button"
|
|
375
|
-
className={`cs-export-chip${format === 'oneliner-csv' ? ' cs-export-chip-active' : ''}`}
|
|
376
|
-
onClick={() => setFormat('oneliner-csv')}
|
|
377
|
-
>
|
|
378
|
-
Spreadsheet (.csv)
|
|
379
|
-
</button>
|
|
380
|
-
</div>
|
|
381
|
-
</div>
|
|
382
|
-
<div style={{ fontSize: '11px', color: 'var(--cs-ui-text-light-secondary, #59574f)' }}>
|
|
383
|
-
A scene-by-scene production stripboard breakdown with locations, cast, and timings.
|
|
384
|
-
</div>
|
|
385
|
-
</div>
|
|
386
|
-
|
|
387
|
-
{/* Shot List Box */}
|
|
388
|
-
<div className="cs-export-report-box">
|
|
389
|
-
<div className="cs-export-report-header">
|
|
390
|
-
<span className="cs-export-report-title">Shot List</span>
|
|
391
|
-
<div className="cs-export-chips">
|
|
392
|
-
<button
|
|
393
|
-
type="button"
|
|
394
|
-
className={`cs-export-chip${format === 'shotlist-docx' ? ' cs-export-chip-active' : ''}`}
|
|
395
|
-
onClick={() => setFormat('shotlist-docx')}
|
|
396
|
-
>
|
|
397
|
-
Word (.docx)
|
|
398
|
-
</button>
|
|
399
|
-
<button
|
|
400
|
-
type="button"
|
|
401
|
-
className={`cs-export-chip${format === 'shotlist-csv' ? ' cs-export-chip-active' : ''}`}
|
|
402
|
-
onClick={() => setFormat('shotlist-csv')}
|
|
403
|
-
>
|
|
404
|
-
Spreadsheet (.csv)
|
|
405
|
-
</button>
|
|
406
|
-
</div>
|
|
407
|
-
</div>
|
|
408
|
-
<div style={{ fontSize: '11px', color: 'var(--cs-ui-text-light-secondary, #59574f)' }}>
|
|
409
|
-
Camera shot specifications extracted from scene headings and production notes.
|
|
410
|
-
</div>
|
|
411
|
-
</div>
|
|
412
|
-
|
|
413
|
-
{/* Cast Breakdown Box */}
|
|
414
|
-
<div className="cs-export-report-box">
|
|
415
|
-
<div className="cs-export-report-header">
|
|
416
|
-
<span className="cs-export-report-title">Cast Breakdown</span>
|
|
417
|
-
<div className="cs-export-chips">
|
|
418
|
-
<button
|
|
419
|
-
type="button"
|
|
420
|
-
className={`cs-export-chip${format === 'cast-docx' ? ' cs-export-chip-active' : ''}`}
|
|
421
|
-
onClick={() => setFormat('cast-docx')}
|
|
422
|
-
>
|
|
423
|
-
Word (.docx)
|
|
424
|
-
</button>
|
|
425
|
-
<button
|
|
426
|
-
type="button"
|
|
427
|
-
className={`cs-export-chip${format === 'cast-csv' ? ' cs-export-chip-active' : ''}`}
|
|
428
|
-
onClick={() => setFormat('cast-csv')}
|
|
429
|
-
>
|
|
430
|
-
Spreadsheet (.csv)
|
|
431
|
-
</button>
|
|
432
|
-
</div>
|
|
433
|
-
</div>
|
|
434
|
-
<div style={{ fontSize: '11px', color: 'var(--cs-ui-text-light-secondary, #59574f)' }}>
|
|
435
|
-
Character occurrences, dialogue counts, and assigned actors list.
|
|
436
|
-
</div>
|
|
437
|
-
</div>
|
|
438
|
-
|
|
439
|
-
{hasIndicText && (
|
|
440
|
-
<div className="cs-settings-row" style={{ marginTop: '8px', padding: '10px', background: 'var(--cs-ui-bg-panel-subtle, #f4f2ea)', borderRadius: '6px', flexWrap: 'wrap', gap: '8px' }}>
|
|
441
|
-
<label className="cs-settings-label" htmlFor="cs-export-romanize-reports" style={{ fontSize: '12px' }}>
|
|
442
|
-
Romanized copy (ISO 15919)
|
|
443
|
-
</label>
|
|
444
|
-
<select
|
|
445
|
-
id="cs-export-romanize-reports"
|
|
446
|
-
className="cs-settings-select"
|
|
447
|
-
value={romanizeReports ? 'on' : 'off'}
|
|
448
|
-
onChange={e => setRomanizeReports(e.target.value === 'on')}
|
|
449
|
-
>
|
|
450
|
-
<option value="off">Off (native script)</option>
|
|
451
|
-
<option value="on">Romanize all text</option>
|
|
452
|
-
</select>
|
|
453
|
-
{romanizeReports && (
|
|
454
|
-
<select
|
|
455
|
-
aria-label="Romanization variant"
|
|
456
|
-
className="cs-settings-select"
|
|
457
|
-
value={romanizeVariant}
|
|
458
|
-
onChange={e => setRomanizeVariant(e.target.value as RomanizeVariant)}
|
|
459
|
-
>
|
|
460
|
-
<option value="strict">Strict ISO 15919</option>
|
|
461
|
-
<option value="colloquial">Casual (no diacritics)</option>
|
|
462
|
-
</select>
|
|
463
|
-
)}
|
|
464
|
-
</div>
|
|
465
|
-
)}
|
|
466
|
-
</div>
|
|
467
|
-
)}
|
|
468
|
-
|
|
469
|
-
{warnings.length > 0 && (
|
|
470
|
-
<div className="cs-export-warnings" style={{ background: '#fffbeb', border: '1px solid #fcd34d', padding: '10px', borderRadius: '4px', marginTop: '14px' }}>
|
|
471
|
-
<div className="cs-export-warn-title" style={{ fontWeight: 600, color: '#b45309', fontSize: '12px', marginBottom: '4px' }}>
|
|
472
|
-
{t('export.warnings')}
|
|
473
|
-
</div>
|
|
474
|
-
{warnings.map((w, i) => (
|
|
475
|
-
<div key={i} className="cs-export-warn-item" style={{ fontSize: '11px', color: '#59574f' }}>
|
|
476
|
-
• {w}
|
|
477
|
-
</div>
|
|
478
|
-
))}
|
|
479
|
-
</div>
|
|
480
|
-
)}
|
|
481
|
-
</div>
|
|
482
|
-
|
|
483
|
-
<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)' }}>
|
|
484
|
-
<button type="button" className="cs-confirm-btn-cancel" onClick={() => setExportVisible(false)}>
|
|
485
|
-
{t('export.cancel')}
|
|
486
|
-
</button>
|
|
487
|
-
<button type="button" className="cs-confirm-btn-primary" onClick={doExport} disabled={isExporting}>
|
|
488
|
-
{isExporting ? 'Exporting...' : t('export.download')}
|
|
489
|
-
</button>
|
|
490
|
-
</div>
|
|
491
|
-
</div>
|
|
492
|
-
</div>
|
|
493
|
-
)
|
|
494
|
-
}
|