@sutrata/editor 0.1.1 → 0.2.0

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.
Files changed (268) hide show
  1. package/README.md +87 -0
  2. package/dist/ai/VoiceConfirmDialog.d.ts +1 -0
  3. package/dist/ai/VoiceConfirmDialog.js +6 -6
  4. package/dist/ai/VoiceToolbar.d.ts +1 -0
  5. package/dist/ai/VoiceToolbar.js +22 -16
  6. package/dist/ai/clean-response.d.ts +7 -0
  7. package/dist/ai/clean-response.js +58 -0
  8. package/dist/ai/format-transcript.d.ts +3 -0
  9. package/dist/ai/format-transcript.js +7 -0
  10. package/dist/ai/prompts.d.ts +8 -1
  11. package/dist/ai/prompts.js +34 -7
  12. package/dist/autocomplete/suggestions.js +1 -1
  13. package/dist/context/DocumentContext.d.ts +28 -4
  14. package/dist/context/DocumentContext.js +67 -37
  15. package/dist/editor/EditorView.js +32 -4
  16. package/dist/editor/SelectionMenu.js +5 -3
  17. package/dist/editor/external-decorations-plugin.d.ts +23 -0
  18. package/dist/editor/external-decorations-plugin.js +151 -0
  19. package/dist/editor/omitted-scene-plugin.d.ts +9 -0
  20. package/dist/editor/omitted-scene-plugin.js +44 -0
  21. package/dist/editor/plugins.js +4 -0
  22. package/dist/editor/private-metadata-plugin.d.ts +10 -0
  23. package/dist/editor/private-metadata-plugin.js +60 -0
  24. package/dist/editor/prosemirror-to-sutra.js +179 -74
  25. package/dist/editor/scene-heading-view.js +2 -0
  26. package/dist/editor/schema.js +69 -12
  27. package/dist/editor/sutra-to-prosemirror.d.ts +2 -0
  28. package/dist/editor/sutra-to-prosemirror.js +32 -34
  29. package/dist/extensions/ai-provider.d.ts +44 -0
  30. package/dist/extensions/ai-provider.js +27 -0
  31. package/dist/extensions/collab.d.ts +21 -0
  32. package/dist/extensions/collab.js +6 -0
  33. package/dist/extensions/command-registry.d.ts +49 -0
  34. package/dist/extensions/command-registry.js +28 -0
  35. package/dist/extensions/decorations.d.ts +48 -0
  36. package/dist/extensions/decorations.js +6 -0
  37. package/dist/extensions/export-registry.d.ts +59 -0
  38. package/dist/extensions/export-registry.js +10 -0
  39. package/dist/extensions/panel-registry.d.ts +30 -0
  40. package/dist/extensions/panel-registry.js +16 -0
  41. package/dist/extensions/registries.d.ts +15 -0
  42. package/dist/extensions/registries.js +12 -0
  43. package/dist/extensions/registry-store.d.ts +26 -0
  44. package/dist/extensions/registry-store.js +35 -0
  45. package/dist/extensions/session.d.ts +29 -0
  46. package/dist/extensions/session.js +20 -0
  47. package/dist/extensions/speech-provider.d.ts +35 -0
  48. package/dist/extensions/speech-provider.js +17 -0
  49. package/dist/file/builtin-exporters.d.ts +9 -0
  50. package/dist/file/builtin-exporters.js +72 -0
  51. package/dist/file/docx-exporter.js +7 -4
  52. package/dist/file/docx-importer.d.ts +9 -0
  53. package/dist/file/docx-importer.js +47 -19
  54. package/dist/file/scene-number.d.ts +5 -0
  55. package/dist/file/scene-number.js +8 -0
  56. package/dist/file/workflow-reports.js +13 -7
  57. package/dist/findreplace/FindReplace.js +5 -2
  58. package/dist/import/AIImportDialog.d.ts +11 -0
  59. package/dist/import/AIImportDialog.js +112 -0
  60. package/dist/import/ai-import.d.ts +51 -0
  61. package/dist/import/ai-import.js +200 -0
  62. package/dist/import/apply-import.d.ts +7 -0
  63. package/dist/import/apply-import.js +35 -0
  64. package/dist/import/extract.d.ts +17 -0
  65. package/dist/import/extract.js +18 -0
  66. package/dist/index.d.ts +17 -2
  67. package/dist/index.js +7 -0
  68. package/dist/locales/bn.json +8 -0
  69. package/dist/locales/en.json +8 -0
  70. package/dist/locales/gu.json +8 -0
  71. package/dist/locales/hi.json +8 -0
  72. package/dist/locales/kn.json +8 -0
  73. package/dist/locales/ml.json +8 -0
  74. package/dist/locales/mr.json +8 -0
  75. package/dist/locales/or.json +8 -0
  76. package/dist/locales/pa.json +8 -0
  77. package/dist/locales/si.json +8 -0
  78. package/dist/locales/ta.json +8 -0
  79. package/dist/locales/te.json +8 -0
  80. package/dist/navigator/SceneNavigator.js +125 -131
  81. package/dist/navigator/StatisticsDialog.js +5 -1
  82. package/dist/navigator/scene-edits.d.ts +18 -0
  83. package/dist/navigator/scene-edits.js +143 -0
  84. package/dist/navigator/scene-list.js +4 -4
  85. package/dist/navigator/scene-numbering.d.ts +37 -0
  86. package/dist/navigator/scene-numbering.js +88 -0
  87. package/dist/settings/SettingsDialog.js +8 -120
  88. package/dist/shell/AppBar.js +21 -2
  89. package/dist/shell/AppShell.js +18 -4
  90. package/dist/shell/ElementToolbar.d.ts +5 -0
  91. package/dist/shell/ElementToolbar.js +35 -25
  92. package/dist/shell/LanguageStatus.js +6 -0
  93. package/dist/shell/StatusBar.js +11 -6
  94. package/dist/shell/builtin-commands.d.ts +10 -0
  95. package/dist/shell/builtin-commands.js +43 -0
  96. package/dist/shell/icons.d.ts +6 -0
  97. package/dist/shell/icons.js +6 -0
  98. package/dist/shell/use-commands.d.ts +7 -0
  99. package/dist/shell/use-commands.js +24 -0
  100. package/dist/shell/use-panel-context.d.ts +3 -0
  101. package/dist/shell/use-panel-context.js +39 -0
  102. package/dist/source/SourceView.js +18 -1
  103. package/dist/styles/screenplay.css +113 -1
  104. package/dist/titlepage/ExportDialog.d.ts +5 -0
  105. package/dist/titlepage/ExportDialog.js +59 -96
  106. package/package.json +32 -5
  107. package/dist/ai/OnboardingDialog.d.ts +0 -6
  108. package/dist/ai/OnboardingDialog.js +0 -115
  109. package/dist/ai/ai-client.d.ts +0 -71
  110. package/dist/ai/ai-client.js +0 -494
  111. package/dist/ai/generated-providers.json +0 -3696
  112. package/dist/ai/voice-service.d.ts +0 -15
  113. package/dist/ai/voice-service.js +0 -81
  114. package/dist/editor/character-view.d.ts +0 -35
  115. package/dist/editor/character-view.js +0 -112
  116. package/dist/editor/transliterate-input.d.ts +0 -10
  117. package/dist/editor/transliterate-input.js +0 -88
  118. package/dist/extensions/types.d.ts +0 -73
  119. package/dist/extensions/types.js +0 -12
  120. package/scripts/copy-assets.mjs +0 -16
  121. package/scripts/generate-providers.mjs +0 -127
  122. package/src/ai/OnboardingDialog.tsx +0 -390
  123. package/src/ai/VoiceConfirmDialog.tsx +0 -137
  124. package/src/ai/VoiceToolbar.tsx +0 -321
  125. package/src/ai/ai-client.ts +0 -596
  126. package/src/ai/commands.ts +0 -164
  127. package/src/ai/generated-providers.json +0 -3696
  128. package/src/ai/prompts.ts +0 -62
  129. package/src/ai/voice-service.ts +0 -96
  130. package/src/autocomplete/AutocompletePlugin.ts +0 -21
  131. package/src/autocomplete/suggestions.ts +0 -147
  132. package/src/context/DocumentContext.tsx +0 -648
  133. package/src/desktop-api.d.ts +0 -40
  134. package/src/editor/EditorView.tsx +0 -82
  135. package/src/editor/SelectionMenu.tsx +0 -152
  136. package/src/editor/attr-pencil-plugin.ts +0 -172
  137. package/src/editor/character-decoration-plugin.ts +0 -104
  138. package/src/editor/editor-bus.ts +0 -51
  139. package/src/editor/find-highlight-plugin.ts +0 -68
  140. package/src/editor/frontmatter-field-view.ts +0 -52
  141. package/src/editor/frontmatter-field.ts +0 -115
  142. package/src/editor/grapheme-cursor.ts +0 -99
  143. package/src/editor/ime-plugin.ts +0 -85
  144. package/src/editor/insert-helper.ts +0 -43
  145. package/src/editor/keymap.ts +0 -124
  146. package/src/editor/language-class-plugin.ts +0 -91
  147. package/src/editor/plugins.ts +0 -26
  148. package/src/editor/prosemirror-to-sutra.ts +0 -192
  149. package/src/editor/scene-heading-view.ts +0 -147
  150. package/src/editor/schema.ts +0 -156
  151. package/src/editor/sutra-to-prosemirror.ts +0 -156
  152. package/src/extensions/storage-adapter.ts +0 -65
  153. package/src/extensions/types.ts +0 -60
  154. package/src/file/docx-exporter.ts +0 -585
  155. package/src/file/docx-importer.ts +0 -313
  156. package/src/file/print-paginator.ts +0 -301
  157. package/src/file/templates.ts +0 -73
  158. package/src/file/workflow-reports.ts +0 -820
  159. package/src/findreplace/FindReplace.tsx +0 -242
  160. package/src/findreplace/find-engine.ts +0 -97
  161. package/src/findreplace/scroll-to-match.ts +0 -89
  162. package/src/fonts/tofu-detect.ts +0 -53
  163. package/src/i18n/LanguageContext.tsx +0 -207
  164. package/src/i18n/language-detect.ts +0 -73
  165. package/src/i18n/useTranslation.tsx +0 -74
  166. package/src/index.ts +0 -27
  167. package/src/locales/bn.json +0 -143
  168. package/src/locales/en.json +0 -143
  169. package/src/locales/gu.json +0 -143
  170. package/src/locales/hi.json +0 -143
  171. package/src/locales/kn.json +0 -143
  172. package/src/locales/ml.json +0 -143
  173. package/src/locales/mr.json +0 -143
  174. package/src/locales/or.json +0 -143
  175. package/src/locales/pa.json +0 -143
  176. package/src/locales/si.json +0 -143
  177. package/src/locales/ta.json +0 -143
  178. package/src/locales/te.json +0 -143
  179. package/src/navigator/SceneNavigator.tsx +0 -844
  180. package/src/navigator/StatisticsDialog.tsx +0 -249
  181. package/src/navigator/scene-list.ts +0 -84
  182. package/src/settings/SettingsDialog.tsx +0 -303
  183. package/src/shell/AppBar.tsx +0 -354
  184. package/src/shell/AppShell.tsx +0 -71
  185. package/src/shell/ConfirmDialog.tsx +0 -71
  186. package/src/shell/ElementToolbar.tsx +0 -175
  187. package/src/shell/LanguageStatus.tsx +0 -101
  188. package/src/shell/StatusBar.tsx +0 -135
  189. package/src/shell/SutrataLogo.tsx +0 -89
  190. package/src/shell/Toast.tsx +0 -46
  191. package/src/shell/ToolbarButton.tsx +0 -30
  192. package/src/shell/VersionHistory.tsx +0 -212
  193. package/src/shell/Watermark.tsx +0 -9
  194. package/src/shell/fonts/fonts.css +0 -1133
  195. package/src/shell/icons.tsx +0 -335
  196. package/src/shell/toolbar-actions.ts +0 -59
  197. package/src/source/SourceView.tsx +0 -111
  198. package/src/source/find-highlight-source.ts +0 -57
  199. package/src/source/sutra-lang.ts +0 -121
  200. package/src/spellcheck/dictionaries.ts +0 -21
  201. package/src/spellcheck/hunspell.ts +0 -110
  202. package/src/spellcheck/spellcheck-plugin.ts +0 -83
  203. package/src/styles/StyleDialog.tsx +0 -148
  204. package/src/styles/builtin-styles.ts +0 -139
  205. package/src/styles/css-adapter.ts +0 -76
  206. package/src/styles/docx-adapter.ts +0 -109
  207. package/src/styles/embed-fonts.ts +0 -81
  208. package/src/styles/pagination.ts +0 -27
  209. package/src/styles/print-adapter.ts +0 -288
  210. package/src/styles/registry.ts +0 -20
  211. package/src/styles/screenplay.css +0 -3006
  212. package/src/styles/script-ranges.ts +0 -17
  213. package/src/styles/types.ts +0 -104
  214. package/src/styles/validate.ts +0 -114
  215. package/src/templates/templates.ts +0 -194
  216. package/src/titlepage/ExportDialog.tsx +0 -494
  217. package/src/titlepage/TitlePageDialog.tsx +0 -312
  218. package/src/titlepage/frontmatter-form.ts +0 -280
  219. package/src/titlepage/languages.ts +0 -37
  220. package/src/types.ts +0 -7
  221. package/tests/AppBar.test.tsx +0 -35
  222. package/tests/AppShell.test.tsx +0 -22
  223. package/tests/ElementToolbar.test.tsx +0 -36
  224. package/tests/LanguageStatus.test.tsx +0 -129
  225. package/tests/SelectionMenu.test.tsx +0 -71
  226. package/tests/ToolbarButton.test.tsx +0 -24
  227. package/tests/ai-onboarding.test.ts +0 -102
  228. package/tests/ai-status.test.tsx +0 -72
  229. package/tests/ai-structured.test.ts +0 -94
  230. package/tests/autocomplete/AutocompletePlugin.test.ts +0 -20
  231. package/tests/autocomplete/suggestions.test.ts +0 -106
  232. package/tests/document-context.test.tsx +0 -565
  233. package/tests/docx-importer.test.ts +0 -160
  234. package/tests/editor/character-decoration-plugin.test.ts +0 -101
  235. package/tests/editor/character-extension.test.ts +0 -44
  236. package/tests/editor/grapheme-cursor.test.ts +0 -118
  237. package/tests/editor/ime-plugin.test.ts +0 -72
  238. package/tests/editor/inline-note-bridge.test.ts +0 -32
  239. package/tests/editor/language-class-plugin.test.ts +0 -265
  240. package/tests/editor/title-page.test.ts +0 -124
  241. package/tests/embed-fonts.test.ts +0 -17
  242. package/tests/exporters.test.ts +0 -365
  243. package/tests/findreplace/FindReplace.test.tsx +0 -391
  244. package/tests/findreplace/find-engine.test.ts +0 -86
  245. package/tests/findreplace/scroll-to-match.test.ts +0 -50
  246. package/tests/fonts/tofu-detect.test.ts +0 -122
  247. package/tests/i18n/LanguageContext.test.tsx +0 -146
  248. package/tests/i18n/language-detect.test.ts +0 -54
  249. package/tests/i18n/locale-parity.test.ts +0 -25
  250. package/tests/i18n/useTranslation.test.tsx +0 -45
  251. package/tests/keymap.test.ts +0 -18
  252. package/tests/navigator/SceneNavigator.test.tsx +0 -177
  253. package/tests/navigator/StatisticsDialog.test.tsx +0 -63
  254. package/tests/navigator/scene-list.test.ts +0 -71
  255. package/tests/print-paginator.test.ts +0 -82
  256. package/tests/shell/VersionHistory.test.tsx +0 -129
  257. package/tests/spellcheck/hunspell.test.ts +0 -82
  258. package/tests/spellcheck/spellcheck-plugin.test.ts +0 -56
  259. package/tests/styles.test.ts +0 -315
  260. package/tests/templates/templates.test.ts +0 -83
  261. package/tests/titlepage/TitlePageDialog.test.tsx +0 -90
  262. package/tests/titlepage/apply-title-page.test.ts +0 -61
  263. package/tests/titlepage/frontmatter-form.test.ts +0 -115
  264. package/tests/toolbar-actions.test.ts +0 -93
  265. package/tests/voice-commands.test.ts +0 -58
  266. package/tsconfig.json +0 -17
  267. package/vitest.config.ts +0 -11
  268. 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
- }