@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,15 +0,0 @@
1
- export interface VoiceServiceCallbacks {
2
- onResult: (text: string, isFinal: boolean) => void;
3
- onError: (error: string) => void;
4
- onEnd: () => void;
5
- }
6
- export declare class VoiceService {
7
- private recognition;
8
- private isListening;
9
- constructor();
10
- isSupported(): boolean;
11
- start(lang: string, callbacks: VoiceServiceCallbacks): void;
12
- stop(): void;
13
- abort(): void;
14
- }
15
- export declare const voiceServiceInstance: VoiceService;
@@ -1,81 +0,0 @@
1
- export class VoiceService {
2
- recognition = null;
3
- isListening = false;
4
- constructor() {
5
- const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
6
- if (SpeechRecognition) {
7
- this.recognition = new SpeechRecognition();
8
- this.recognition.continuous = true;
9
- this.recognition.interimResults = true;
10
- }
11
- }
12
- isSupported() {
13
- return !!this.recognition;
14
- }
15
- start(lang, callbacks) {
16
- if (!this.recognition) {
17
- callbacks.onError('Web Speech API is not supported in this browser.');
18
- return;
19
- }
20
- if (this.isListening) {
21
- return;
22
- }
23
- this.recognition.lang = lang;
24
- this.isListening = true;
25
- this.recognition.onresult = (event) => {
26
- let interimTranscript = '';
27
- let finalTranscript = '';
28
- for (let i = event.resultIndex; i < event.results.length; ++i) {
29
- if (event.results[i].isFinal) {
30
- finalTranscript += event.results[i][0].transcript;
31
- }
32
- else {
33
- interimTranscript += event.results[i][0].transcript;
34
- }
35
- }
36
- const text = finalTranscript || interimTranscript;
37
- callbacks.onResult(text, finalTranscript.length > 0);
38
- };
39
- this.recognition.onerror = (event) => {
40
- // Ignore 'no-speech' error to prevent crashing, just log it
41
- if (event.error === 'no-speech') {
42
- console.warn('Speech recognition: no speech detected');
43
- return;
44
- }
45
- callbacks.onError(event.error || 'Speech recognition error');
46
- };
47
- this.recognition.onend = () => {
48
- this.isListening = false;
49
- callbacks.onEnd();
50
- };
51
- try {
52
- this.recognition.start();
53
- }
54
- catch (e) {
55
- callbacks.onError(e.message || String(e));
56
- }
57
- }
58
- stop() {
59
- if (this.recognition && this.isListening) {
60
- try {
61
- this.recognition.stop();
62
- }
63
- catch (e) {
64
- console.error('Failed to stop recognition:', e);
65
- }
66
- this.isListening = false;
67
- }
68
- }
69
- abort() {
70
- if (this.recognition && this.isListening) {
71
- try {
72
- this.recognition.abort();
73
- }
74
- catch (e) {
75
- console.error('Failed to abort recognition:', e);
76
- }
77
- this.isListening = false;
78
- }
79
- }
80
- }
81
- export const voiceServiceInstance = new VoiceService();
@@ -1,35 +0,0 @@
1
- import type { Node as PmNode } from 'prosemirror-model';
2
- import type { EditorView, NodeView, ViewMutationRecord } from 'prosemirror-view';
3
- /**
4
- * NodeView for character. Renders the name text (ProseMirror-managed) plus
5
- * an editable extension input — "(V.O.)", "(O.S.)", etc. — on the right.
6
- *
7
- * Without this, character used the schema's plain static toDOM spec
8
- * (['div', {class:'cs-character'}, 0]), whose content hole only renders
9
- * PM's managed inline content (the name). extension is a real, populated
10
- * node attr (captured correctly by the parser and carried correctly by
11
- * sutra-to-prosemirror.ts/prosemirror-to-sutra.ts both ways) but nothing
12
- * ever painted it to the screen — it was silently invisible. Mirrors
13
- * SceneHeadingView's pattern for the same kind of "attr needs its own
14
- * visible, editable-but-outside-the-PM-content-model widget" problem.
15
- *
16
- * The extension value is stored (and round-tripped to Sutra text) *with*
17
- * its parentheses, e.g. "(V.O.)" — matching what the parser's CHARACTER
18
- * regex captures — so a typed value without parens is auto-wrapped on commit.
19
- */
20
- export declare class CharacterView implements NodeView {
21
- dom: HTMLElement;
22
- contentDOM: HTMLElement;
23
- private input;
24
- private pmView;
25
- private getPos;
26
- constructor(node: PmNode, view: EditorView, getPos: () => number | undefined);
27
- private commit;
28
- private focusEditorAfter;
29
- private onInputKeydown;
30
- private onInputBlur;
31
- update(node: PmNode): boolean;
32
- destroy(): void;
33
- stopEvent(event: Event): boolean;
34
- ignoreMutation(mutation: ViewMutationRecord): boolean;
35
- }
@@ -1,112 +0,0 @@
1
- import { TextSelection } from 'prosemirror-state';
2
- /**
3
- * NodeView for character. Renders the name text (ProseMirror-managed) plus
4
- * an editable extension input — "(V.O.)", "(O.S.)", etc. — on the right.
5
- *
6
- * Without this, character used the schema's plain static toDOM spec
7
- * (['div', {class:'cs-character'}, 0]), whose content hole only renders
8
- * PM's managed inline content (the name). extension is a real, populated
9
- * node attr (captured correctly by the parser and carried correctly by
10
- * sutra-to-prosemirror.ts/prosemirror-to-sutra.ts both ways) but nothing
11
- * ever painted it to the screen — it was silently invisible. Mirrors
12
- * SceneHeadingView's pattern for the same kind of "attr needs its own
13
- * visible, editable-but-outside-the-PM-content-model widget" problem.
14
- *
15
- * The extension value is stored (and round-tripped to Sutra text) *with*
16
- * its parentheses, e.g. "(V.O.)" — matching what the parser's CHARACTER
17
- * regex captures — so a typed value without parens is auto-wrapped on commit.
18
- */
19
- export class CharacterView {
20
- dom;
21
- contentDOM;
22
- input;
23
- pmView;
24
- getPos;
25
- constructor(node, view, getPos) {
26
- this.pmView = view;
27
- this.getPos = getPos;
28
- this.dom = document.createElement('div');
29
- this.dom.className = 'cs-character';
30
- this.contentDOM = document.createElement('span');
31
- this.contentDOM.className = 'cs-character-text';
32
- this.dom.appendChild(this.contentDOM);
33
- this.input = document.createElement('input');
34
- this.input.type = 'text';
35
- this.input.className = 'cs-character-extension';
36
- this.input.placeholder = '(V.O.)';
37
- this.input.value = node.attrs['extension'] ?? '';
38
- this.input.setAttribute('aria-label', 'Character extension, e.g. (V.O.) or (O.S.)');
39
- this.input.spellcheck = false;
40
- this.input.addEventListener('keydown', this.onInputKeydown);
41
- this.input.addEventListener('blur', this.onInputBlur);
42
- this.input.addEventListener('mousedown', (e) => e.stopPropagation());
43
- this.dom.appendChild(this.input);
44
- }
45
- commit() {
46
- const raw = this.input.value.trim();
47
- let extension = raw === '' ? null : raw;
48
- if (extension && !(extension.startsWith('(') && extension.endsWith(')'))) {
49
- extension = `(${extension})`;
50
- }
51
- this.input.value = extension ?? '';
52
- const pos = this.getPos();
53
- if (pos === undefined)
54
- return;
55
- const node = this.pmView.state.doc.nodeAt(pos);
56
- if (!node)
57
- return;
58
- if (node.attrs['extension'] === extension)
59
- return;
60
- const tr = this.pmView.state.tr.setNodeMarkup(pos, undefined, { ...node.attrs, extension });
61
- this.pmView.dispatch(tr);
62
- }
63
- focusEditorAfter() {
64
- const pos = this.getPos();
65
- if (pos === undefined)
66
- return;
67
- const node = this.pmView.state.doc.nodeAt(pos);
68
- if (!node)
69
- return;
70
- const endOfText = pos + node.nodeSize - 1;
71
- const sel = TextSelection.near(this.pmView.state.doc.resolve(endOfText), -1);
72
- this.pmView.dispatch(this.pmView.state.tr.setSelection(sel));
73
- this.pmView.focus();
74
- }
75
- onInputKeydown = (e) => {
76
- if (e.key === 'Escape') {
77
- e.preventDefault();
78
- e.stopPropagation();
79
- this.input.value = this.pmView.state.doc.nodeAt(this.getPos() ?? 0)?.attrs['extension'] ?? '';
80
- this.pmView.focus();
81
- return;
82
- }
83
- if (e.key === 'Enter' || e.key === 'Tab') {
84
- e.preventDefault();
85
- e.stopPropagation();
86
- this.commit();
87
- this.focusEditorAfter();
88
- return;
89
- }
90
- };
91
- onInputBlur = () => {
92
- this.commit();
93
- };
94
- update(node) {
95
- if (node.type.name !== 'character')
96
- return false;
97
- if (document.activeElement !== this.input) {
98
- this.input.value = node.attrs['extension'] ?? '';
99
- }
100
- return true;
101
- }
102
- destroy() {
103
- this.input.removeEventListener('keydown', this.onInputKeydown);
104
- this.input.removeEventListener('blur', this.onInputBlur);
105
- }
106
- stopEvent(event) {
107
- return event.target === this.input || this.input.contains(event.target);
108
- }
109
- ignoreMutation(mutation) {
110
- return this.input.contains(mutation.target) || mutation.target === this.input;
111
- }
112
- }
@@ -1,10 +0,0 @@
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>;
@@ -1,88 +0,0 @@
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
- }
@@ -1,73 +0,0 @@
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
- }
@@ -1,12 +0,0 @@
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 {};
@@ -1,16 +0,0 @@
1
- import { mkdirSync, copyFileSync } from 'node:fs';
2
- import { dirname, join } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
-
5
- const root = join(dirname(fileURLToPath(import.meta.url)), '..');
6
-
7
- const files = [
8
- ['src/shell/fonts/fonts.css', 'dist/shell/fonts/fonts.css'],
9
- ['src/styles/screenplay.css', 'dist/styles/screenplay.css'],
10
- ['src/desktop-api.d.ts', 'dist/desktop-api.d.ts'],
11
- ];
12
-
13
- for (const [from, to] of files) {
14
- mkdirSync(dirname(join(root, to)), { recursive: true });
15
- copyFileSync(join(root, from), join(root, to));
16
- }
@@ -1,127 +0,0 @@
1
- // Generates src/ai/generated-providers.json (and a copy at
2
- // packages/app/public/models-config.json) from the community-maintained
3
- // all-llm-provider-list registry, at build time.
4
- //
5
- // Only providers Sutrata can actually drive are kept: ones with a real
6
- // env_variable (i.e. a "paste an API key" auth model — this drops
7
- // OAuth/browser-cookie/local-only/no-auth entries) and either
8
- // openai_compatible: true, or Anthropic (the one vendor ai-client.ts still
9
- // calls via a bespoke request shape, since Anthropic has no OpenAI-compat
10
- // endpoint). Google/Gemini is retargeted at its official OpenAI-compat base
11
- // URL (https://generativelanguage.googleapis.com/v1beta/openai) so it flows
12
- // through the generic branch too, rather than needing its own adapter.
13
- //
14
- // TODO(future): this list is currently refreshed only when this script is
15
- // re-run at build time. A follow-up could instead fetch the upstream list
16
- // live each time the user opens the AI settings/onboarding dialog, so the
17
- // provider catalog stays current without a rebuild — intentionally not done
18
- // here to keep this change scoped.
19
-
20
- import { writeFileSync, existsSync } from 'node:fs';
21
- import { dirname, join } from 'node:path';
22
- import { fileURLToPath } from 'node:url';
23
-
24
- const root = join(dirname(fileURLToPath(import.meta.url)), '..');
25
- const editorOutPath = join(root, 'src/ai/generated-providers.json');
26
- const appOutPath = join(root, '../app/public/models-config.json');
27
-
28
- const SOURCE_URL = 'https://raw.githubusercontent.com/foisalislambd/all-llm-provider-list/main/data/providers.json';
29
-
30
- // id/baseUrl overrides for vendors Sutrata treats specially. Ids double as
31
- // localStorage key suffixes (sutrata_api_key_<id>) and, for anthropic, as
32
- // invokeProvider's branch discriminator — they must stay stable across
33
- // regenerations regardless of what the upstream slug says.
34
- //
35
- // `models` overrides are needed because the upstream list's popular_models
36
- // sometimes holds human-readable display names (e.g. "Claude Opus 4.8")
37
- // rather than API-ready model ids — fine for a dropdown label, but wrong if
38
- // sent verbatim as the `model` field. These two are the ids actually used
39
- // for testConnection() before listModels() has a chance to fetch live ones;
40
- // everyone else relies on listModels() to correct a bad static id at
41
- // first use.
42
- const OVERRIDES = {
43
- anthropic: {
44
- id: 'anthropic',
45
- baseUrl: 'https://api.anthropic.com/v1',
46
- models: [
47
- { id: 'claude-opus-5', name: 'Claude Opus 5' },
48
- { id: 'claude-sonnet-5', name: 'Claude Sonnet 5' },
49
- { id: 'claude-haiku-4-5', name: 'Claude Haiku 4.5' },
50
- ],
51
- },
52
- 'google-ai-studio': {
53
- id: 'gemini',
54
- baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
55
- models: [
56
- { id: 'gemini-2.5-flash', name: 'Gemini 2.5 Flash' },
57
- { id: 'gemini-2.5-pro', name: 'Gemini 2.5 Pro' },
58
- { id: 'gemini-2.0-flash-lite', name: 'Gemini 2.0 Flash Lite' },
59
- ],
60
- },
61
- };
62
- OVERRIDES.gemini = OVERRIDES['google-ai-studio'];
63
- OVERRIDES.google = OVERRIDES['google-ai-studio'];
64
-
65
- const ESSENTIAL_IDS = new Set(['anthropic', 'gemini', 'groq']);
66
-
67
- function isSupported(entry) {
68
- if (!entry.env_variable || !entry.api_base_url) return false;
69
- const override = OVERRIDES[entry.slug];
70
- return entry.openai_compatible === true || override?.id === 'anthropic';
71
- }
72
-
73
- function toProviderConfig(entry) {
74
- const override = OVERRIDES[entry.slug];
75
- const id = override?.id ?? entry.slug;
76
- const baseUrl = override?.baseUrl ?? entry.api_base_url;
77
- const config = {
78
- id,
79
- name: entry.name,
80
- baseUrl,
81
- models: override?.models ?? (entry.popular_models ?? []).map((m) => ({ id: m, name: m })),
82
- };
83
- if (ESSENTIAL_IDS.has(id)) config.essential = true;
84
- return config;
85
- }
86
-
87
- async function main() {
88
- let source;
89
- try {
90
- const res = await fetch(SOURCE_URL);
91
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
92
- source = await res.json();
93
- } catch (e) {
94
- console.warn(`[generate-providers] Failed to fetch ${SOURCE_URL}: ${e}. Leaving existing generated files untouched.`);
95
- if (!existsSync(editorOutPath)) {
96
- console.error('[generate-providers] No existing generated-providers.json to fall back to — build will use the compiled-in defaults only.');
97
- }
98
- return;
99
- }
100
-
101
- const entries = Array.isArray(source) ? source : source.providers ?? [];
102
- const seen = new Set();
103
- const providers = [];
104
- for (const entry of entries) {
105
- if (!isSupported(entry)) continue;
106
- const config = toProviderConfig(entry);
107
- if (seen.has(config.id)) continue; // first match wins (upstream list order)
108
- seen.add(config.id);
109
- providers.push(config);
110
- }
111
-
112
- const output = {
113
- generatedAt: new Date().toISOString(),
114
- source: 'https://github.com/foisalislambd/all-llm-provider-list',
115
- providers,
116
- };
117
-
118
- const json = JSON.stringify(output, null, 2) + '\n';
119
- writeFileSync(editorOutPath, json);
120
-
121
- const appConfig = { providers: output.providers };
122
- writeFileSync(appOutPath, JSON.stringify(appConfig, null, 2) + '\n');
123
-
124
- console.log(`[generate-providers] Wrote ${providers.length} providers to ${editorOutPath} and ${appOutPath}.`);
125
- }
126
-
127
- await main();