@sutrata/editor 0.1.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 (337) hide show
  1. package/LICENSE +674 -0
  2. package/dist/ai/OnboardingDialog.d.ts +6 -0
  3. package/dist/ai/OnboardingDialog.js +115 -0
  4. package/dist/ai/VoiceConfirmDialog.d.ts +9 -0
  5. package/dist/ai/VoiceConfirmDialog.js +37 -0
  6. package/dist/ai/VoiceToolbar.d.ts +2 -0
  7. package/dist/ai/VoiceToolbar.js +177 -0
  8. package/dist/ai/ai-client.d.ts +71 -0
  9. package/dist/ai/ai-client.js +494 -0
  10. package/dist/ai/commands.d.ts +12 -0
  11. package/dist/ai/commands.js +140 -0
  12. package/dist/ai/generated-providers.json +3696 -0
  13. package/dist/ai/prompts.d.ts +25 -0
  14. package/dist/ai/prompts.js +59 -0
  15. package/dist/ai/voice-service.d.ts +15 -0
  16. package/dist/ai/voice-service.js +81 -0
  17. package/dist/autocomplete/AutocompletePlugin.d.ts +2 -0
  18. package/dist/autocomplete/AutocompletePlugin.js +19 -0
  19. package/dist/autocomplete/suggestions.d.ts +25 -0
  20. package/dist/autocomplete/suggestions.js +126 -0
  21. package/dist/context/DocumentContext.d.ts +68 -0
  22. package/dist/context/DocumentContext.js +605 -0
  23. package/dist/desktop-api.d.ts +40 -0
  24. package/dist/editor/EditorView.d.ts +2 -0
  25. package/dist/editor/EditorView.js +78 -0
  26. package/dist/editor/SelectionMenu.d.ts +2 -0
  27. package/dist/editor/SelectionMenu.js +102 -0
  28. package/dist/editor/attr-pencil-plugin.d.ts +17 -0
  29. package/dist/editor/attr-pencil-plugin.js +158 -0
  30. package/dist/editor/character-decoration-plugin.d.ts +26 -0
  31. package/dist/editor/character-decoration-plugin.js +77 -0
  32. package/dist/editor/character-view.d.ts +35 -0
  33. package/dist/editor/character-view.js +112 -0
  34. package/dist/editor/editor-bus.d.ts +14 -0
  35. package/dist/editor/editor-bus.js +39 -0
  36. package/dist/editor/find-highlight-plugin.d.ts +7 -0
  37. package/dist/editor/find-highlight-plugin.js +57 -0
  38. package/dist/editor/frontmatter-field-view.d.ts +10 -0
  39. package/dist/editor/frontmatter-field-view.js +48 -0
  40. package/dist/editor/frontmatter-field.d.ts +21 -0
  41. package/dist/editor/frontmatter-field.js +116 -0
  42. package/dist/editor/grapheme-cursor.d.ts +10 -0
  43. package/dist/editor/grapheme-cursor.js +104 -0
  44. package/dist/editor/ime-plugin.d.ts +18 -0
  45. package/dist/editor/ime-plugin.js +71 -0
  46. package/dist/editor/insert-helper.d.ts +1 -0
  47. package/dist/editor/insert-helper.js +42 -0
  48. package/dist/editor/keymap.d.ts +3 -0
  49. package/dist/editor/keymap.js +131 -0
  50. package/dist/editor/language-class-plugin.d.ts +7 -0
  51. package/dist/editor/language-class-plugin.js +75 -0
  52. package/dist/editor/plugins.d.ts +1 -0
  53. package/dist/editor/plugins.js +25 -0
  54. package/dist/editor/prosemirror-to-sutra.d.ts +2 -0
  55. package/dist/editor/prosemirror-to-sutra.js +208 -0
  56. package/dist/editor/scene-heading-view.d.ts +27 -0
  57. package/dist/editor/scene-heading-view.js +136 -0
  58. package/dist/editor/schema.d.ts +3 -0
  59. package/dist/editor/schema.js +135 -0
  60. package/dist/editor/sutra-to-prosemirror.d.ts +3 -0
  61. package/dist/editor/sutra-to-prosemirror.js +154 -0
  62. package/dist/editor/transliterate-input.d.ts +10 -0
  63. package/dist/editor/transliterate-input.js +88 -0
  64. package/dist/extensions/storage-adapter.d.ts +59 -0
  65. package/dist/extensions/storage-adapter.js +9 -0
  66. package/dist/extensions/types.d.ts +73 -0
  67. package/dist/extensions/types.js +12 -0
  68. package/dist/file/docx-exporter.d.ts +8 -0
  69. package/dist/file/docx-exporter.js +494 -0
  70. package/dist/file/docx-importer.d.ts +10 -0
  71. package/dist/file/docx-importer.js +283 -0
  72. package/dist/file/print-paginator.d.ts +29 -0
  73. package/dist/file/print-paginator.js +289 -0
  74. package/dist/file/templates.d.ts +2 -0
  75. package/dist/file/templates.js +66 -0
  76. package/dist/file/workflow-reports.d.ts +34 -0
  77. package/dist/file/workflow-reports.js +719 -0
  78. package/dist/findreplace/FindReplace.d.ts +2 -0
  79. package/dist/findreplace/FindReplace.js +164 -0
  80. package/dist/findreplace/find-engine.d.ts +29 -0
  81. package/dist/findreplace/find-engine.js +69 -0
  82. package/dist/findreplace/scroll-to-match.d.ts +29 -0
  83. package/dist/findreplace/scroll-to-match.js +74 -0
  84. package/dist/fonts/tofu-detect.d.ts +1 -0
  85. package/dist/fonts/tofu-detect.js +48 -0
  86. package/dist/i18n/LanguageContext.d.ts +10 -0
  87. package/dist/i18n/LanguageContext.js +174 -0
  88. package/dist/i18n/language-detect.d.ts +1 -0
  89. package/dist/i18n/language-detect.js +58 -0
  90. package/dist/i18n/useTranslation.d.ts +11 -0
  91. package/dist/i18n/useTranslation.js +60 -0
  92. package/dist/index.d.ts +11 -0
  93. package/dist/index.js +5 -0
  94. package/dist/locales/bn.json +143 -0
  95. package/dist/locales/en.json +143 -0
  96. package/dist/locales/gu.json +143 -0
  97. package/dist/locales/hi.json +143 -0
  98. package/dist/locales/kn.json +143 -0
  99. package/dist/locales/ml.json +143 -0
  100. package/dist/locales/mr.json +143 -0
  101. package/dist/locales/or.json +143 -0
  102. package/dist/locales/pa.json +143 -0
  103. package/dist/locales/si.json +143 -0
  104. package/dist/locales/ta.json +143 -0
  105. package/dist/locales/te.json +143 -0
  106. package/dist/navigator/SceneNavigator.d.ts +2 -0
  107. package/dist/navigator/SceneNavigator.js +612 -0
  108. package/dist/navigator/StatisticsDialog.d.ts +6 -0
  109. package/dist/navigator/StatisticsDialog.js +146 -0
  110. package/dist/navigator/scene-list.d.ts +16 -0
  111. package/dist/navigator/scene-list.js +70 -0
  112. package/dist/settings/SettingsDialog.d.ts +6 -0
  113. package/dist/settings/SettingsDialog.js +147 -0
  114. package/dist/shell/AppBar.d.ts +2 -0
  115. package/dist/shell/AppBar.js +34 -0
  116. package/dist/shell/AppShell.d.ts +2 -0
  117. package/dist/shell/AppShell.js +30 -0
  118. package/dist/shell/ConfirmDialog.d.ts +16 -0
  119. package/dist/shell/ConfirmDialog.js +23 -0
  120. package/dist/shell/ElementToolbar.d.ts +2 -0
  121. package/dist/shell/ElementToolbar.js +30 -0
  122. package/dist/shell/LanguageStatus.d.ts +2 -0
  123. package/dist/shell/LanguageStatus.js +65 -0
  124. package/dist/shell/StatusBar.d.ts +2 -0
  125. package/dist/shell/StatusBar.js +54 -0
  126. package/dist/shell/SutrataLogo.d.ts +8 -0
  127. package/dist/shell/SutrataLogo.js +7 -0
  128. package/dist/shell/Toast.d.ts +12 -0
  129. package/dist/shell/Toast.js +21 -0
  130. package/dist/shell/ToolbarButton.d.ts +11 -0
  131. package/dist/shell/ToolbarButton.js +4 -0
  132. package/dist/shell/VersionHistory.d.ts +12 -0
  133. package/dist/shell/VersionHistory.js +89 -0
  134. package/dist/shell/Watermark.d.ts +3 -0
  135. package/dist/shell/Watermark.js +10 -0
  136. package/dist/shell/fonts/fonts.css +1133 -0
  137. package/dist/shell/icons.d.ts +45 -0
  138. package/dist/shell/icons.js +51 -0
  139. package/dist/shell/toolbar-actions.d.ts +6 -0
  140. package/dist/shell/toolbar-actions.js +58 -0
  141. package/dist/source/SourceView.d.ts +1 -0
  142. package/dist/source/SourceView.js +106 -0
  143. package/dist/source/find-highlight-source.d.ts +14 -0
  144. package/dist/source/find-highlight-source.js +47 -0
  145. package/dist/source/sutra-lang.d.ts +2 -0
  146. package/dist/source/sutra-lang.js +97 -0
  147. package/dist/spellcheck/dictionaries.d.ts +9 -0
  148. package/dist/spellcheck/dictionaries.js +16 -0
  149. package/dist/spellcheck/hunspell.d.ts +23 -0
  150. package/dist/spellcheck/hunspell.js +94 -0
  151. package/dist/spellcheck/spellcheck-plugin.d.ts +18 -0
  152. package/dist/spellcheck/spellcheck-plugin.js +75 -0
  153. package/dist/styles/StyleDialog.d.ts +6 -0
  154. package/dist/styles/StyleDialog.js +63 -0
  155. package/dist/styles/builtin-styles.d.ts +43 -0
  156. package/dist/styles/builtin-styles.js +131 -0
  157. package/dist/styles/css-adapter.d.ts +32 -0
  158. package/dist/styles/css-adapter.js +71 -0
  159. package/dist/styles/docx-adapter.d.ts +7 -0
  160. package/dist/styles/docx-adapter.js +84 -0
  161. package/dist/styles/embed-fonts.d.ts +20 -0
  162. package/dist/styles/embed-fonts.js +81 -0
  163. package/dist/styles/pagination.d.ts +19 -0
  164. package/dist/styles/pagination.js +22 -0
  165. package/dist/styles/print-adapter.d.ts +6 -0
  166. package/dist/styles/print-adapter.js +288 -0
  167. package/dist/styles/registry.d.ts +6 -0
  168. package/dist/styles/registry.js +14 -0
  169. package/dist/styles/screenplay.css +3006 -0
  170. package/dist/styles/script-ranges.d.ts +6 -0
  171. package/dist/styles/script-ranges.js +17 -0
  172. package/dist/styles/types.d.ts +93 -0
  173. package/dist/styles/types.js +18 -0
  174. package/dist/styles/validate.d.ts +12 -0
  175. package/dist/styles/validate.js +105 -0
  176. package/dist/templates/templates.d.ts +10 -0
  177. package/dist/templates/templates.js +181 -0
  178. package/dist/titlepage/ExportDialog.d.ts +1 -0
  179. package/dist/titlepage/ExportDialog.js +152 -0
  180. package/dist/titlepage/TitlePageDialog.d.ts +6 -0
  181. package/dist/titlepage/TitlePageDialog.js +96 -0
  182. package/dist/titlepage/frontmatter-form.d.ts +56 -0
  183. package/dist/titlepage/frontmatter-form.js +245 -0
  184. package/dist/titlepage/languages.d.ts +10 -0
  185. package/dist/titlepage/languages.js +31 -0
  186. package/dist/types.d.ts +6 -0
  187. package/dist/types.js +1 -0
  188. package/package.json +47 -0
  189. package/scripts/copy-assets.mjs +16 -0
  190. package/scripts/generate-providers.mjs +127 -0
  191. package/src/ai/OnboardingDialog.tsx +390 -0
  192. package/src/ai/VoiceConfirmDialog.tsx +137 -0
  193. package/src/ai/VoiceToolbar.tsx +321 -0
  194. package/src/ai/ai-client.ts +596 -0
  195. package/src/ai/commands.ts +164 -0
  196. package/src/ai/generated-providers.json +3696 -0
  197. package/src/ai/prompts.ts +62 -0
  198. package/src/ai/voice-service.ts +96 -0
  199. package/src/autocomplete/AutocompletePlugin.ts +21 -0
  200. package/src/autocomplete/suggestions.ts +147 -0
  201. package/src/context/DocumentContext.tsx +648 -0
  202. package/src/desktop-api.d.ts +40 -0
  203. package/src/editor/EditorView.tsx +82 -0
  204. package/src/editor/SelectionMenu.tsx +152 -0
  205. package/src/editor/attr-pencil-plugin.ts +172 -0
  206. package/src/editor/character-decoration-plugin.ts +104 -0
  207. package/src/editor/editor-bus.ts +51 -0
  208. package/src/editor/find-highlight-plugin.ts +68 -0
  209. package/src/editor/frontmatter-field-view.ts +52 -0
  210. package/src/editor/frontmatter-field.ts +115 -0
  211. package/src/editor/grapheme-cursor.ts +99 -0
  212. package/src/editor/ime-plugin.ts +85 -0
  213. package/src/editor/insert-helper.ts +43 -0
  214. package/src/editor/keymap.ts +124 -0
  215. package/src/editor/language-class-plugin.ts +91 -0
  216. package/src/editor/plugins.ts +26 -0
  217. package/src/editor/prosemirror-to-sutra.ts +192 -0
  218. package/src/editor/scene-heading-view.ts +147 -0
  219. package/src/editor/schema.ts +156 -0
  220. package/src/editor/sutra-to-prosemirror.ts +156 -0
  221. package/src/extensions/storage-adapter.ts +65 -0
  222. package/src/extensions/types.ts +60 -0
  223. package/src/file/docx-exporter.ts +585 -0
  224. package/src/file/docx-importer.ts +313 -0
  225. package/src/file/print-paginator.ts +301 -0
  226. package/src/file/templates.ts +73 -0
  227. package/src/file/workflow-reports.ts +820 -0
  228. package/src/findreplace/FindReplace.tsx +242 -0
  229. package/src/findreplace/find-engine.ts +97 -0
  230. package/src/findreplace/scroll-to-match.ts +89 -0
  231. package/src/fonts/tofu-detect.ts +53 -0
  232. package/src/i18n/LanguageContext.tsx +207 -0
  233. package/src/i18n/language-detect.ts +73 -0
  234. package/src/i18n/useTranslation.tsx +74 -0
  235. package/src/index.ts +27 -0
  236. package/src/locales/bn.json +143 -0
  237. package/src/locales/en.json +143 -0
  238. package/src/locales/gu.json +143 -0
  239. package/src/locales/hi.json +143 -0
  240. package/src/locales/kn.json +143 -0
  241. package/src/locales/ml.json +143 -0
  242. package/src/locales/mr.json +143 -0
  243. package/src/locales/or.json +143 -0
  244. package/src/locales/pa.json +143 -0
  245. package/src/locales/si.json +143 -0
  246. package/src/locales/ta.json +143 -0
  247. package/src/locales/te.json +143 -0
  248. package/src/navigator/SceneNavigator.tsx +844 -0
  249. package/src/navigator/StatisticsDialog.tsx +249 -0
  250. package/src/navigator/scene-list.ts +84 -0
  251. package/src/settings/SettingsDialog.tsx +303 -0
  252. package/src/shell/AppBar.tsx +354 -0
  253. package/src/shell/AppShell.tsx +71 -0
  254. package/src/shell/ConfirmDialog.tsx +71 -0
  255. package/src/shell/ElementToolbar.tsx +175 -0
  256. package/src/shell/LanguageStatus.tsx +101 -0
  257. package/src/shell/StatusBar.tsx +135 -0
  258. package/src/shell/SutrataLogo.tsx +89 -0
  259. package/src/shell/Toast.tsx +46 -0
  260. package/src/shell/ToolbarButton.tsx +30 -0
  261. package/src/shell/VersionHistory.tsx +212 -0
  262. package/src/shell/Watermark.tsx +9 -0
  263. package/src/shell/fonts/fonts.css +1133 -0
  264. package/src/shell/icons.tsx +335 -0
  265. package/src/shell/toolbar-actions.ts +59 -0
  266. package/src/source/SourceView.tsx +111 -0
  267. package/src/source/find-highlight-source.ts +57 -0
  268. package/src/source/sutra-lang.ts +121 -0
  269. package/src/spellcheck/dictionaries.ts +21 -0
  270. package/src/spellcheck/hunspell.ts +110 -0
  271. package/src/spellcheck/spellcheck-plugin.ts +83 -0
  272. package/src/styles/StyleDialog.tsx +148 -0
  273. package/src/styles/builtin-styles.ts +139 -0
  274. package/src/styles/css-adapter.ts +76 -0
  275. package/src/styles/docx-adapter.ts +109 -0
  276. package/src/styles/embed-fonts.ts +81 -0
  277. package/src/styles/pagination.ts +27 -0
  278. package/src/styles/print-adapter.ts +288 -0
  279. package/src/styles/registry.ts +20 -0
  280. package/src/styles/screenplay.css +3006 -0
  281. package/src/styles/script-ranges.ts +17 -0
  282. package/src/styles/types.ts +104 -0
  283. package/src/styles/validate.ts +114 -0
  284. package/src/templates/templates.ts +194 -0
  285. package/src/titlepage/ExportDialog.tsx +494 -0
  286. package/src/titlepage/TitlePageDialog.tsx +312 -0
  287. package/src/titlepage/frontmatter-form.ts +280 -0
  288. package/src/titlepage/languages.ts +37 -0
  289. package/src/types.ts +7 -0
  290. package/tests/AppBar.test.tsx +35 -0
  291. package/tests/AppShell.test.tsx +22 -0
  292. package/tests/ElementToolbar.test.tsx +36 -0
  293. package/tests/LanguageStatus.test.tsx +129 -0
  294. package/tests/SelectionMenu.test.tsx +71 -0
  295. package/tests/ToolbarButton.test.tsx +24 -0
  296. package/tests/ai-onboarding.test.ts +102 -0
  297. package/tests/ai-status.test.tsx +72 -0
  298. package/tests/ai-structured.test.ts +94 -0
  299. package/tests/autocomplete/AutocompletePlugin.test.ts +20 -0
  300. package/tests/autocomplete/suggestions.test.ts +106 -0
  301. package/tests/document-context.test.tsx +565 -0
  302. package/tests/docx-importer.test.ts +160 -0
  303. package/tests/editor/character-decoration-plugin.test.ts +101 -0
  304. package/tests/editor/character-extension.test.ts +44 -0
  305. package/tests/editor/grapheme-cursor.test.ts +118 -0
  306. package/tests/editor/ime-plugin.test.ts +72 -0
  307. package/tests/editor/inline-note-bridge.test.ts +32 -0
  308. package/tests/editor/language-class-plugin.test.ts +265 -0
  309. package/tests/editor/title-page.test.ts +124 -0
  310. package/tests/embed-fonts.test.ts +17 -0
  311. package/tests/exporters.test.ts +365 -0
  312. package/tests/findreplace/FindReplace.test.tsx +391 -0
  313. package/tests/findreplace/find-engine.test.ts +86 -0
  314. package/tests/findreplace/scroll-to-match.test.ts +50 -0
  315. package/tests/fonts/tofu-detect.test.ts +122 -0
  316. package/tests/i18n/LanguageContext.test.tsx +146 -0
  317. package/tests/i18n/language-detect.test.ts +54 -0
  318. package/tests/i18n/locale-parity.test.ts +25 -0
  319. package/tests/i18n/useTranslation.test.tsx +45 -0
  320. package/tests/keymap.test.ts +18 -0
  321. package/tests/navigator/SceneNavigator.test.tsx +177 -0
  322. package/tests/navigator/StatisticsDialog.test.tsx +63 -0
  323. package/tests/navigator/scene-list.test.ts +71 -0
  324. package/tests/print-paginator.test.ts +82 -0
  325. package/tests/shell/VersionHistory.test.tsx +129 -0
  326. package/tests/spellcheck/hunspell.test.ts +82 -0
  327. package/tests/spellcheck/spellcheck-plugin.test.ts +56 -0
  328. package/tests/styles.test.ts +315 -0
  329. package/tests/templates/templates.test.ts +83 -0
  330. package/tests/titlepage/TitlePageDialog.test.tsx +90 -0
  331. package/tests/titlepage/apply-title-page.test.ts +61 -0
  332. package/tests/titlepage/frontmatter-form.test.ts +115 -0
  333. package/tests/toolbar-actions.test.ts +93 -0
  334. package/tests/voice-commands.test.ts +58 -0
  335. package/tsconfig.json +17 -0
  336. package/vitest.config.ts +11 -0
  337. package/vitest.setup.ts +65 -0
@@ -0,0 +1,17 @@
1
+ /** Unicode block ranges for the complex scripts Sutrata recognizes, keyed by the same
2
+ * lang codes used throughout (docx-exporter.ts's SCRIPT_FONT_BY_LANG, types.ts's
3
+ * COMPLEX_SCRIPT_CODES, complexScriptFontOverrides / italicScripts). Shared so
4
+ * docx-exporter.ts (font override) and workflow-reports.ts (forced-italic wrapping in the
5
+ * print/PDF HTML) test the same ranges rather than maintaining two copies that could drift. */
6
+ export const SCRIPT_UNICODE_RANGES: Record<string, RegExp> = {
7
+ hi: /[ऀ-ॿ]/,
8
+ ta: /[஀-௿]/,
9
+ te: /[ఀ-౿]/,
10
+ kn: /[ಀ-೿]/,
11
+ ml: /[ഀ-ൿ]/,
12
+ bn: /[ঀ-৿]/,
13
+ gu: /[઀-૿]/,
14
+ pa: /[਀-੿]/,
15
+ or: /[଀-୿]/,
16
+ si: /[඀-෿]/,
17
+ }
@@ -0,0 +1,104 @@
1
+ /** Screenplay body elements that carry a visual style — one entry per docx-exporter.ts
2
+ * paragraph style / screenplay.css class. Excludes structural/chrome things
3
+ * (scene metadata, page breaks, parenthetical parens) that aren't stylistic choices. */
4
+ export const BODY_ELEMENT_KEYS = [
5
+ 'sceneHeading', 'action', 'character', 'parenthetical', 'dialogue',
6
+ 'transition', 'centered', 'lyrics', 'section', 'note', 'comment',
7
+ ] as const
8
+ export type BodyElementKey = typeof BODY_ELEMENT_KEYS[number]
9
+
10
+ /** Cover-page elements. Export-only (DOCX/PDF) — there is no live editor
11
+ * preview of the cover page today, so these never produce editor CSS vars. */
12
+ export const COVER_ELEMENT_KEYS = ['title', 'author', 'logline'] as const
13
+ export type CoverElementKey = typeof COVER_ELEMENT_KEYS[number]
14
+
15
+ export interface ElementStyle {
16
+ fontSizePt: number
17
+ bold?: boolean
18
+ italic?: boolean
19
+ underline?: boolean
20
+ /** 6 hex digits, no leading '#' (docx-native shape; CSS adapter adds the '#'). */
21
+ color?: string
22
+ /** Percentage of the printable content width. */
23
+ marginLeftPct?: number
24
+ marginRightPct?: number
25
+ align?: 'left' | 'center' | 'right'
26
+ /** Space before/after the paragraph, in points. DOCX-only: the live editor
27
+ * only ever applies `spaceBeforePt` (as margin-top); adjacent elements in
28
+ * the editor create their gap from the *following* element's margin-top,
29
+ * not from a margin-bottom on the preceding one, so `spaceAfterPt` has no
30
+ * editor-CSS effect — see css-adapter.ts. */
31
+ spaceBeforePt?: number
32
+ spaceAfterPt?: number
33
+ }
34
+
35
+ export type ScreenplayElements =
36
+ Record<BodyElementKey, ElementStyle> & Record<CoverElementKey, ElementStyle>
37
+
38
+ /** How the exported cover page arranges its blocks (DOCX + PDF; there is no editor
39
+ * preview of the cover). 'centered' stacks everything down the middle of the page;
40
+ * 'corners' puts title / "by <author>" / logline flush top-left and date + contact
41
+ * down in the bottom-right, the arrangement Tamil shooting scripts print. Per-element
42
+ * font/size/color still come from `elements.title|author|logline` either way — this
43
+ * chooses the *placement* those elements are laid out in, nothing else. */
44
+ export type CoverLayout = 'centered' | 'corners'
45
+ export const COVER_LAYOUTS: readonly CoverLayout[] = ['centered', 'corners']
46
+ export const DEFAULT_COVER_LAYOUT: CoverLayout = 'centered'
47
+
48
+ export interface ScreenplayStyleDefinition {
49
+ /** Stable slug: persisted in frontmatter's `style:` field and as the IndexedDB key. */
50
+ id: string
51
+ name: string
52
+ description?: string
53
+ /** Latin body font. For the live editor and PDF/print export this is just the
54
+ * first entry in a CSS font stack (screenplay.css / print-adapter.ts put
55
+ * 'Noto Sans'/script-specific Noto Sans fonts after it), so the browser's own
56
+ * per-glyph fallback transparently covers any script this font doesn't have —
57
+ * no per-style CSS plumbing needed. DOCX has no such per-glyph fallback
58
+ * (a `w:rFonts` run property names exactly one font), so a style whose body
59
+ * font *does* cover a non-Latin script it cares about (e.g. Traditional's
60
+ * Vijaya for Tamil) must say so explicitly via `complexScriptFontOverrides`. */
61
+ fontFamily: string
62
+ /** Per-script font override, keyed by the same lang codes docx-exporter.ts's
63
+ * getComplexFont switches on ('ta', 'hi', 'te', ...). Omit a script and it keeps
64
+ * using the shared default (e.g. 'Noto Sans Tamil') — this is an opt-in exception,
65
+ * not a requirement to theme every script. Consumed by all three renderers:
66
+ * DOCX (docx-exporter.ts, where it's required — a `w:rFonts` run property names
67
+ * exactly one font, so there's no per-glyph fallback to lean on), the live editor
68
+ * (css-adapter.ts turns each entry into a `--cs-lang-<code>-font-family` CSS var
69
+ * that screenplay.css's `.cs-lang-<code>` rules prefer over the generic Noto Sans
70
+ * fallback), and PDF/print (print-adapter.ts folds the values into the body font
71
+ * stack ahead of the generic Indic fallbacks). */
72
+ complexScriptFontOverrides?: Record<string, string>
73
+ /** Lang codes (same set as above) whose *default* presentation is italic under this
74
+ * style — not "emphasized text happens to be italic", but "upright is not this
75
+ * script's normal look here" (e.g. Traditional's Tamil, styled after Noto Serif
76
+ * Tamil Italic to approximate Vijaya's slant). Forces italic even where the
77
+ * element itself isn't (bold scene headings included) — confirmed as the intended
78
+ * behavior, not layered underneath per-element italic. DOCX applies this
79
+ * unconditionally per run; the editor applies it via a `.cs-force-italic-<code>`
80
+ * class (see EditorView.tsx/css-adapter.ts) rather than a plain CSS var, because a
81
+ * var would unconditionally set `font-style` and stomp elements that are
82
+ * legitimately italic for their own reasons (lyrics, comments) on styles that
83
+ * don't list the script here. PDF/print has no per-span language tagging, so this
84
+ * has no effect there — a known, documented gap, not an oversight. */
85
+ italicScripts?: string[]
86
+ /** Defaults to DEFAULT_COVER_LAYOUT when a style (or an imported one) omits it. */
87
+ coverLayout?: CoverLayout
88
+ page: {
89
+ marginTopIn: number
90
+ marginBottomIn: number
91
+ marginLeftIn: number
92
+ marginRightIn: number
93
+ }
94
+ elements: ScreenplayElements
95
+ }
96
+
97
+ export const DEFAULT_STYLE_ID = 'classic'
98
+
99
+ /** Lang codes docx-exporter.ts's getComplexFont / SCRIPT_RANGES recognize — the full
100
+ * key space for `complexScriptFontOverrides` / `italicScripts`. Iterated by
101
+ * css-adapter.ts to clear a previous style's per-script vars/classes when switching
102
+ * to a style that doesn't set them (so nothing lingers). */
103
+ export const COMPLEX_SCRIPT_CODES = ['hi', 'ta', 'te', 'kn', 'ml', 'bn', 'gu', 'pa', 'or', 'si'] as const
104
+ export type ComplexScriptCode = typeof COMPLEX_SCRIPT_CODES[number]
@@ -0,0 +1,114 @@
1
+ import { BODY_ELEMENT_KEYS, COVER_ELEMENT_KEYS, COVER_LAYOUTS } from './types.js'
2
+ import type { ScreenplayStyleDefinition } from './types.js'
3
+
4
+ export type ValidationResult =
5
+ | { ok: true; style: ScreenplayStyleDefinition }
6
+ | { ok: false; errors: string[] }
7
+
8
+ const ALIGNS = new Set(['left', 'center', 'right'])
9
+ const COLOR_RE = /^[0-9a-fA-F]{6}$/
10
+
11
+ function isFiniteNumber(v: unknown): v is number {
12
+ return typeof v === 'number' && Number.isFinite(v)
13
+ }
14
+
15
+ function validateElement(path: string, data: unknown, errors: string[]): void {
16
+ if (typeof data !== 'object' || data === null) {
17
+ errors.push(`${path}: expected an object`)
18
+ return
19
+ }
20
+ const el = data as Record<string, unknown>
21
+ if (!isFiniteNumber(el['fontSizePt']) || el['fontSizePt'] <= 0) {
22
+ errors.push(`${path}.fontSizePt: expected a positive number`)
23
+ }
24
+ for (const flag of ['bold', 'italic', 'underline'] as const) {
25
+ if (el[flag] !== undefined && typeof el[flag] !== 'boolean') {
26
+ errors.push(`${path}.${flag}: expected a boolean`)
27
+ }
28
+ }
29
+ if (el['color'] !== undefined && (typeof el['color'] !== 'string' || !COLOR_RE.test(el['color']))) {
30
+ errors.push(`${path}.color: expected 6 hex digits with no '#' (e.g. "0A5C6A")`)
31
+ }
32
+ for (const marginKey of ['marginLeftPct', 'marginRightPct'] as const) {
33
+ const v = el[marginKey]
34
+ if (v !== undefined && (!isFiniteNumber(v) || v < 0 || v > 100)) {
35
+ errors.push(`${path}.${marginKey}: expected a number between 0 and 100`)
36
+ }
37
+ }
38
+ if (el['align'] !== undefined && (typeof el['align'] !== 'string' || !ALIGNS.has(el['align']))) {
39
+ errors.push(`${path}.align: expected "left", "center", or "right"`)
40
+ }
41
+ for (const spaceKey of ['spaceBeforePt', 'spaceAfterPt'] as const) {
42
+ const v = el[spaceKey]
43
+ if (v !== undefined && !isFiniteNumber(v)) {
44
+ errors.push(`${path}.${spaceKey}: expected a number`)
45
+ }
46
+ }
47
+ }
48
+
49
+ /** Hand-rolled structural validation for a style imported from a .json file — no schema
50
+ * library dependency, matching the codebase's other hand-rolled parsers (frontmatter.ts).
51
+ * Collects every error found rather than failing on the first, for a useful import message. */
52
+ export function validateStyleJson(data: unknown): ValidationResult {
53
+ const errors: string[] = []
54
+ if (typeof data !== 'object' || data === null) {
55
+ return { ok: false, errors: ['Expected a JSON object'] }
56
+ }
57
+ const obj = data as Record<string, unknown>
58
+
59
+ for (const key of ['id', 'name', 'fontFamily'] as const) {
60
+ if (typeof obj[key] !== 'string' || obj[key] === '') {
61
+ errors.push(`${key}: expected a non-empty string`)
62
+ }
63
+ }
64
+ if (obj['description'] !== undefined && typeof obj['description'] !== 'string') {
65
+ errors.push('description: expected a string')
66
+ }
67
+ if (obj['complexScriptFontOverrides'] !== undefined) {
68
+ const overrides = obj['complexScriptFontOverrides']
69
+ if (typeof overrides !== 'object' || overrides === null) {
70
+ errors.push('complexScriptFontOverrides: expected an object')
71
+ } else if (Object.values(overrides).some(v => typeof v !== 'string' || v === '')) {
72
+ errors.push('complexScriptFontOverrides: expected every value to be a non-empty string')
73
+ }
74
+ }
75
+ if (obj['italicScripts'] !== undefined) {
76
+ const scripts = obj['italicScripts']
77
+ if (!Array.isArray(scripts) || scripts.some(v => typeof v !== 'string' || v === '')) {
78
+ errors.push('italicScripts: expected an array of non-empty strings')
79
+ }
80
+ }
81
+
82
+ if (obj['coverLayout'] !== undefined && !COVER_LAYOUTS.includes(obj['coverLayout'] as never)) {
83
+ errors.push(`coverLayout: expected ${COVER_LAYOUTS.map(l => `"${l}"`).join(' or ')}`)
84
+ }
85
+
86
+ const page = obj['page']
87
+ if (typeof page !== 'object' || page === null) {
88
+ errors.push('page: expected an object')
89
+ } else {
90
+ const p = page as Record<string, unknown>
91
+ for (const key of ['marginTopIn', 'marginBottomIn', 'marginLeftIn', 'marginRightIn'] as const) {
92
+ if (!isFiniteNumber(p[key]) || p[key] < 0) {
93
+ errors.push(`page.${key}: expected a non-negative number`)
94
+ }
95
+ }
96
+ }
97
+
98
+ const elements = obj['elements']
99
+ if (typeof elements !== 'object' || elements === null) {
100
+ errors.push('elements: expected an object')
101
+ } else {
102
+ const el = elements as Record<string, unknown>
103
+ for (const key of [...BODY_ELEMENT_KEYS, ...COVER_ELEMENT_KEYS]) {
104
+ if (!(key in el)) {
105
+ errors.push(`elements.${key}: missing`)
106
+ } else {
107
+ validateElement(`elements.${key}`, el[key], errors)
108
+ }
109
+ }
110
+ }
111
+
112
+ if (errors.length > 0) return { ok: false, errors }
113
+ return { ok: true, style: obj as unknown as ScreenplayStyleDefinition }
114
+ }
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Localized screenplay templates for each supported script.
3
+ * Returns a starter Sutra document in the target language.
4
+ */
5
+
6
+ export const TEMPLATES: Record<string, string> = {
7
+ en: `---
8
+ title: Untitled Screenplay
9
+ lang: en
10
+ ---
11
+
12
+ ## INT. ROOM - DAY
13
+
14
+ A writer sits at a desk, staring at a blank screen.
15
+
16
+ @WRITER
17
+ (to themselves)
18
+ Where do I begin?
19
+
20
+ `,
21
+
22
+ hi: `---
23
+ title: अनाम पटकथा
24
+ lang: hi
25
+ ---
26
+
27
+ ## आंत. कमरा - दिन
28
+
29
+ एक लेखक मेज पर बैठा है, खाली स्क्रीन को देख रहा है।
30
+
31
+ @लेखक
32
+ (खुद से)
33
+ मैं कहाँ से शुरू करूँ?
34
+
35
+ `,
36
+
37
+ ta: `---
38
+ title: தலைப்பற்ற திரைக்கதை
39
+ lang: ta
40
+ ---
41
+
42
+ ## உள்ளே. அறை - பகல்
43
+
44
+ ஒரு எழுத்தாளர் மேசையில் அமர்ந்து, வெற்று திரையை பார்க்கிறார்.
45
+
46
+ @எழுத்தாளர்
47
+ (தனக்கே)
48
+ நான் எங்கிருந்து தொடங்குவது?
49
+
50
+ `,
51
+
52
+ te: `---
53
+ title: పేరులేని స్క్రీన్‌ప్లే
54
+ lang: te
55
+ ---
56
+
57
+ ## లోపల. గది - పగలు
58
+
59
+ ఒక రచయిత బల్లదగ్గర కూర్చుని, ఖాళీ స్క్రీన్‌ని చూస్తున్నారు.
60
+
61
+ @రచయిత
62
+ (తమలో తాము)
63
+ నేను ఎక్కడ నుండి ప్రారంభించాలి?
64
+
65
+ `,
66
+
67
+ kn: `---
68
+ title: ಹೆಸರಿಲ್ಲದ ಚಲನಚಿತ್ರ ಕಥೆ
69
+ lang: kn
70
+ ---
71
+
72
+ ## ಒಳಗೆ. ಕೋಣೆ - ಹಗಲು
73
+
74
+ ಒಬ್ಬ ಬರಹಗಾರ ಮೇಜಿನ ಬಳಿ ಕುಳಿತು, ಖಾಲಿ ಪರದೆಯನ್ನು ನೋಡುತ್ತಿದ್ದಾರೆ.
75
+
76
+ @ಬರಹಗಾರ
77
+ (ತಮ್ಮಷ್ಟಕ್ಕೆ)
78
+ ನಾನು ಎಲ್ಲಿಂದ ಪ್ರಾರಂಭಿಸಲಿ?
79
+
80
+ `,
81
+
82
+ ml: `---
83
+ title: പേരില്ലാത്ത തിരക്കഥ
84
+ lang: ml
85
+ ---
86
+
87
+ ## അകത്ത്. മുറി - പകൽ
88
+
89
+ ഒരു എഴുത്തുകാരൻ മേശയ്ക്കരുകിൽ ഇരുന്ന്, ശൂന്യമായ സ്ക്രീൻ നോക്കുന്നു.
90
+
91
+ @എഴുത്തുകാരൻ
92
+ (സ്വയം)
93
+ ഞാൻ എവിടെ നിന്ന് തുടങ്ങണം?
94
+
95
+ `,
96
+
97
+ bn: `---
98
+ title: নামহীন চিত্রনাট্য
99
+ lang: bn
100
+ ---
101
+
102
+ ## ভেতরে. ঘর - দিন
103
+
104
+ একজন লেখক টেবিলের পাশে বসে, ফাঁকা পর্দার দিকে তাকিয়ে আছেন।
105
+
106
+ @লেখক
107
+ (নিজের মনে)
108
+ আমি কোথা থেকে শুরু করব?
109
+
110
+ `,
111
+
112
+ gu: `---
113
+ title: અનામ પટકથા
114
+ lang: gu
115
+ ---
116
+
117
+ ## અંદર. ઓરડો - દિવસ
118
+
119
+ એક લેખક ટેબલ પર બેઠો છે, ખાલી સ્ક્રીન જોઈ રહ્યો છે.
120
+
121
+ @લેખક
122
+ (પોતાની જાતને)
123
+ હું ક્યાંથી શરૂ કરું?
124
+
125
+ `,
126
+
127
+ mr: `---
128
+ title: अनाम पटकथा
129
+ lang: mr
130
+ ---
131
+
132
+ ## आत. खोली - दिवस
133
+
134
+ एक लेखक टेबलावर बसला आहे, रिकाम्या स्क्रीनकडे पाहत आहे.
135
+
136
+ @लेखक
137
+ (स्वतःशी)
138
+ मी कुठून सुरुवात करू?
139
+
140
+ `,
141
+
142
+ pa: `---
143
+ title: ਬੇਨਾਮ ਸਕ੍ਰੀਨਪਲੇ
144
+ lang: pa
145
+ ---
146
+
147
+ ## ਅੰਦਰ. ਕਮਰਾ - ਦਿਨ
148
+
149
+ ਇੱਕ ਲੇਖਕ ਮੇਜ਼ ਕੋਲ ਬੈਠਾ ਹੈ, ਖਾਲੀ ਸਕ੍ਰੀਨ ਵੱਲ ਦੇਖ ਰਿਹਾ ਹੈ।
150
+
151
+ @ਲੇਖਕ
152
+ (ਆਪਣੇ ਆਪ ਨੂੰ)
153
+ ਮੈਂ ਕਿੱਥੋਂ ਸ਼ੁਰੂ ਕਰਾਂ?
154
+
155
+ `,
156
+
157
+ or: `---
158
+ title: ଅନାମ ଚଳଚ୍ଚିତ୍ର ଲିପି
159
+ lang: or
160
+ ---
161
+
162
+ ## ଭିତର. କୋଠରୀ - ଦିନ
163
+
164
+ ଜଣେ ଲେଖକ ଟେବୁଲ ପାଖରେ ବସି, ଖାଲି ସ୍କ୍ରୀନ ଆଡ଼କୁ ଚାହିଁ ରହିଛନ୍ତି।
165
+
166
+ @ଲେଖକ
167
+ (ନିଜ ମନରେ)
168
+ ମୁଁ କୁଆଡ଼ୁ ଆରମ୍ଭ କରିବି?
169
+
170
+ `,
171
+
172
+ si: `---
173
+ title: නම් නොකළ තිර රචනය
174
+ lang: si
175
+ ---
176
+
177
+ ## ඇතුළත. කාමරය - දවල්
178
+
179
+ ලේඛකයෙක් මේසයක් ළඟ වාඩි වී, හිස් තිරයක් දෙස බලා සිටියි.
180
+
181
+ @ලේඛකයා
182
+ (තමන්ටම)
183
+ මම කොහෙන්ද පටන් ගන්නේ?
184
+
185
+ `,
186
+ }
187
+
188
+ /**
189
+ * Get the starter template for a given language code.
190
+ * Falls back to English if no template for the requested language.
191
+ */
192
+ export function getTemplate(lang: string): string {
193
+ return TEMPLATES[lang] ?? TEMPLATES['en'] ?? ''
194
+ }