@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.
- package/README.md +87 -0
- package/dist/ai/VoiceConfirmDialog.d.ts +1 -0
- package/dist/ai/VoiceConfirmDialog.js +6 -6
- package/dist/ai/VoiceToolbar.d.ts +1 -0
- package/dist/ai/VoiceToolbar.js +22 -16
- package/dist/ai/clean-response.d.ts +7 -0
- package/dist/ai/clean-response.js +58 -0
- package/dist/ai/format-transcript.d.ts +3 -0
- package/dist/ai/format-transcript.js +7 -0
- package/dist/ai/prompts.d.ts +8 -1
- package/dist/ai/prompts.js +34 -7
- package/dist/autocomplete/suggestions.js +1 -1
- package/dist/context/DocumentContext.d.ts +28 -4
- package/dist/context/DocumentContext.js +67 -37
- package/dist/editor/EditorView.js +32 -4
- package/dist/editor/SelectionMenu.js +5 -3
- package/dist/editor/external-decorations-plugin.d.ts +23 -0
- package/dist/editor/external-decorations-plugin.js +151 -0
- package/dist/editor/omitted-scene-plugin.d.ts +9 -0
- package/dist/editor/omitted-scene-plugin.js +44 -0
- package/dist/editor/plugins.js +4 -0
- package/dist/editor/private-metadata-plugin.d.ts +10 -0
- package/dist/editor/private-metadata-plugin.js +60 -0
- package/dist/editor/prosemirror-to-sutra.js +179 -74
- package/dist/editor/scene-heading-view.js +2 -0
- package/dist/editor/schema.js +69 -12
- package/dist/editor/sutra-to-prosemirror.d.ts +2 -0
- package/dist/editor/sutra-to-prosemirror.js +32 -34
- package/dist/extensions/ai-provider.d.ts +44 -0
- package/dist/extensions/ai-provider.js +27 -0
- package/dist/extensions/collab.d.ts +21 -0
- package/dist/extensions/collab.js +6 -0
- package/dist/extensions/command-registry.d.ts +49 -0
- package/dist/extensions/command-registry.js +28 -0
- package/dist/extensions/decorations.d.ts +48 -0
- package/dist/extensions/decorations.js +6 -0
- package/dist/extensions/export-registry.d.ts +59 -0
- package/dist/extensions/export-registry.js +10 -0
- package/dist/extensions/panel-registry.d.ts +30 -0
- package/dist/extensions/panel-registry.js +16 -0
- package/dist/extensions/registries.d.ts +15 -0
- package/dist/extensions/registries.js +12 -0
- package/dist/extensions/registry-store.d.ts +26 -0
- package/dist/extensions/registry-store.js +35 -0
- package/dist/extensions/session.d.ts +29 -0
- package/dist/extensions/session.js +20 -0
- package/dist/extensions/speech-provider.d.ts +35 -0
- package/dist/extensions/speech-provider.js +17 -0
- package/dist/file/builtin-exporters.d.ts +9 -0
- package/dist/file/builtin-exporters.js +72 -0
- package/dist/file/docx-exporter.js +7 -4
- package/dist/file/docx-importer.d.ts +9 -0
- package/dist/file/docx-importer.js +47 -19
- package/dist/file/scene-number.d.ts +5 -0
- package/dist/file/scene-number.js +8 -0
- package/dist/file/workflow-reports.js +13 -7
- package/dist/findreplace/FindReplace.js +5 -2
- package/dist/import/AIImportDialog.d.ts +11 -0
- package/dist/import/AIImportDialog.js +112 -0
- package/dist/import/ai-import.d.ts +51 -0
- package/dist/import/ai-import.js +200 -0
- package/dist/import/apply-import.d.ts +7 -0
- package/dist/import/apply-import.js +35 -0
- package/dist/import/extract.d.ts +17 -0
- package/dist/import/extract.js +18 -0
- package/dist/index.d.ts +17 -2
- package/dist/index.js +7 -0
- package/dist/locales/bn.json +8 -0
- package/dist/locales/en.json +8 -0
- package/dist/locales/gu.json +8 -0
- package/dist/locales/hi.json +8 -0
- package/dist/locales/kn.json +8 -0
- package/dist/locales/ml.json +8 -0
- package/dist/locales/mr.json +8 -0
- package/dist/locales/or.json +8 -0
- package/dist/locales/pa.json +8 -0
- package/dist/locales/si.json +8 -0
- package/dist/locales/ta.json +8 -0
- package/dist/locales/te.json +8 -0
- package/dist/navigator/SceneNavigator.js +125 -131
- package/dist/navigator/StatisticsDialog.js +5 -1
- package/dist/navigator/scene-edits.d.ts +18 -0
- package/dist/navigator/scene-edits.js +143 -0
- package/dist/navigator/scene-list.js +4 -4
- package/dist/navigator/scene-numbering.d.ts +37 -0
- package/dist/navigator/scene-numbering.js +88 -0
- package/dist/settings/SettingsDialog.js +8 -120
- package/dist/shell/AppBar.js +21 -2
- package/dist/shell/AppShell.js +18 -4
- package/dist/shell/ElementToolbar.d.ts +5 -0
- package/dist/shell/ElementToolbar.js +35 -25
- package/dist/shell/LanguageStatus.js +6 -0
- package/dist/shell/StatusBar.js +11 -6
- package/dist/shell/builtin-commands.d.ts +10 -0
- package/dist/shell/builtin-commands.js +43 -0
- package/dist/shell/icons.d.ts +6 -0
- package/dist/shell/icons.js +6 -0
- package/dist/shell/use-commands.d.ts +7 -0
- package/dist/shell/use-commands.js +24 -0
- package/dist/shell/use-panel-context.d.ts +3 -0
- package/dist/shell/use-panel-context.js +39 -0
- package/dist/source/SourceView.js +18 -1
- package/dist/styles/screenplay.css +113 -1
- package/dist/titlepage/ExportDialog.d.ts +5 -0
- package/dist/titlepage/ExportDialog.js +59 -96
- package/package.json +32 -5
- package/dist/ai/OnboardingDialog.d.ts +0 -6
- package/dist/ai/OnboardingDialog.js +0 -115
- package/dist/ai/ai-client.d.ts +0 -71
- package/dist/ai/ai-client.js +0 -494
- package/dist/ai/generated-providers.json +0 -3696
- package/dist/ai/voice-service.d.ts +0 -15
- package/dist/ai/voice-service.js +0 -81
- package/dist/editor/character-view.d.ts +0 -35
- package/dist/editor/character-view.js +0 -112
- package/dist/editor/transliterate-input.d.ts +0 -10
- package/dist/editor/transliterate-input.js +0 -88
- package/dist/extensions/types.d.ts +0 -73
- package/dist/extensions/types.js +0 -12
- package/scripts/copy-assets.mjs +0 -16
- package/scripts/generate-providers.mjs +0 -127
- package/src/ai/OnboardingDialog.tsx +0 -390
- package/src/ai/VoiceConfirmDialog.tsx +0 -137
- package/src/ai/VoiceToolbar.tsx +0 -321
- package/src/ai/ai-client.ts +0 -596
- package/src/ai/commands.ts +0 -164
- package/src/ai/generated-providers.json +0 -3696
- package/src/ai/prompts.ts +0 -62
- package/src/ai/voice-service.ts +0 -96
- package/src/autocomplete/AutocompletePlugin.ts +0 -21
- package/src/autocomplete/suggestions.ts +0 -147
- package/src/context/DocumentContext.tsx +0 -648
- package/src/desktop-api.d.ts +0 -40
- package/src/editor/EditorView.tsx +0 -82
- package/src/editor/SelectionMenu.tsx +0 -152
- package/src/editor/attr-pencil-plugin.ts +0 -172
- package/src/editor/character-decoration-plugin.ts +0 -104
- package/src/editor/editor-bus.ts +0 -51
- package/src/editor/find-highlight-plugin.ts +0 -68
- package/src/editor/frontmatter-field-view.ts +0 -52
- package/src/editor/frontmatter-field.ts +0 -115
- package/src/editor/grapheme-cursor.ts +0 -99
- package/src/editor/ime-plugin.ts +0 -85
- package/src/editor/insert-helper.ts +0 -43
- package/src/editor/keymap.ts +0 -124
- package/src/editor/language-class-plugin.ts +0 -91
- package/src/editor/plugins.ts +0 -26
- package/src/editor/prosemirror-to-sutra.ts +0 -192
- package/src/editor/scene-heading-view.ts +0 -147
- package/src/editor/schema.ts +0 -156
- package/src/editor/sutra-to-prosemirror.ts +0 -156
- package/src/extensions/storage-adapter.ts +0 -65
- package/src/extensions/types.ts +0 -60
- package/src/file/docx-exporter.ts +0 -585
- package/src/file/docx-importer.ts +0 -313
- package/src/file/print-paginator.ts +0 -301
- package/src/file/templates.ts +0 -73
- package/src/file/workflow-reports.ts +0 -820
- package/src/findreplace/FindReplace.tsx +0 -242
- package/src/findreplace/find-engine.ts +0 -97
- package/src/findreplace/scroll-to-match.ts +0 -89
- package/src/fonts/tofu-detect.ts +0 -53
- package/src/i18n/LanguageContext.tsx +0 -207
- package/src/i18n/language-detect.ts +0 -73
- package/src/i18n/useTranslation.tsx +0 -74
- package/src/index.ts +0 -27
- package/src/locales/bn.json +0 -143
- package/src/locales/en.json +0 -143
- package/src/locales/gu.json +0 -143
- package/src/locales/hi.json +0 -143
- package/src/locales/kn.json +0 -143
- package/src/locales/ml.json +0 -143
- package/src/locales/mr.json +0 -143
- package/src/locales/or.json +0 -143
- package/src/locales/pa.json +0 -143
- package/src/locales/si.json +0 -143
- package/src/locales/ta.json +0 -143
- package/src/locales/te.json +0 -143
- package/src/navigator/SceneNavigator.tsx +0 -844
- package/src/navigator/StatisticsDialog.tsx +0 -249
- package/src/navigator/scene-list.ts +0 -84
- package/src/settings/SettingsDialog.tsx +0 -303
- package/src/shell/AppBar.tsx +0 -354
- package/src/shell/AppShell.tsx +0 -71
- package/src/shell/ConfirmDialog.tsx +0 -71
- package/src/shell/ElementToolbar.tsx +0 -175
- package/src/shell/LanguageStatus.tsx +0 -101
- package/src/shell/StatusBar.tsx +0 -135
- package/src/shell/SutrataLogo.tsx +0 -89
- package/src/shell/Toast.tsx +0 -46
- package/src/shell/ToolbarButton.tsx +0 -30
- package/src/shell/VersionHistory.tsx +0 -212
- package/src/shell/Watermark.tsx +0 -9
- package/src/shell/fonts/fonts.css +0 -1133
- package/src/shell/icons.tsx +0 -335
- package/src/shell/toolbar-actions.ts +0 -59
- package/src/source/SourceView.tsx +0 -111
- package/src/source/find-highlight-source.ts +0 -57
- package/src/source/sutra-lang.ts +0 -121
- package/src/spellcheck/dictionaries.ts +0 -21
- package/src/spellcheck/hunspell.ts +0 -110
- package/src/spellcheck/spellcheck-plugin.ts +0 -83
- package/src/styles/StyleDialog.tsx +0 -148
- package/src/styles/builtin-styles.ts +0 -139
- package/src/styles/css-adapter.ts +0 -76
- package/src/styles/docx-adapter.ts +0 -109
- package/src/styles/embed-fonts.ts +0 -81
- package/src/styles/pagination.ts +0 -27
- package/src/styles/print-adapter.ts +0 -288
- package/src/styles/registry.ts +0 -20
- package/src/styles/screenplay.css +0 -3006
- package/src/styles/script-ranges.ts +0 -17
- package/src/styles/types.ts +0 -104
- package/src/styles/validate.ts +0 -114
- package/src/templates/templates.ts +0 -194
- package/src/titlepage/ExportDialog.tsx +0 -494
- package/src/titlepage/TitlePageDialog.tsx +0 -312
- package/src/titlepage/frontmatter-form.ts +0 -280
- package/src/titlepage/languages.ts +0 -37
- package/src/types.ts +0 -7
- package/tests/AppBar.test.tsx +0 -35
- package/tests/AppShell.test.tsx +0 -22
- package/tests/ElementToolbar.test.tsx +0 -36
- package/tests/LanguageStatus.test.tsx +0 -129
- package/tests/SelectionMenu.test.tsx +0 -71
- package/tests/ToolbarButton.test.tsx +0 -24
- package/tests/ai-onboarding.test.ts +0 -102
- package/tests/ai-status.test.tsx +0 -72
- package/tests/ai-structured.test.ts +0 -94
- package/tests/autocomplete/AutocompletePlugin.test.ts +0 -20
- package/tests/autocomplete/suggestions.test.ts +0 -106
- package/tests/document-context.test.tsx +0 -565
- package/tests/docx-importer.test.ts +0 -160
- package/tests/editor/character-decoration-plugin.test.ts +0 -101
- package/tests/editor/character-extension.test.ts +0 -44
- package/tests/editor/grapheme-cursor.test.ts +0 -118
- package/tests/editor/ime-plugin.test.ts +0 -72
- package/tests/editor/inline-note-bridge.test.ts +0 -32
- package/tests/editor/language-class-plugin.test.ts +0 -265
- package/tests/editor/title-page.test.ts +0 -124
- package/tests/embed-fonts.test.ts +0 -17
- package/tests/exporters.test.ts +0 -365
- package/tests/findreplace/FindReplace.test.tsx +0 -391
- package/tests/findreplace/find-engine.test.ts +0 -86
- package/tests/findreplace/scroll-to-match.test.ts +0 -50
- package/tests/fonts/tofu-detect.test.ts +0 -122
- package/tests/i18n/LanguageContext.test.tsx +0 -146
- package/tests/i18n/language-detect.test.ts +0 -54
- package/tests/i18n/locale-parity.test.ts +0 -25
- package/tests/i18n/useTranslation.test.tsx +0 -45
- package/tests/keymap.test.ts +0 -18
- package/tests/navigator/SceneNavigator.test.tsx +0 -177
- package/tests/navigator/StatisticsDialog.test.tsx +0 -63
- package/tests/navigator/scene-list.test.ts +0 -71
- package/tests/print-paginator.test.ts +0 -82
- package/tests/shell/VersionHistory.test.tsx +0 -129
- package/tests/spellcheck/hunspell.test.ts +0 -82
- package/tests/spellcheck/spellcheck-plugin.test.ts +0 -56
- package/tests/styles.test.ts +0 -315
- package/tests/templates/templates.test.ts +0 -83
- package/tests/titlepage/TitlePageDialog.test.tsx +0 -90
- package/tests/titlepage/apply-title-page.test.ts +0 -61
- package/tests/titlepage/frontmatter-form.test.ts +0 -115
- package/tests/toolbar-actions.test.ts +0 -93
- package/tests/voice-commands.test.ts +0 -58
- package/tsconfig.json +0 -17
- package/vitest.config.ts +0 -11
- package/vitest.setup.ts +0 -65
|
@@ -1,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;
|
package/dist/ai/voice-service.js
DELETED
|
@@ -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
|
-
}
|
package/dist/extensions/types.js
DELETED
|
@@ -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 {};
|
package/scripts/copy-assets.mjs
DELETED
|
@@ -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();
|