@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,335 +0,0 @@
1
- import React from 'react'
2
-
3
- type IconProps = { size?: number }
4
- const base = (size = 16) => ({
5
- width: size,
6
- height: size,
7
- viewBox: '0 0 16 16',
8
- fill: 'none',
9
- stroke: 'currentColor',
10
- strokeWidth: 1.35,
11
- strokeLinecap: 'round' as const,
12
- strokeLinejoin: 'round' as const,
13
- })
14
-
15
- export const NavigatorIcon = ({ size }: IconProps) => (
16
- <svg {...base(size)}>
17
- <rect x="1.5" y="2" width="13" height="12" rx="1.5" />
18
- <line x1="5.5" y1="2" x2="5.5" y2="14" />
19
- </svg>
20
- )
21
-
22
- export const TitlePageIcon = ({ size }: IconProps) => (
23
- <svg {...base(size)}>
24
- <path d="M3 1.5h6l4 4V13.5a1 1 0 01-1 1H3a1 1 0 01-1-1v-11a1 1 0 011-1z" />
25
- <polyline points="9 1.5 9 5.5 13 5.5" />
26
- <line x1="5" y1="8" x2="11" y2="8" />
27
- <line x1="5" y1="10.5" x2="9" y2="10.5" />
28
- </svg>
29
- )
30
-
31
- export const NewIcon = ({ size }: IconProps) => (
32
- <svg {...base(size)}>
33
- <path d="M3 1.5h6l4 4V13.5a1 1 0 01-1 1H3a1 1 0 01-1-1v-11a1 1 0 011-1z" />
34
- <polyline points="9 1.5 9 5.5 13 5.5" />
35
- <line x1="8" y1="8.5" x2="8" y2="12" />
36
- <line x1="6.25" y1="10.25" x2="9.75" y2="10.25" />
37
- </svg>
38
- )
39
-
40
- export const OpenIcon = ({ size }: IconProps) => (
41
- <svg {...base(size)}>
42
- <path d="M1.5 3.5a1 1 0 011-1h3.5l1.5 1.5h7a1 1 0 011 1V6H1.5V3.5z" />
43
- <path d="M1 6.5h14l-1.5 6.5a1 1 0 01-1 .7H3.5a1 1 0 01-1-.7L1 6.5z" />
44
- </svg>
45
- )
46
-
47
- export const ImportDocxIcon = ({ size }: IconProps) => (
48
- <svg {...base(size)}>
49
- <path d="M3 1.5h6l4 4V13.5a1 1 0 01-1 1H3a1 1 0 01-1-1v-11a1 1 0 011-1z" />
50
- <polyline points="9 1.5 9 5.5 13 5.5" />
51
- <line x1="8" y1="7.5" x2="8" y2="11.5" />
52
- <polyline points="6 9.5 8 11.5 10 9.5" />
53
- </svg>
54
- )
55
-
56
- export const StyleIcon = ({ size }: IconProps) => (
57
- <svg {...base(size)}>
58
- <path d="M8 2a6 6 0 00-6 6c0 3.3 2.7 6 6 6 1 0 1.8-.8 1.8-1.8 0-.5-.2-.9-.5-1.2-.3-.4-.5-.9-.5-1.5 0-1.1.9-2 2-2h1.2C13.8 7.5 14 6.7 14 5.8 14 3.7 11.3 2 8 2z" />
59
- <circle cx="5" cy="6" r="0.75" fill="currentColor" stroke="none" />
60
- <circle cx="8" cy="4.8" r="0.75" fill="currentColor" stroke="none" />
61
- <circle cx="11" cy="6" r="0.75" fill="currentColor" stroke="none" />
62
- </svg>
63
- )
64
-
65
- export const SaveIcon = ({ size }: IconProps) => (
66
- <svg {...base(size)}>
67
- <path d="M2.5 2h8.5l2.5 2.5V13.5a1 1 0 01-1 1h-10a1 1 0 01-1-1V3a1 1 0 011-1z" />
68
- <path d="M5 2v4h5V2" />
69
- <path d="M4.5 9h7v5h-7z" />
70
- </svg>
71
- )
72
-
73
- export const SaveAsIcon = ({ size = 16 }: IconProps) => (
74
- <svg {...base(size)}>
75
- <path d="M2.5 2h7.5l2.5 2.5V8" />
76
- <path d="M5 2v3.5h4V2" />
77
- <path d="M2.5 13.5a1 1 0 01-1-1V3a1 1 0 011-1" />
78
- <path d="M1.5 12.5a1 1 0 001 1h5.5" />
79
- <circle cx="12" cy="12" r="3" />
80
- <line x1="12" y1="10.5" x2="12" y2="13.5" />
81
- <line x1="10.5" y1="12" x2="13.5" y2="12" />
82
- </svg>
83
- )
84
-
85
- export const UndoIcon = ({ size }: IconProps) => (
86
- <svg {...base(size)}>
87
- <path d="M2 7c0-2.8 2.2-5 5-5 2.5 0 4.6 1.8 5 4.2" />
88
- <path d="M2 3v4h4" />
89
- </svg>
90
- )
91
-
92
- export const RedoIcon = ({ size }: IconProps) => (
93
- <svg {...base(size)}>
94
- <path d="M14 7c0-2.8-2.2-5-5-5C6.5 2 4.4 3.8 4 6.2" />
95
- <path d="M14 3v4h-4" />
96
- </svg>
97
- )
98
-
99
- export const SceneIcon = ({ size }: IconProps) => (
100
- <svg {...base(size)} viewBox="0 0 18 18">
101
- <line x1="3" y1="5" x2="3" y2="13" />
102
- <line x1="3" y1="9" x2="8" y2="9" />
103
- <line x1="8" y1="5" x2="8" y2="13" />
104
- <line x1="12" y1="5" x2="12" y2="13" />
105
- <line x1="12" y1="9" x2="17" y2="9" />
106
- <line x1="17" y1="5" x2="17" y2="13" />
107
- </svg>
108
- )
109
-
110
- export const ActionIcon = ({ size }: IconProps) => (
111
- <svg {...base(size)} viewBox="0 0 18 18">
112
- <line x1="2" y1="5" x2="16" y2="5" />
113
- <line x1="2" y1="8" x2="16" y2="8" />
114
- <line x1="2" y1="11" x2="12" y2="11" />
115
- <line x1="2" y1="14" x2="9" y2="14" />
116
- </svg>
117
- )
118
-
119
- export const CharacterIcon = ({ size }: IconProps) => (
120
- <svg {...base(size)} viewBox="0 0 18 18">
121
- <circle cx="9" cy="6" r="3" />
122
- <path d="M3 16c0-3.3 2.7-6 6-6s6 2.7 6 6" />
123
- </svg>
124
- )
125
-
126
- export const DialogueIcon = ({ size }: IconProps) => (
127
- <svg {...base(size)} viewBox="0 0 18 18">
128
- <path d="M2 4h14a1 1 0 011 1v6a1 1 0 01-1 1H6l-4 3v-3H2a1 1 0 01-1-1V5a1 1 0 011-1z" />
129
- </svg>
130
- )
131
-
132
- export const ParentheticalIcon = ({ size }: IconProps) => (
133
- <svg {...base(size)} viewBox="0 0 18 18">
134
- <path d="M6 4c-2 2-2 8 0 10" />
135
- <path d="M12 4c2 2 2 8 0 10" />
136
- </svg>
137
- )
138
-
139
- export const TransitionIcon = ({ size }: IconProps) => (
140
- <svg {...base(size)} viewBox="0 0 18 18">
141
- <polyline points="3,6 8,9 3,12" />
142
- <polyline points="9,6 14,9 9,12" />
143
- <line x1="15" y1="6" x2="15" y2="12" />
144
- </svg>
145
- )
146
-
147
- export const CenteredIcon = ({ size }: IconProps) => (
148
- <svg {...base(size)} viewBox="0 0 18 18">
149
- <line x1="3" y1="5" x2="15" y2="5" />
150
- <line x1="5" y1="9" x2="13" y2="9" />
151
- <line x1="3" y1="13" x2="15" y2="13" />
152
- </svg>
153
- )
154
-
155
- export const LyricsIcon = ({ size }: IconProps) => (
156
- <svg {...base(size)} viewBox="0 0 18 18">
157
- <circle cx="5" cy="13" r="2" />
158
- <circle cx="13" cy="11" r="2" />
159
- <line x1="7" y1="13" x2="7" y2="4" />
160
- <line x1="15" y1="11" x2="15" y2="2" />
161
- <line x1="7" y1="4" x2="15" y2="2" />
162
- </svg>
163
- )
164
-
165
- export const NoteIcon = ({ size }: IconProps) => (
166
- <svg {...base(size)} viewBox="0 0 18 18">
167
- <rect x="2" y="3" width="11" height="12" rx="1" />
168
- <line x1="5" y1="7" x2="10" y2="7" />
169
- <line x1="5" y1="10" x2="10" y2="10" />
170
- <line x1="5" y1="13" x2="8" y2="13" />
171
- </svg>
172
- )
173
-
174
- export const PageBreakIcon = ({ size }: IconProps) => (
175
- <svg {...base(size)} viewBox="0 0 18 18">
176
- <line x1="2" y1="9" x2="16" y2="9" />
177
- <line x1="2" y1="5" x2="16" y2="5" strokeDasharray="2 2" opacity="0.4" />
178
- <line x1="2" y1="13" x2="16" y2="13" strokeDasharray="2 2" opacity="0.4" />
179
- </svg>
180
- )
181
-
182
- export const SectionIcon = ({ size }: IconProps) => (
183
- <svg {...base(size)} viewBox="0 0 18 18">
184
- <line x1="3" y1="6" x2="15" y2="6" />
185
- <line x1="3" y1="9" x2="15" y2="9" />
186
- <line x1="6" y1="3" x2="6" y2="7" />
187
- <line x1="12" y1="3" x2="12" y2="7" />
188
- <line x1="6" y1="8" x2="6" y2="12" />
189
- <line x1="12" y1="8" x2="12" y2="12" />
190
- </svg>
191
- )
192
-
193
- export const BoldIcon = ({ size = 16 }: IconProps) => (
194
- <svg width={size} height={size} viewBox="0 0 16 16" fill="currentColor">
195
- <text x="3" y="13" fontFamily="Georgia,serif" fontSize="13" fontWeight="700">B</text>
196
- </svg>
197
- )
198
-
199
- export const ItalicIcon = ({ size = 16 }: IconProps) => (
200
- <svg width={size} height={size} viewBox="0 0 16 16" fill="currentColor">
201
- <text x="5" y="13" fontFamily="Georgia,serif" fontSize="13" fontStyle="italic">I</text>
202
- </svg>
203
- )
204
-
205
- export const UnderlineIcon = ({ size = 16 }: IconProps) => (
206
- <svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth={1.3} strokeLinecap="round">
207
- <text x="4" y="11" fontFamily="Georgia,serif" fontSize="11" fill="currentColor" stroke="none">U</text>
208
- <line x1="3" y1="14" x2="13" y2="14" />
209
- </svg>
210
- )
211
-
212
- export const SourceIcon = ({ size }: IconProps) => (
213
- <svg {...base(size)}>
214
- <polyline points="5,4 2,8 5,12" />
215
- <polyline points="11,4 14,8 11,12" />
216
- <line x1="9" y1="2" x2="7" y2="14" />
217
- </svg>
218
- )
219
-
220
- export const FormattedIcon = ({ size }: IconProps) => (
221
- <svg {...base(size)}>
222
- <line x1="2" y1="4" x2="14" y2="4" />
223
- <line x1="2" y1="7" x2="14" y2="7" />
224
- <line x1="2" y1="10" x2="9" y2="10" />
225
- </svg>
226
- )
227
-
228
- export const RibbonIcon = ({ size }: IconProps) => (
229
- <svg {...base(size)}>
230
- <rect x="1" y="4" width="4" height="4" rx="0.5" />
231
- <rect x="6" y="4" width="4" height="4" rx="0.5" />
232
- <rect x="11" y="4" width="4" height="4" rx="0.5" />
233
- <line x1="1" y1="11" x2="15" y2="11" />
234
- <line x1="1" y1="13.5" x2="10" y2="13.5" />
235
- </svg>
236
- )
237
-
238
- export const ExportIcon = ({ size }: IconProps) => (
239
- <svg {...base(size)}>
240
- <path d="M2.5 7.5v5a1 1 0 001 1h9a1 1 0 001-1v-5" />
241
- <line x1="8" y1="2" x2="8" y2="9.5" />
242
- <polyline points="4.75 5.25 8 2 11.25 5.25" />
243
- </svg>
244
- )
245
-
246
- export const SettingsIcon = ({ size }: IconProps) => (
247
- <svg {...base(size)}>
248
- <circle cx="8" cy="8" r="2.25" />
249
- <path d="M6.9 1.8h2.2l.3 1.4a4.8 4.8 0 011.2.7l1.3-.6 1.5 1.5-.6 1.3c.3.4.5.8.7 1.2l1.4.3v2.2l-1.4.3a4.8 4.8 0 01-.7 1.2l.6 1.3-1.5 1.5-1.3-.6a4.8 4.8 0 01-1.2.7l-.3 1.4H6.9l-.3-1.4a4.8 4.8 0 01-1.2-.7l-1.3.6-1.5-1.5.6-1.3a4.8 4.8 0 01-.7-1.2l-1.4-.3V6.9l1.4-.3a4.8 4.8 0 01.7-1.2l-.6-1.3 1.5-1.5 1.3.6a4.8 4.8 0 011.2-.7l.3-1.4z" />
250
- </svg>
251
- )
252
-
253
- export const OverflowIcon = ({ size = 14 }: IconProps) => (
254
- <svg width={size} height={size} viewBox="0 0 16 16" fill="currentColor">
255
- <circle cx="3" cy="8" r="1.5" />
256
- <circle cx="8" cy="8" r="1.5" />
257
- <circle cx="13" cy="8" r="1.5" />
258
- </svg>
259
- )
260
-
261
- export const EyeIcon = ({ size }: IconProps) => (
262
- <svg {...base(size)}>
263
- <path d="M1 8s3-6 7-6 7 6 7 6-3 6-7 6-7-6-7-6z" />
264
- <circle cx="8" cy="8" r="2.2" />
265
- </svg>
266
- )
267
-
268
- export const EyeOffIcon = ({ size }: IconProps) => (
269
- <svg {...base(size)}>
270
- <path d="M1 8s3-6 7-6 7 6 7 6-3 6-7 6-7-6-7-6z" />
271
- <circle cx="8" cy="8" r="2.2" />
272
- <line x1="2" y1="2" x2="14" y2="14" />
273
- </svg>
274
- )
275
-
276
- export const LockIcon = ({ size }: IconProps) => (
277
- <svg {...base(size)}>
278
- <rect x="3" y="7" width="10" height="7" rx="1.5" />
279
- <path d="M4.5 7V4.5a3.5 3.5 0 017 0V7" />
280
- </svg>
281
- )
282
-
283
- export const StatsIcon = ({ size }: IconProps) => (
284
- <svg {...base(size)}>
285
- <line x1="12" y1="14" x2="12" y2="4" />
286
- <line x1="8" y1="14" x2="8" y2="7" />
287
- <line x1="4" y1="14" x2="4" y2="10" />
288
- </svg>
289
- )
290
-
291
- export const HistoryIcon = ({ size }: IconProps) => (
292
- <svg {...base(size)}>
293
- <circle cx="8" cy="8.5" r="6" />
294
- <polyline points="8,5.5 8,8.5 10.5,10" />
295
- </svg>
296
- )
297
-
298
- export const MicIcon = ({ size }: IconProps) => (
299
- <svg {...base(size)}>
300
- <rect x="5.5" y="2" width="5" height="8" rx="2.5" />
301
- <path d="M3 7.5a5 5 0 0010 0" />
302
- <line x1="8" y1="12.5" x2="8" y2="14.5" />
303
- <line x1="5.5" y1="14.5" x2="10.5" y2="14.5" />
304
- </svg>
305
- )
306
-
307
- export const SparklesIcon = ({ size }: IconProps) => (
308
- <svg {...base(size)}>
309
- <path d="M8 2l1.2 3.8L13 7l-3.8 1.2L8 12l-1.2-3.8L3 7l3.8-1.2L8 2z" />
310
- <path d="M12.5 11l.6 1.9 1.9.6-1.9.6-.6 1.9-.6-1.9-1.9-.6 1.9-.6.6-1.9z" />
311
- </svg>
312
- )
313
-
314
- export const ClockIcon = ({ size }: IconProps) => (
315
- <svg {...base(size)}>
316
- <circle cx="8" cy="8" r="6" />
317
- <polyline points="8 4.5 8 8 10.5 9.5" />
318
- </svg>
319
- )
320
-
321
- export const MenuIcon = ({ size }: IconProps) => (
322
- <svg {...base(size)}>
323
- <line x1="2.5" y1="4.5" x2="13.5" y2="4.5" />
324
- <line x1="2.5" y1="8" x2="13.5" y2="8" />
325
- <line x1="2.5" y1="11.5" x2="13.5" y2="11.5" />
326
- </svg>
327
- )
328
-
329
- export const CloseIcon = ({ size }: IconProps) => (
330
- <svg {...base(size)}>
331
- <line x1="3.5" y1="3.5" x2="12.5" y2="12.5" />
332
- <line x1="12.5" y1="3.5" x2="3.5" y2="12.5" />
333
- </svg>
334
- )
335
-
@@ -1,59 +0,0 @@
1
- import type { Command } from 'prosemirror-state'
2
- import { TextSelection } from 'prosemirror-state'
3
- import { toggleMark } from 'prosemirror-commands'
4
- import { undo, redo } from 'prosemirror-history'
5
- import { schema } from '../editor/schema'
6
-
7
- export function makeSetBlock(typeName: string, attrs: Record<string, unknown> = {}): Command {
8
- return (state, dispatch) => {
9
- const nodeType = schema.nodes[typeName]
10
- if (!nodeType) return false
11
- if (dispatch) {
12
- const { from, to } = state.selection
13
- const tr = state.tr
14
- state.doc.nodesBetween(from, to, (node, pos) => {
15
- if (node.isBlock && node.type.name !== 'doc') {
16
- tr.setNodeMarkup(pos, nodeType, { ...node.attrs, ...attrs })
17
- }
18
- })
19
- dispatch(tr)
20
- }
21
- return true
22
- }
23
- }
24
-
25
- export function makeToggleMark(markName: string): Command {
26
- return (state, dispatch) => {
27
- const mark = schema.marks[markName]
28
- if (!mark) return false
29
- if (state.selection.empty) return false
30
- return toggleMark(mark)(state, dispatch)
31
- }
32
- }
33
-
34
- // The "Note" toolbar button/shortcut does double duty: on an Action line it
35
- // marks text as an inline [[ ]] note — the selection, or (with no selection)
36
- // the whole line — leaving the line itself an Action. Inline notes aren't
37
- // allowed on any other line type, so elsewhere it falls back to the original
38
- // whole-block conversion to a standalone `note` block.
39
- export function makeSetOrToggleNote(): Command {
40
- return (state, dispatch) => {
41
- const { $from, $to, empty } = state.selection
42
- const inAction = $from.parent.type.name === 'action' && (empty || $from.sameParent($to))
43
- if (!inAction) return makeSetBlock('note')(state, dispatch)
44
-
45
- const mark = schema.marks['note_mark']
46
- if (!mark) return false
47
-
48
- let target = state
49
- if (empty) {
50
- const lineStart = $from.start()
51
- const lineEnd = $from.end()
52
- if (lineEnd <= lineStart) return false
53
- target = state.apply(state.tr.setSelection(TextSelection.create(state.doc, lineStart, lineEnd)))
54
- }
55
- return toggleMark(mark)(target, dispatch)
56
- }
57
- }
58
-
59
- export { undo, redo }
@@ -1,111 +0,0 @@
1
- import { useEffect, useRef } from 'react'
2
- import { EditorState } from '@codemirror/state'
3
- import { EditorView, keymap } from '@codemirror/view'
4
- import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'
5
- import { HighlightStyle, syntaxHighlighting } from '@codemirror/language'
6
- import { tags } from '@lezer/highlight'
7
- import { sutraLanguage } from './sutra-lang'
8
- import { useDocument } from '../context/DocumentContext'
9
- import { setSourceView } from '../editor/editor-bus'
10
- import { sourceFindHighlightExtension } from './find-highlight-source'
11
-
12
- // Light theme matching the app's warm off-white palette.
13
- // No internal scroll — the editor grows to content height and .cs-main scrolls.
14
- const sutraTheme = EditorView.theme({
15
- '&': {
16
- width: '100%',
17
- maxWidth: '960px',
18
- fontSize: '12pt',
19
- fontFamily: "'Courier New', Courier, monospace",
20
- lineHeight: '1.5',
21
- backgroundColor: 'transparent',
22
- },
23
- '.cm-scroller': { overflow: 'visible', lineHeight: '1.5' },
24
- '.cm-content': { padding: '0', caretColor: '#1a1a18' },
25
- '.cm-line': { padding: '0' },
26
- '&.cm-focused': { outline: 'none' },
27
- '.cm-cursor': { borderLeftColor: '#1a1a18' },
28
- '.cm-selectionBackground, ::selection': { backgroundColor: '#b8d3f0' },
29
- '&.cm-focused .cm-selectionBackground': { backgroundColor: '#b8d3f0' },
30
- })
31
-
32
- const sutraHighlight = HighlightStyle.define([
33
- // ## Scene heading — dark teal, bold
34
- { tag: tags.keyword, color: '#0a5c6a', fontWeight: 'bold' },
35
- // # Section heading — medium slate
36
- { tag: tags.heading, color: '#4a5568', fontWeight: 'bold' },
37
- // @Character — warm brown/amber
38
- { tag: tags.variableName, color: '#9c4221', fontWeight: 'bold' },
39
- // Dialogue — dark ink, slightly indented feel via color
40
- { tag: tags.labelName, color: '#2a3568' },
41
- // Parenthetical — muted gray-green
42
- { tag: tags.string, color: '#5a7a4a', fontStyle: 'italic' },
43
- // >> Transition / centered — medium blue-gray
44
- { tag: tags.operator, color: '#4a5a7a', fontWeight: 'bold' },
45
- // ~ Lyrics — purple
46
- { tag: tags.meta, color: '#6b46c1' },
47
- // [[ Note ]] / <!-- comment --> — gray
48
- { tag: tags.comment, color: '#9ca3af', fontStyle: 'italic' },
49
- // & metadata key — teal
50
- { tag: tags.attributeName, color: '#0694a2' },
51
- // Frontmatter values — muted
52
- { tag: tags.attributeValue, color: '#6b7280' },
53
- // === page break, sigils — light gray
54
- { tag: tags.punctuation, color: '#9ca3af' },
55
- ])
56
-
57
- export function SourceView() {
58
- const { text, setText } = useDocument()
59
- const containerRef = useRef<HTMLDivElement>(null)
60
- const viewRef = useRef<EditorView | null>(null)
61
- // Track externally-driven text so we don't feed our own updates back
62
- const lastTextRef = useRef(text)
63
-
64
- useEffect(() => {
65
- if (!containerRef.current) return
66
-
67
- const view = new EditorView({
68
- state: EditorState.create({
69
- doc: text,
70
- extensions: [
71
- history(),
72
- keymap.of([...defaultKeymap, ...historyKeymap]),
73
- sutraLanguage,
74
- sutraTheme,
75
- syntaxHighlighting(sutraHighlight),
76
- sourceFindHighlightExtension,
77
- EditorView.lineWrapping,
78
- EditorView.updateListener.of(update => {
79
- if (update.docChanged) {
80
- const newText = update.state.doc.toString()
81
- lastTextRef.current = newText
82
- setText(newText)
83
- }
84
- }),
85
- ],
86
- }),
87
- parent: containerRef.current,
88
- })
89
-
90
- viewRef.current = view
91
- setSourceView(view)
92
- return () => {
93
- setSourceView(null)
94
- view.destroy()
95
- viewRef.current = null
96
- }
97
- // eslint-disable-next-line react-hooks/exhaustive-deps
98
- }, [])
99
-
100
- // Sync external text changes (e.g. switching back from formatted view)
101
- useEffect(() => {
102
- const view = viewRef.current
103
- if (!view || text === lastTextRef.current) return
104
- lastTextRef.current = text
105
- view.dispatch({
106
- changes: { from: 0, to: view.state.doc.length, insert: text },
107
- })
108
- }, [text])
109
-
110
- return <div ref={containerRef} className="cs-source-view" />
111
- }
@@ -1,57 +0,0 @@
1
- import { StateEffect, StateField } from '@codemirror/state'
2
- import { Decoration, EditorView } from '@codemirror/view'
3
- import type { DecorationSet } from '@codemirror/view'
4
- import { getSourceView } from '../editor/editor-bus'
5
-
6
- export interface SourceMatch {
7
- start: number
8
- end: number
9
- }
10
-
11
- const setMatchesEffect = StateEffect.define<{ matches: SourceMatch[]; activeIndex: number }>()
12
-
13
- function buildDecorations(matches: SourceMatch[], activeIndex: number, docLength: number): DecorationSet {
14
- if (matches.length === 0) return Decoration.none
15
- const decos = matches
16
- .filter(m => m.start >= 0 && m.end <= docLength && m.start < m.end)
17
- .map((m, i) => Decoration.mark({ class: i === activeIndex ? 'cs-find-active' : 'cs-find-match' }).range(m.start, m.end))
18
- return Decoration.set(decos, true)
19
- }
20
-
21
- const findHighlightField = StateField.define<DecorationSet>({
22
- create() {
23
- return Decoration.none
24
- },
25
- update(deco, tr) {
26
- deco = deco.map(tr.changes)
27
- for (const effect of tr.effects) {
28
- if (effect.is(setMatchesEffect)) {
29
- deco = buildDecorations(effect.value.matches, effect.value.activeIndex, tr.state.doc.length)
30
- }
31
- }
32
- return deco
33
- },
34
- provide: field => EditorView.decorations.from(field),
35
- })
36
-
37
- /** CodeMirror extension providing find-match highlighting in source view. */
38
- export const sourceFindHighlightExtension = findHighlightField
39
-
40
- /** Called by FindReplace whenever matches or active index change in source mode. */
41
- export function setSourceFindHighlights(matches: SourceMatch[], activeIndex: number): void {
42
- const view = getSourceView()
43
- if (!view) return
44
- view.dispatch({ effects: setMatchesEffect.of({ matches, activeIndex }) })
45
- }
46
-
47
- /** Called by FindReplace when the panel closes — clears all source-view highlights. */
48
- export function clearSourceFindHighlights(): void {
49
- setSourceFindHighlights([], 0)
50
- }
51
-
52
- /** Scroll the source view so the given match is visible. */
53
- export function scrollToSourceMatch(match: SourceMatch): void {
54
- const view = getSourceView()
55
- if (!view) return
56
- view.dispatch({ effects: EditorView.scrollIntoView(match.start, { y: 'center' }) })
57
- }
@@ -1,121 +0,0 @@
1
- import { StreamLanguage, LanguageSupport } from '@codemirror/language'
2
-
3
- type State = {
4
- inFrontmatter: boolean
5
- lineNum: number
6
- afterCharacter: boolean
7
- }
8
-
9
- const sutra = StreamLanguage.define<State>({
10
- name: 'sutra',
11
-
12
- startState: () => ({ inFrontmatter: false, lineNum: 0, afterCharacter: false }),
13
-
14
- token(stream, state) {
15
- state.lineNum++
16
-
17
- // Blank line — reset dialogue context, emit nothing
18
- if (stream.match(/^\s*$/)) {
19
- state.afterCharacter = false
20
- return null
21
- }
22
-
23
- // Frontmatter delimiter ---
24
- if (state.lineNum === 1 && stream.match(/^---\s*$/)) {
25
- state.inFrontmatter = true
26
- return 'keyword'
27
- }
28
- if (state.inFrontmatter) {
29
- if (stream.match(/^---\s*$/)) {
30
- state.inFrontmatter = false
31
- return 'keyword'
32
- }
33
- // key: value lines inside frontmatter
34
- if (stream.match(/^[A-Za-z][A-Za-z ]*:/)) return 'attributeName'
35
- stream.skipToEnd()
36
- return 'string'
37
- }
38
-
39
- // Page break ===
40
- if (stream.match(/^===\s*$/)) {
41
- state.afterCharacter = false
42
- return 'punctuation'
43
- }
44
-
45
- // Comment <!-- ... -->
46
- if (stream.match(/^<!--[\s\S]*?-->\s*$/)) {
47
- state.afterCharacter = false
48
- return 'comment'
49
- }
50
-
51
- // Note [[ ... ]]
52
- if (stream.match(/^\[\[[\s\S]*?\]\]\s*$/)) {
53
- state.afterCharacter = false
54
- return 'comment'
55
- }
56
-
57
- // Scene heading ## ...
58
- if (stream.match(/^##/)) {
59
- stream.skipToEnd()
60
- state.afterCharacter = false
61
- return 'keyword'
62
- }
63
-
64
- // Section heading # ...
65
- if (stream.match(/^#(?!#)/)) {
66
- stream.skipToEnd()
67
- state.afterCharacter = false
68
- return 'heading'
69
- }
70
-
71
- // Scene metadata & key: value
72
- if (stream.match(/^&\s/)) {
73
- stream.skipToEnd()
74
- state.afterCharacter = false
75
- return 'attributeName'
76
- }
77
-
78
- // Character cue @name ...
79
- if (stream.match(/^@/)) {
80
- stream.skipToEnd()
81
- state.afterCharacter = true
82
- return 'variableName'
83
- }
84
-
85
- // Centered or transition >> ...
86
- if (stream.match(/^>>/)) {
87
- stream.skipToEnd()
88
- state.afterCharacter = false
89
- return 'operator'
90
- }
91
-
92
- // Lyrics ~ ...
93
- if (stream.match(/^~\s/)) {
94
- stream.skipToEnd()
95
- state.afterCharacter = false
96
- return 'meta'
97
- }
98
-
99
- // Parenthetical (inside dialogue context)
100
- if (state.afterCharacter && stream.match(/^\(/)) {
101
- stream.skipToEnd()
102
- return 'string'
103
- }
104
-
105
- // Dialogue (inside dialogue context) — styled differently from action
106
- if (state.afterCharacter) {
107
- stream.skipToEnd()
108
- return 'labelName'
109
- }
110
-
111
- // Action — unstyled
112
- stream.skipToEnd()
113
- return null
114
- },
115
-
116
- blankLine(state) {
117
- state.afterCharacter = false
118
- },
119
- })
120
-
121
- export const sutraLanguage = new LanguageSupport(sutra)