@sutrata/editor 0.1.1 → 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 +30 -3
  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
@@ -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)