@sutrata/editor 0.1.0 → 0.1.2

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 (152) hide show
  1. package/README.md +87 -0
  2. package/dist/ai/generated-providers.json +1 -1
  3. package/package.json +36 -9
  4. package/scripts/copy-assets.mjs +0 -16
  5. package/scripts/generate-providers.mjs +0 -127
  6. package/src/ai/OnboardingDialog.tsx +0 -390
  7. package/src/ai/VoiceConfirmDialog.tsx +0 -137
  8. package/src/ai/VoiceToolbar.tsx +0 -321
  9. package/src/ai/ai-client.ts +0 -596
  10. package/src/ai/commands.ts +0 -164
  11. package/src/ai/generated-providers.json +0 -3696
  12. package/src/ai/prompts.ts +0 -62
  13. package/src/ai/voice-service.ts +0 -96
  14. package/src/autocomplete/AutocompletePlugin.ts +0 -21
  15. package/src/autocomplete/suggestions.ts +0 -147
  16. package/src/context/DocumentContext.tsx +0 -648
  17. package/src/desktop-api.d.ts +0 -40
  18. package/src/editor/EditorView.tsx +0 -82
  19. package/src/editor/SelectionMenu.tsx +0 -152
  20. package/src/editor/attr-pencil-plugin.ts +0 -172
  21. package/src/editor/character-decoration-plugin.ts +0 -104
  22. package/src/editor/editor-bus.ts +0 -51
  23. package/src/editor/find-highlight-plugin.ts +0 -68
  24. package/src/editor/frontmatter-field-view.ts +0 -52
  25. package/src/editor/frontmatter-field.ts +0 -115
  26. package/src/editor/grapheme-cursor.ts +0 -99
  27. package/src/editor/ime-plugin.ts +0 -85
  28. package/src/editor/insert-helper.ts +0 -43
  29. package/src/editor/keymap.ts +0 -124
  30. package/src/editor/language-class-plugin.ts +0 -91
  31. package/src/editor/plugins.ts +0 -26
  32. package/src/editor/prosemirror-to-sutra.ts +0 -192
  33. package/src/editor/scene-heading-view.ts +0 -147
  34. package/src/editor/schema.ts +0 -156
  35. package/src/editor/sutra-to-prosemirror.ts +0 -156
  36. package/src/extensions/storage-adapter.ts +0 -65
  37. package/src/extensions/types.ts +0 -60
  38. package/src/file/docx-exporter.ts +0 -585
  39. package/src/file/docx-importer.ts +0 -313
  40. package/src/file/print-paginator.ts +0 -301
  41. package/src/file/templates.ts +0 -73
  42. package/src/file/workflow-reports.ts +0 -820
  43. package/src/findreplace/FindReplace.tsx +0 -242
  44. package/src/findreplace/find-engine.ts +0 -97
  45. package/src/findreplace/scroll-to-match.ts +0 -89
  46. package/src/fonts/tofu-detect.ts +0 -53
  47. package/src/i18n/LanguageContext.tsx +0 -207
  48. package/src/i18n/language-detect.ts +0 -73
  49. package/src/i18n/useTranslation.tsx +0 -74
  50. package/src/index.ts +0 -27
  51. package/src/locales/bn.json +0 -143
  52. package/src/locales/en.json +0 -143
  53. package/src/locales/gu.json +0 -143
  54. package/src/locales/hi.json +0 -143
  55. package/src/locales/kn.json +0 -143
  56. package/src/locales/ml.json +0 -143
  57. package/src/locales/mr.json +0 -143
  58. package/src/locales/or.json +0 -143
  59. package/src/locales/pa.json +0 -143
  60. package/src/locales/si.json +0 -143
  61. package/src/locales/ta.json +0 -143
  62. package/src/locales/te.json +0 -143
  63. package/src/navigator/SceneNavigator.tsx +0 -844
  64. package/src/navigator/StatisticsDialog.tsx +0 -249
  65. package/src/navigator/scene-list.ts +0 -84
  66. package/src/settings/SettingsDialog.tsx +0 -303
  67. package/src/shell/AppBar.tsx +0 -354
  68. package/src/shell/AppShell.tsx +0 -71
  69. package/src/shell/ConfirmDialog.tsx +0 -71
  70. package/src/shell/ElementToolbar.tsx +0 -175
  71. package/src/shell/LanguageStatus.tsx +0 -101
  72. package/src/shell/StatusBar.tsx +0 -135
  73. package/src/shell/SutrataLogo.tsx +0 -89
  74. package/src/shell/Toast.tsx +0 -46
  75. package/src/shell/ToolbarButton.tsx +0 -30
  76. package/src/shell/VersionHistory.tsx +0 -212
  77. package/src/shell/Watermark.tsx +0 -9
  78. package/src/shell/fonts/fonts.css +0 -1133
  79. package/src/shell/icons.tsx +0 -335
  80. package/src/shell/toolbar-actions.ts +0 -59
  81. package/src/source/SourceView.tsx +0 -111
  82. package/src/source/find-highlight-source.ts +0 -57
  83. package/src/source/sutra-lang.ts +0 -121
  84. package/src/spellcheck/dictionaries.ts +0 -21
  85. package/src/spellcheck/hunspell.ts +0 -110
  86. package/src/spellcheck/spellcheck-plugin.ts +0 -83
  87. package/src/styles/StyleDialog.tsx +0 -148
  88. package/src/styles/builtin-styles.ts +0 -139
  89. package/src/styles/css-adapter.ts +0 -76
  90. package/src/styles/docx-adapter.ts +0 -109
  91. package/src/styles/embed-fonts.ts +0 -81
  92. package/src/styles/pagination.ts +0 -27
  93. package/src/styles/print-adapter.ts +0 -288
  94. package/src/styles/registry.ts +0 -20
  95. package/src/styles/screenplay.css +0 -3006
  96. package/src/styles/script-ranges.ts +0 -17
  97. package/src/styles/types.ts +0 -104
  98. package/src/styles/validate.ts +0 -114
  99. package/src/templates/templates.ts +0 -194
  100. package/src/titlepage/ExportDialog.tsx +0 -494
  101. package/src/titlepage/TitlePageDialog.tsx +0 -312
  102. package/src/titlepage/frontmatter-form.ts +0 -280
  103. package/src/titlepage/languages.ts +0 -37
  104. package/src/types.ts +0 -7
  105. package/tests/AppBar.test.tsx +0 -35
  106. package/tests/AppShell.test.tsx +0 -22
  107. package/tests/ElementToolbar.test.tsx +0 -36
  108. package/tests/LanguageStatus.test.tsx +0 -129
  109. package/tests/SelectionMenu.test.tsx +0 -71
  110. package/tests/ToolbarButton.test.tsx +0 -24
  111. package/tests/ai-onboarding.test.ts +0 -102
  112. package/tests/ai-status.test.tsx +0 -72
  113. package/tests/ai-structured.test.ts +0 -94
  114. package/tests/autocomplete/AutocompletePlugin.test.ts +0 -20
  115. package/tests/autocomplete/suggestions.test.ts +0 -106
  116. package/tests/document-context.test.tsx +0 -565
  117. package/tests/docx-importer.test.ts +0 -160
  118. package/tests/editor/character-decoration-plugin.test.ts +0 -101
  119. package/tests/editor/character-extension.test.ts +0 -44
  120. package/tests/editor/grapheme-cursor.test.ts +0 -118
  121. package/tests/editor/ime-plugin.test.ts +0 -72
  122. package/tests/editor/inline-note-bridge.test.ts +0 -32
  123. package/tests/editor/language-class-plugin.test.ts +0 -265
  124. package/tests/editor/title-page.test.ts +0 -124
  125. package/tests/embed-fonts.test.ts +0 -17
  126. package/tests/exporters.test.ts +0 -365
  127. package/tests/findreplace/FindReplace.test.tsx +0 -391
  128. package/tests/findreplace/find-engine.test.ts +0 -86
  129. package/tests/findreplace/scroll-to-match.test.ts +0 -50
  130. package/tests/fonts/tofu-detect.test.ts +0 -122
  131. package/tests/i18n/LanguageContext.test.tsx +0 -146
  132. package/tests/i18n/language-detect.test.ts +0 -54
  133. package/tests/i18n/locale-parity.test.ts +0 -25
  134. package/tests/i18n/useTranslation.test.tsx +0 -45
  135. package/tests/keymap.test.ts +0 -18
  136. package/tests/navigator/SceneNavigator.test.tsx +0 -177
  137. package/tests/navigator/StatisticsDialog.test.tsx +0 -63
  138. package/tests/navigator/scene-list.test.ts +0 -71
  139. package/tests/print-paginator.test.ts +0 -82
  140. package/tests/shell/VersionHistory.test.tsx +0 -129
  141. package/tests/spellcheck/hunspell.test.ts +0 -82
  142. package/tests/spellcheck/spellcheck-plugin.test.ts +0 -56
  143. package/tests/styles.test.ts +0 -315
  144. package/tests/templates/templates.test.ts +0 -83
  145. package/tests/titlepage/TitlePageDialog.test.tsx +0 -90
  146. package/tests/titlepage/apply-title-page.test.ts +0 -61
  147. package/tests/titlepage/frontmatter-form.test.ts +0 -115
  148. package/tests/toolbar-actions.test.ts +0 -93
  149. package/tests/voice-commands.test.ts +0 -58
  150. package/tsconfig.json +0 -17
  151. package/vitest.config.ts +0 -11
  152. package/vitest.setup.ts +0 -65
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # @sutrata/editor
2
+
3
+ The React screenplay editor behind [Sutrata](https://github.com/sutrata/sutrata), for writing scripts in **Sutra**, a plain-text screenplay format for world languages. It's built on ProseMirror and handles Indic and other complex scripts properly:
4
+ - Text is stored in logical order.
5
+ - The cursor and selection move by grapheme cluster (user-perceived character).
6
+ - Find/replace matches after NFC normalization, so differently encoded forms of the same text still match.
7
+
8
+ This package contains the complete editor:
9
+ - WYSIWYG and source modes, with smart Enter that cycles through screenplay elements.
10
+ - A scene navigator, find/replace, a title-page form and screenplay styles.
11
+ - Statistics, autocomplete and spellcheck.
12
+ - Export to PDF, DOCX, Fountain and workflow reports, including romanized exports.
13
+ - Localized UI.
14
+
15
+ Sutra parsing and serialization come from [`@sutrata/parser`](https://www.npmjs.com/package/@sutrata/parser).
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ npm install @sutrata/editor react react-dom
21
+ ```
22
+
23
+ `react` and `react-dom` (^18.3) are peer dependencies.
24
+
25
+ ## Usage
26
+
27
+ The editor doesn't read or write files itself. You pass it a `StorageAdapter`, and it saves, opens and keeps version history through that adapter. The same editor can therefore run on local files, in a desktop shell or against a server.
28
+
29
+ ```tsx
30
+ import React from 'react'
31
+ import { createRoot } from 'react-dom/client'
32
+ import {
33
+ TranslationProvider,
34
+ LanguageProvider,
35
+ DocumentProvider,
36
+ AppShell,
37
+ type StorageAdapter,
38
+ } from '@sutrata/editor'
39
+ import '@sutrata/editor/dist/shell/fonts/fonts.css'
40
+ import '@sutrata/editor/dist/styles/screenplay.css'
41
+
42
+ const storageAdapter: StorageAdapter = {
43
+ // documents and version history
44
+ saveDocument: async (path, content) => { /* ... */ },
45
+ loadDocument: async (path) => null,
46
+ listVersions: async (path) => [],
47
+ // file dialogs
48
+ openFile: async () => null, // { name, content, handle }
49
+ openDocx: async () => null, // { name, buffer }
50
+ openStyleJson: async () => null, // { name, text }
51
+ saveFile: async (name, content, handle) => null, // { savedName, handle }
52
+ // custom screenplay styles
53
+ saveStyle: async (style) => {},
54
+ loadAllStyles: async () => [],
55
+ deleteStyle: async (id) => {},
56
+ }
57
+
58
+ createRoot(document.getElementById('root')!).render(
59
+ <TranslationProvider>
60
+ <LanguageProvider>
61
+ <DocumentProvider storageAdapter={storageAdapter}>
62
+ <AppShell />
63
+ </DocumentProvider>
64
+ </LanguageProvider>
65
+ </TranslationProvider>,
66
+ )
67
+ ```
68
+
69
+ `StorageAdapter` also has optional methods: `saveVersion`, `deleteAllVersions`, `persistFileHandle` and `restoreFileHandle`. For a complete implementation built on IndexedDB and the File System Access API, see [`local-storage-adapter.ts`](https://github.com/sutrata/sutrata/blob/main/packages/app/src/local-storage-adapter.ts) in the Sutrata app.
70
+
71
+ Inside the providers, `useDocument()` gives you the current Sutra text, its parsed AST and the editor state.
72
+
73
+ ### Fonts
74
+
75
+ `fonts.css` declares Noto font faces for every supported script, loaded from `/fonts/*.woff2` on your own site. This package does not include the font files. Copy them from [`packages/app/public/fonts`](https://github.com/sutrata/sutrata/tree/main/packages/app/public/fonts) into your app's `/fonts/` path, or use your own `@font-face` rules. Without them, some scripts may display as missing-glyph boxes (tofu).
76
+
77
+ ## Extension points
78
+
79
+ Beyond `StorageAdapter`, the package exports type definitions for more extension points: `AIProvider`, `SpeechProvider`, `SessionContext`, `PanelRegistry`, `CommandRegistry`, `ExportRegistry`, `CollabBinding` and `DecorationProvider`. **These are type-only previews and are not wired into the editor yet.** Their shapes may change before 1.0.
80
+
81
+ ## Status
82
+
83
+ Pre-1.0. The API may change between minor versions.
84
+
85
+ ## License
86
+
87
+ [GPL-3.0-or-later](https://github.com/sutrata/sutrata/blob/main/packages/editor/LICENSE).
@@ -1,5 +1,5 @@
1
1
  {
2
- "generatedAt": "2026-09-24T06:55:45.925Z",
2
+ "generatedAt": "2026-09-24T07:18:07.361Z",
3
3
  "source": "https://github.com/foisalislambd/all-llm-provider-list",
4
4
  "providers": [
5
5
  {
package/package.json CHANGED
@@ -1,16 +1,33 @@
1
1
  {
2
2
  "name": "@sutrata/editor",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
+ "description": "React + ProseMirror screenplay editor for Sutra, with first-class support for Indic and other complex scripts.",
4
5
  "license": "GPL-3.0-or-later",
6
+ "keywords": [
7
+ "sutra",
8
+ "screenplay",
9
+ "editor",
10
+ "prosemirror",
11
+ "react",
12
+ "indic",
13
+ "unicode",
14
+ "fountain"
15
+ ],
16
+ "homepage": "https://github.com/sutrata/sutrata/tree/main/packages/editor#readme",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/sutrata/sutrata.git",
20
+ "directory": "packages/editor"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/sutrata/sutrata/issues"
24
+ },
5
25
  "type": "module",
6
26
  "main": "./dist/index.js",
7
27
  "types": "./dist/index.d.ts",
8
- "scripts": {
9
- "build": "node scripts/generate-providers.mjs && tsc && node scripts/copy-assets.mjs",
10
- "generate:providers": "node scripts/generate-providers.mjs",
11
- "test": "vitest run",
12
- "test:watch": "vitest"
13
- },
28
+ "files": [
29
+ "dist"
30
+ ],
14
31
  "dependencies": {
15
32
  "@codemirror/commands": "^6.10.3",
16
33
  "@codemirror/language": "^6.12.3",
@@ -18,7 +35,6 @@
18
35
  "@codemirror/theme-one-dark": "^6.1.3",
19
36
  "@codemirror/view": "^6.43.1",
20
37
  "@lezer/highlight": "^1.2.3",
21
- "@sutrata/parser": "workspace:*",
22
38
  "diff": "^9.0.0",
23
39
  "docx": "^9.7.1",
24
40
  "jszip": "^3.10.1",
@@ -30,6 +46,9 @@
30
46
  "prosemirror-state": "^1.4.3",
31
47
  "prosemirror-transform": "^1.9.0",
32
48
  "prosemirror-view": "^1.33.0",
49
+ "@sutrata/parser": "0.1.1"
50
+ },
51
+ "peerDependencies": {
33
52
  "react": "^18.3.0",
34
53
  "react-dom": "^18.3.0"
35
54
  },
@@ -41,7 +60,15 @@
41
60
  "@types/react-dom": "^18.3.0",
42
61
  "@vitejs/plugin-react": "^4.3.0",
43
62
  "jsdom": "^29.1.1",
63
+ "react": "^18.3.0",
64
+ "react-dom": "^18.3.0",
44
65
  "typescript": "^5.4.0",
45
66
  "vitest": "^1.6.0"
67
+ },
68
+ "scripts": {
69
+ "build": "node scripts/generate-providers.mjs && tsc && node scripts/copy-assets.mjs",
70
+ "generate:providers": "node scripts/generate-providers.mjs",
71
+ "test": "vitest run",
72
+ "test:watch": "vitest"
46
73
  }
47
- }
74
+ }
@@ -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();