@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,154 @@
1
+ import { schema } from './schema';
2
+ function spansToNodes(spans) {
3
+ return spans.flatMap(span => {
4
+ if (span.type === 'text')
5
+ return span.text ? [schema.text(span.text)] : [];
6
+ if (span.type === 'note') {
7
+ if (!span.text)
8
+ return [];
9
+ const markType = schema.marks['note_mark'];
10
+ return [schema.text(span.text).mark([markType.create()])];
11
+ }
12
+ const inner = spansToNodes(span.spans);
13
+ const markType = schema.marks[span.type];
14
+ if (!markType || inner.length === 0)
15
+ return inner;
16
+ return inner.map(n => n.mark([markType.create()]));
17
+ });
18
+ }
19
+ function textNodes(text) {
20
+ const lines = text.split('\n');
21
+ const nodes = [];
22
+ for (let i = 0; i < lines.length; i++) {
23
+ const line = lines[i];
24
+ if (line)
25
+ nodes.push(schema.text(line));
26
+ if (i < lines.length - 1)
27
+ nodes.push(schema.nodes['hard_break'].create());
28
+ }
29
+ return nodes;
30
+ }
31
+ function sceneContentToNodes(nodes) {
32
+ const result = [];
33
+ for (const node of nodes) {
34
+ switch (node.type) {
35
+ case 'action':
36
+ result.push(schema.nodes['action'].create({}, spansToNodes(node.spans)));
37
+ break;
38
+ case 'character': {
39
+ // Name, extension and dual-marker are rendered as one continuous
40
+ // text run — see character-decoration-plugin.ts, which recognizes
41
+ // a trailing "(...)"/"^" live and styles it, rather than storing
42
+ // them as separate node attrs.
43
+ const cueText = [node.name, node.extension, node.isDual ? '^' : null]
44
+ .filter((part) => !!part)
45
+ .join(' ');
46
+ result.push(schema.nodes['character'].create({}, textNodes(cueText)));
47
+ for (const child of node.children) {
48
+ if (child.type === 'dialogue') {
49
+ result.push(schema.nodes['dialogue'].create({}, spansToNodes(child.spans)));
50
+ }
51
+ else {
52
+ result.push(schema.nodes['parenthetical'].create({}, textNodes(child.text)));
53
+ }
54
+ }
55
+ break;
56
+ }
57
+ case 'dual-dialogue':
58
+ result.push(...sceneContentToNodes([node.left]));
59
+ result.push(...sceneContentToNodes([node.right]));
60
+ break;
61
+ case 'transition':
62
+ result.push(schema.nodes['transition'].create({}, textNodes(node.text)));
63
+ break;
64
+ case 'centered':
65
+ result.push(schema.nodes['centered'].create({}, textNodes(node.text)));
66
+ break;
67
+ case 'lyrics':
68
+ result.push(schema.nodes['lyrics'].create({}, spansToNodes(node.spans)));
69
+ break;
70
+ case 'note':
71
+ result.push(schema.nodes['note'].create({}, textNodes(node.text)));
72
+ break;
73
+ case 'comment':
74
+ result.push(schema.nodes['comment'].create({}, textNodes(node.text)));
75
+ break;
76
+ case 'page-break':
77
+ result.push(schema.nodes['page_break'].create());
78
+ break;
79
+ }
80
+ }
81
+ return result;
82
+ }
83
+ /**
84
+ * Frontmatter → title_page node. One frontmatter_field per key; one-level
85
+ * nested maps (multilingual title:) flatten to dotted keys ("title.hi").
86
+ */
87
+ function frontmatterToNode(doc) {
88
+ const fm = doc.frontmatter;
89
+ if (!fm)
90
+ return null;
91
+ const fields = [];
92
+ for (const [key, value] of Object.entries(fm.data)) {
93
+ if (value !== null && typeof value === 'object') {
94
+ for (const [sub, subVal] of Object.entries(value)) {
95
+ fields.push(schema.nodes['frontmatter_field'].create({ fmKey: `${key}.${sub}` }, textNodes(String(subVal))));
96
+ }
97
+ }
98
+ else {
99
+ fields.push(schema.nodes['frontmatter_field'].create({ fmKey: key }, textNodes(String(value ?? ''))));
100
+ }
101
+ }
102
+ if (fields.length === 0)
103
+ return null;
104
+ return schema.nodes['title_page'].create({}, fields);
105
+ }
106
+ export function sutraToProsemirror(doc) {
107
+ const topNodes = [];
108
+ const titlePage = frontmatterToNode(doc);
109
+ if (titlePage)
110
+ topNodes.push(titlePage);
111
+ for (const node of doc.children) {
112
+ switch (node.type) {
113
+ case 'section':
114
+ topNodes.push(schema.nodes['section'].create({ level: node.level, id: node.id }, textNodes(node.text)));
115
+ break;
116
+ case 'scene-heading': {
117
+ const sceneNode = node;
118
+ topNodes.push(schema.nodes['scene_heading'].create({ id: sceneNode.id }, textNodes(sceneNode.text)));
119
+ for (const meta of sceneNode.metadata) {
120
+ topNodes.push(schema.nodes['scene_metadata'].create({ metaKey: meta.key }, textNodes(meta.value)));
121
+ }
122
+ topNodes.push(...sceneContentToNodes(sceneNode.children));
123
+ break;
124
+ }
125
+ case 'action':
126
+ topNodes.push(schema.nodes['action'].create({}, spansToNodes(node.spans)));
127
+ break;
128
+ case 'transition':
129
+ topNodes.push(schema.nodes['transition'].create({}, textNodes(node.text)));
130
+ break;
131
+ case 'centered':
132
+ topNodes.push(schema.nodes['centered'].create({}, textNodes(node.text)));
133
+ break;
134
+ case 'lyrics':
135
+ topNodes.push(schema.nodes['lyrics'].create({}, spansToNodes(node.spans)));
136
+ break;
137
+ case 'note':
138
+ topNodes.push(schema.nodes['note'].create({}, textNodes(node.text)));
139
+ break;
140
+ case 'comment':
141
+ topNodes.push(schema.nodes['comment'].create({}, textNodes(node.text)));
142
+ break;
143
+ case 'page-break':
144
+ topNodes.push(schema.nodes['page_break'].create());
145
+ break;
146
+ }
147
+ }
148
+ // doc content is 'title_page? block+' — always need at least one body block
149
+ const hasBlock = topNodes.some(n => n.type.name !== 'title_page');
150
+ if (!hasBlock) {
151
+ topNodes.push(schema.nodes['action'].create({}, []));
152
+ }
153
+ return schema.nodes['doc'].create({}, topNodes);
154
+ }
@@ -0,0 +1,10 @@
1
+ import { Plugin } from 'prosemirror-state';
2
+ /** Enable or disable transliteration mode. */
3
+ export declare function setTranslitMode(enabled: boolean, lang?: string): void;
4
+ /** Returns whether transliteration mode is active. */
5
+ export declare function isTranslitMode(): boolean;
6
+ /**
7
+ * ProseMirror plugin that intercepts text input and converts roman keystrokes
8
+ * to native script on word boundaries.
9
+ */
10
+ export declare function createTranslitPlugin(): Plugin<any>;
@@ -0,0 +1,88 @@
1
+ import { Plugin } from 'prosemirror-state';
2
+ import { transliterate } from '@sutra/parser';
3
+ // Map BCP-47 codes to Script names used by the transliterate engine
4
+ const LANG_TO_SCRIPT = {
5
+ 'hi': 'devanagari',
6
+ 'mr': 'devanagari',
7
+ 'sa': 'devanagari',
8
+ 'ta': 'tamil',
9
+ 'te': 'telugu',
10
+ 'kn': 'kannada',
11
+ 'ml': 'malayalam',
12
+ 'bn': 'bengali',
13
+ 'gu': 'gujarati',
14
+ 'pa': 'gurmukhi',
15
+ 'or': 'odia',
16
+ };
17
+ // Word boundary characters — trigger transliteration
18
+ const WORD_BOUNDARIES = new Set([' ', '\t', '\n', '.', ',', '?', '!', ':', ';', '-', '(', ')']);
19
+ const globalState = {
20
+ enabled: false,
21
+ buffer: '',
22
+ currentLang: 'hi',
23
+ };
24
+ /** Enable or disable transliteration mode. */
25
+ export function setTranslitMode(enabled, lang = 'hi') {
26
+ globalState.enabled = enabled;
27
+ globalState.currentLang = lang;
28
+ globalState.buffer = '';
29
+ }
30
+ /** Returns whether transliteration mode is active. */
31
+ export function isTranslitMode() {
32
+ return globalState.enabled;
33
+ }
34
+ /**
35
+ * ProseMirror plugin that intercepts text input and converts roman keystrokes
36
+ * to native script on word boundaries.
37
+ */
38
+ export function createTranslitPlugin() {
39
+ return new Plugin({
40
+ props: {
41
+ handleTextInput(view, _from, _to, text) {
42
+ if (!globalState.enabled)
43
+ return false;
44
+ const script = LANG_TO_SCRIPT[globalState.currentLang];
45
+ if (!script)
46
+ return false;
47
+ if (WORD_BOUNDARIES.has(text)) {
48
+ // Flush buffer: transliterate and replace
49
+ if (globalState.buffer.length > 0) {
50
+ const native = transliterate(globalState.buffer, script);
51
+ const { state, dispatch } = view;
52
+ const { from } = state.selection;
53
+ // Replace the buffered roman text with native script + boundary char
54
+ const bufferStart = from - globalState.buffer.length;
55
+ const tr = state.tr
56
+ .replaceWith(bufferStart, from, state.schema.text(native + text));
57
+ globalState.buffer = '';
58
+ dispatch(tr);
59
+ return true;
60
+ }
61
+ // Nothing buffered, let boundary char through
62
+ return false;
63
+ }
64
+ else {
65
+ // Accumulate roman keystrokes in buffer
66
+ globalState.buffer += text;
67
+ // Let ProseMirror insert the roman char temporarily (user sees buffering)
68
+ return false;
69
+ }
70
+ },
71
+ handleKeyDown(_view, event) {
72
+ if (!globalState.enabled)
73
+ return false;
74
+ // Backspace: remove from buffer too
75
+ if (event.key === 'Backspace' && globalState.buffer.length > 0) {
76
+ globalState.buffer = globalState.buffer.slice(0, -1);
77
+ return false; // let PM handle the actual deletion
78
+ }
79
+ // Escape: clear buffer without inserting
80
+ if (event.key === 'Escape') {
81
+ globalState.buffer = '';
82
+ return false;
83
+ }
84
+ return false;
85
+ },
86
+ },
87
+ });
88
+ }
@@ -0,0 +1,59 @@
1
+ import type { VersionEntry } from '../types';
2
+ import type { ScreenplayStyleDefinition } from '../styles/types';
3
+ /**
4
+ * Injected by the embedder (OSS spec §11.5). The OSS app supplies a local
5
+ * implementation (IndexedDB + File System Access API, see @sutrata/app's
6
+ * local-storage-adapter.ts); Sutrata Cloud supplies a networked one.
7
+ *
8
+ * `handle` fields are opaque (`unknown`) on purpose: a web FileSystemFileHandle
9
+ * has no equivalent in a non-web adapter. Callers just thread whatever the
10
+ * adapter returned back into a later saveFile() call without inspecting it.
11
+ */
12
+ export interface StorageAdapter {
13
+ saveDocument(path: string, content: string): Promise<void>;
14
+ loadDocument(path: string): Promise<string | null>;
15
+ listVersions(path: string): Promise<VersionEntry[]>;
16
+ openFile(): Promise<{
17
+ name: string;
18
+ content: string;
19
+ handle: unknown;
20
+ } | null>;
21
+ openDocx(): Promise<{
22
+ name: string;
23
+ buffer: ArrayBuffer;
24
+ } | null>;
25
+ openStyleJson(): Promise<{
26
+ name: string;
27
+ text: string;
28
+ } | null>;
29
+ saveFile(name: string, content: string, existingHandle?: unknown): Promise<{
30
+ savedName: string;
31
+ handle: unknown;
32
+ } | null>;
33
+ saveStyle(style: ScreenplayStyleDefinition): Promise<void>;
34
+ loadAllStyles(): Promise<ScreenplayStyleDefinition[]>;
35
+ deleteStyle(id: string): Promise<void>;
36
+ /**
37
+ * Optional: lets a restored session (see DocumentContext's boot-recovery
38
+ * effect) keep writing straight back to the file the user had open instead
39
+ * of forcing Save As. Adapters without a handle concept (Cloud, or a
40
+ * browser build with no persisted-handle support) simply omit these; the
41
+ * caller feature-detects with `?.`. `restoreFileHandle` should only ever
42
+ * return a handle that's usable without prompting — if permission would
43
+ * need to be re-requested, return null and let the user Save As instead.
44
+ */
45
+ persistFileHandle?(path: string, handle: unknown): Promise<void>;
46
+ restoreFileHandle?(path: string): Promise<unknown | null>;
47
+ /**
48
+ * Optional: delete all saved versions of a document. Used by the version
49
+ * history UI's "clear all versions" action.
50
+ */
51
+ deleteAllVersions?(path: string): Promise<void>;
52
+ /**
53
+ * Optional: save a version snapshot for a document. Adapters should only
54
+ * persist a new version entry when there is a difference against the previous version.
55
+ */
56
+ saveVersion?(path: string, content: string): Promise<boolean>;
57
+ }
58
+ export declare const StorageAdapterProvider: import("react").Provider<StorageAdapter | null>;
59
+ export declare function useStorageAdapter(): StorageAdapter;
@@ -0,0 +1,9 @@
1
+ import { createContext, useContext } from 'react';
2
+ const StorageAdapterContext = createContext(null);
3
+ export const StorageAdapterProvider = StorageAdapterContext.Provider;
4
+ export function useStorageAdapter() {
5
+ const adapter = useContext(StorageAdapterContext);
6
+ if (!adapter)
7
+ throw new Error('useStorageAdapter must be used inside a StorageAdapterProvider (DocumentProvider supplies one)');
8
+ return adapter;
9
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Type-only extension-point surface (OSS spec §11.5). These declare the shape
3
+ * embedders (Sutrata Cloud, third-party plugins) will implement, but nothing
4
+ * in @sutrata/editor is wired to them yet — ai-client.ts and voice-service.ts
5
+ * are still direct imports within this package, not injected. Promoting them
6
+ * to real dependency-injected providers (matching StorageAdapter's pattern in
7
+ * ./storage-adapter.ts) is future work; see docs/TODO.
8
+ *
9
+ * [P1 interface, stable by P2] per the OSS spec — these shapes may still
10
+ * change before they're load-bearing.
11
+ */
12
+ /** LLM completion with streaming; declares capabilities and data-policy text for disclosure. */
13
+ export interface AIProvider {
14
+ id: string;
15
+ displayName: string;
16
+ dataPolicyText: string;
17
+ complete(prompt: string, opts?: {
18
+ stream?: boolean;
19
+ }): Promise<string>;
20
+ }
21
+ /** Speech-to-text. */
22
+ export interface SpeechProvider {
23
+ id: string;
24
+ displayName: string;
25
+ transcribe(audio: Blob, opts?: {
26
+ language?: string;
27
+ }): Promise<string>;
28
+ }
29
+ /** Current user identity, permissions (read-only, comment, edit), feature flags. */
30
+ export interface SessionContext {
31
+ userId: string | null;
32
+ displayName: string | null;
33
+ permission: 'read' | 'comment' | 'edit';
34
+ featureFlags: Record<string, boolean>;
35
+ }
36
+ /** Add side panels, inspector sections, and app-bar items; receives the AST and scene IDs. */
37
+ export interface PanelRegistry {
38
+ registerPanel(panel: {
39
+ id: string;
40
+ title: string;
41
+ render: () => unknown;
42
+ }): void;
43
+ }
44
+ /** Add commands, menu items, keyboard shortcuts. */
45
+ export interface CommandRegistry {
46
+ registerCommand(command: {
47
+ id: string;
48
+ label: string;
49
+ run: () => void;
50
+ shortcut?: string;
51
+ }): void;
52
+ }
53
+ /** Add export formats and report types. */
54
+ export interface ExportRegistry {
55
+ registerExporter(exporter: {
56
+ id: string;
57
+ label: string;
58
+ run: (text: string) => Promise<Blob>;
59
+ }): void;
60
+ }
61
+ /** Hook to bind the editor state to an external real-time sync provider (e.g. a Yjs document). */
62
+ export interface CollabBinding {
63
+ bind(): () => void;
64
+ }
65
+ /** Render external annotations (comments, breakdown highlights, revision marks) anchored to
66
+ * scene IDs and text ranges. */
67
+ export interface DecorationProvider {
68
+ getDecorations(sceneId: string): Array<{
69
+ from: number;
70
+ to: number;
71
+ className: string;
72
+ }>;
73
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Type-only extension-point surface (OSS spec §11.5). These declare the shape
3
+ * embedders (Sutrata Cloud, third-party plugins) will implement, but nothing
4
+ * in @sutrata/editor is wired to them yet — ai-client.ts and voice-service.ts
5
+ * are still direct imports within this package, not injected. Promoting them
6
+ * to real dependency-injected providers (matching StorageAdapter's pattern in
7
+ * ./storage-adapter.ts) is future work; see docs/TODO.
8
+ *
9
+ * [P1 interface, stable by P2] per the OSS spec — these shapes may still
10
+ * change before they're load-bearing.
11
+ */
12
+ export {};
@@ -0,0 +1,8 @@
1
+ import type { DocumentNode } from '@sutrata/parser';
2
+ import type { ScreenplayStyleDefinition } from '../styles/types';
3
+ /** Main exporter function. `styleOverride` picks a style id for this export only
4
+ * (without touching the document); otherwise the document's frontmatter `style:`
5
+ * field is used, falling back to the default style. `customStyles` is the list of
6
+ * user-imported styles loaded from IndexedDB (see file/storage.ts). `skipNotes` omits
7
+ * both block-level and inline `[[ ]]` notes from the exported document. */
8
+ export declare function exportToDocx(ast: DocumentNode, styleOverride?: string, customStyles?: ScreenplayStyleDefinition[], skipNotes?: boolean): Promise<Blob>;