@ecsplico/editor 2.1.2 → 3.0.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 (692) hide show
  1. package/CHANGELOG.md +68 -1
  2. package/README.md +39 -156
  3. package/dist/adapters/comments/drizzle.d.ts +1 -1
  4. package/dist/adapters/comments/{client.js → rest-model.js} +3 -3
  5. package/dist/adapters/comments/rest-model.js.map +1 -0
  6. package/dist/adapters/comments/types.d.ts +2 -2
  7. package/dist/adapters/document-state.d.ts +23 -0
  8. package/dist/adapters/document-state.js +66 -0
  9. package/dist/adapters/document-state.js.map +1 -0
  10. package/dist/adapters/documents.js +14 -11
  11. package/dist/adapters/documents.js.map +1 -1
  12. package/dist/adapters/drizzle.d.ts +2 -2
  13. package/dist/adapters/drizzle.js +97 -88
  14. package/dist/adapters/drizzle.js.map +1 -1
  15. package/dist/adapters/forge.d.ts +12 -7
  16. package/dist/adapters/forge.js +38 -45
  17. package/dist/adapters/forge.js.map +1 -1
  18. package/dist/adapters/http.d.ts +20 -0
  19. package/dist/adapters/http.js +68 -0
  20. package/dist/adapters/http.js.map +1 -0
  21. package/dist/adapters/index.d.ts +5 -5
  22. package/dist/adapters/json.d.ts +3 -3
  23. package/dist/adapters/json.js +58 -49
  24. package/dist/adapters/json.js.map +1 -1
  25. package/dist/adapters/memory.d.ts +21 -0
  26. package/dist/adapters/memory.js +53 -0
  27. package/dist/adapters/memory.js.map +1 -0
  28. package/dist/adapters/nitro.d.ts +23 -20
  29. package/dist/adapters/nitro.js +58 -163
  30. package/dist/adapters/nitro.js.map +1 -1
  31. package/dist/adapters/react.d.ts +55 -0
  32. package/dist/adapters/react.js +209 -0
  33. package/dist/adapters/react.js.map +1 -0
  34. package/dist/adapters/shared.d.ts +15 -21
  35. package/dist/adapters/shared.js +10 -8
  36. package/dist/adapters/shared.js.map +1 -1
  37. package/dist/adapters/vue.d.ts +15 -30
  38. package/dist/adapters/vue.js +36 -87
  39. package/dist/adapters/vue.js.map +1 -1
  40. package/dist/config/index.d.ts +105 -0
  41. package/dist/config/index.js +95 -0
  42. package/dist/config/index.js.map +1 -0
  43. package/dist/core/abilities.js +1 -0
  44. package/dist/core/abilities.js.map +1 -1
  45. package/dist/{adapters/routes.js → core/api-routes.js} +5 -3
  46. package/dist/core/api-routes.js.map +1 -0
  47. package/dist/core/backend-capability.d.ts +7 -0
  48. package/dist/core/backend-capability.js +17 -0
  49. package/dist/core/backend-capability.js.map +1 -0
  50. package/dist/core/backend-types.d.ts +245 -0
  51. package/dist/core/backend-types.js +17 -0
  52. package/dist/core/backend-types.js.map +1 -0
  53. package/dist/core/backend.d.ts +108 -0
  54. package/dist/core/backend.js +14 -0
  55. package/dist/core/backend.js.map +1 -0
  56. package/dist/core/block-convert.d.ts +7 -5
  57. package/dist/core/block-convert.js +28 -5
  58. package/dist/core/block-convert.js.map +1 -1
  59. package/dist/core/command-types.d.ts +89 -0
  60. package/dist/core/commands.d.ts +40 -0
  61. package/dist/core/commands.js +151 -0
  62. package/dist/core/commands.js.map +1 -0
  63. package/dist/core/context.d.ts +17 -20
  64. package/dist/core/context.js +24 -21
  65. package/dist/core/context.js.map +1 -1
  66. package/dist/core/core-feature.d.ts +22 -7
  67. package/dist/core/core-feature.js +51 -18
  68. package/dist/core/core-feature.js.map +1 -1
  69. package/dist/core/define-editor.d.ts +4 -3
  70. package/dist/core/define-editor.js +14 -7
  71. package/dist/core/define-editor.js.map +1 -1
  72. package/dist/core/document-markdown.js +12 -3
  73. package/dist/core/document-markdown.js.map +1 -1
  74. package/dist/core/editor-ui.d.ts +8 -1
  75. package/dist/core/editor-ui.js +9 -8
  76. package/dist/core/editor-ui.js.map +1 -1
  77. package/dist/core/extension-points.d.ts +3 -15
  78. package/dist/core/feature-context.d.ts +8 -7
  79. package/dist/core/feature-context.js +12 -11
  80. package/dist/core/feature-context.js.map +1 -1
  81. package/dist/core/feature.d.ts +35 -19
  82. package/dist/core/feature.js.map +1 -1
  83. package/dist/core/fences.d.ts +2 -1
  84. package/dist/core/fences.js +3 -2
  85. package/dist/core/fences.js.map +1 -1
  86. package/dist/core/fetch-error.d.ts +1 -1
  87. package/dist/core/fetch-error.js +3 -1
  88. package/dist/core/fetch-error.js.map +1 -1
  89. package/dist/core/hash.d.ts +5 -0
  90. package/dist/core/hash.js +11 -0
  91. package/dist/core/hash.js.map +1 -0
  92. package/dist/core/http-backend.d.ts +11 -0
  93. package/dist/core/http-backend.js +195 -0
  94. package/dist/core/http-backend.js.map +1 -0
  95. package/dist/core/index.d.ts +14 -9
  96. package/dist/core/index.js +12 -9
  97. package/dist/core/palette.d.ts +48 -0
  98. package/dist/core/palette.js +95 -0
  99. package/dist/core/palette.js.map +1 -0
  100. package/dist/core/schema/abilities.d.ts +2 -1
  101. package/dist/core/schema/abilities.js +5 -1
  102. package/dist/core/schema/abilities.js.map +1 -1
  103. package/dist/core/schema/index.d.ts +11 -5
  104. package/dist/core/schema/index.js +13 -8
  105. package/dist/core/schema/index.js.map +1 -1
  106. package/dist/core/schema/mark-flags.d.ts +8 -1
  107. package/dist/core/schema/of-features.d.ts +13 -0
  108. package/dist/core/schema/of-features.js +14 -0
  109. package/dist/core/schema/of-features.js.map +1 -0
  110. package/dist/core/slash.d.ts +8 -6
  111. package/dist/core/slash.js +24 -16
  112. package/dist/core/slash.js.map +1 -1
  113. package/dist/core/surface-session.js +13 -5
  114. package/dist/core/surface-session.js.map +1 -1
  115. package/dist/core/toolbar-items.d.ts +10 -4
  116. package/dist/core/toolbar-items.js +269 -195
  117. package/dist/core/toolbar-items.js.map +1 -1
  118. package/dist/core/util.js +11 -0
  119. package/dist/core/util.js.map +1 -0
  120. package/dist/document/DocumentSurface.d.ts +1 -1
  121. package/dist/features/ai/agent-op-review.d.ts +1 -1
  122. package/dist/features/ai/agent-op-review.js +1 -1
  123. package/dist/features/ai/agent-ops.js +1 -1
  124. package/dist/features/ai/ai-apply.d.ts +2 -2
  125. package/dist/features/ai/ai-apply.js +1 -1
  126. package/dist/features/ai/ai-review.js +1 -1
  127. package/dist/features/ai/ai-sse.d.ts +6 -6
  128. package/dist/features/ai/ai-sse.js +20 -63
  129. package/dist/features/ai/ai-sse.js.map +1 -1
  130. package/dist/features/ai/assistant.d.ts +8 -7
  131. package/dist/features/ai/assistant.js +3 -3
  132. package/dist/features/ai/assistant.js.map +1 -1
  133. package/dist/features/ai/block-actions.d.ts +3 -2
  134. package/dist/features/ai/block-actions.js +3 -2
  135. package/dist/features/ai/block-actions.js.map +1 -1
  136. package/dist/features/ai/components/AiSelectionMenu.js +1 -1
  137. package/dist/features/ai/components/DocumentChatAnswer.js +1 -1
  138. package/dist/features/ai/components/DocumentChatOps.vue.d.ts +1 -1
  139. package/dist/features/ai/components/SettingsModelProfiles.js +8 -8
  140. package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -1
  141. package/dist/features/ai/document-chat-model.d.ts +8 -7
  142. package/dist/features/ai/document-chat-model.js +5 -5
  143. package/dist/features/ai/document-chat-model.js.map +1 -1
  144. package/dist/features/ai/document-chat.js +1 -1
  145. package/dist/features/ai/feature.js +27 -14
  146. package/dist/features/ai/feature.js.map +1 -1
  147. package/dist/features/ai/ghost-autocomplete.d.ts +7 -6
  148. package/dist/features/ai/ghost-autocomplete.js +6 -9
  149. package/dist/features/ai/ghost-autocomplete.js.map +1 -1
  150. package/dist/features/ai/index.d.ts +4 -3
  151. package/dist/features/ai/index.js +10 -10
  152. package/dist/features/ai/model-profiles.d.ts +5 -4
  153. package/dist/features/ai/model-profiles.js +2 -2
  154. package/dist/features/ai/model-profiles.js.map +1 -1
  155. package/dist/features/ai/protocol/block-lines.js +1 -1
  156. package/dist/features/ai/protocol/block-lines.js.map +1 -1
  157. package/dist/features/ai/protocol/chat.js +4 -1
  158. package/dist/features/ai/protocol/chat.js.map +1 -1
  159. package/dist/features/ai/protocol/index.d.ts +3 -2
  160. package/dist/features/ai/protocol/models.d.ts +2 -16
  161. package/dist/features/ai/protocol/models.js.map +1 -1
  162. package/dist/features/ai/rate-limit.d.ts +1 -1
  163. package/dist/features/ai/rate-limit.js +3 -1
  164. package/dist/features/ai/rate-limit.js.map +1 -1
  165. package/dist/features/ai/react/AiSelectionMenu.js +2 -2
  166. package/dist/features/ai/react/AiSidebar.js +4 -4
  167. package/dist/features/ai/react/DocumentChatAnswer.js +1 -1
  168. package/dist/features/ai/react/DocumentChatOps.d.ts +1 -1
  169. package/dist/features/ai/react/DocumentChatOps.js +1 -1
  170. package/dist/features/ai/react/SettingsModelProfiles.js +8 -10
  171. package/dist/features/ai/react/SettingsModelProfiles.js.map +1 -1
  172. package/dist/features/ai/react/use-ai-assistant.d.ts +1 -1
  173. package/dist/features/ai/react/use-ai-assistant.js +3 -3
  174. package/dist/features/ai/react/use-ai-assistant.js.map +1 -1
  175. package/dist/features/ai/react/use-document-chat.js +2 -2
  176. package/dist/features/ai/react/use-document-chat.js.map +1 -1
  177. package/dist/features/ai/react/use-model-profiles.d.ts +2 -1
  178. package/dist/features/ai/selection-flow.d.ts +1 -1
  179. package/dist/features/ai/selection-flow.js +1 -1
  180. package/dist/features/ai/sidebar-flow.d.ts +1 -1
  181. package/dist/features/ai/sidebar-flow.js +1 -1
  182. package/dist/features/ai/use-ai-assistant.d.ts +1 -1
  183. package/dist/features/ai/use-ai-assistant.js +3 -3
  184. package/dist/features/ai/use-ai-assistant.js.map +1 -1
  185. package/dist/features/ai/use-document-chat.js +2 -2
  186. package/dist/features/ai/use-document-chat.js.map +1 -1
  187. package/dist/features/ai/use-model-profiles.d.ts +2 -1
  188. package/dist/features/authors/attribution.d.ts +1 -1
  189. package/dist/features/authors/feature.js +12 -11
  190. package/dist/features/authors/feature.js.map +1 -1
  191. package/dist/features/block-ui/block-actions.d.ts +1 -1
  192. package/dist/features/block-ui/block-actions.js +1 -1
  193. package/dist/features/block-ui/block-actions.js.map +1 -1
  194. package/dist/features/block-ui/components/BlockUi.js +1 -1
  195. package/dist/features/block-ui/feature.js +1 -0
  196. package/dist/features/block-ui/feature.js.map +1 -1
  197. package/dist/features/block-ui/react/BlockUi.js +1 -1
  198. package/dist/features/collab/anchor-resolver.js +1 -1
  199. package/dist/features/collab/feature.js +2 -0
  200. package/dist/features/collab/feature.js.map +1 -1
  201. package/dist/features/comments/components/CommentThreadView.vue.d.ts +2 -1
  202. package/dist/features/comments/feature.js +12 -11
  203. package/dist/features/comments/feature.js.map +1 -1
  204. package/dist/features/comments/index.d.ts +3 -2
  205. package/dist/features/comments/index.js +1 -1
  206. package/dist/features/comments/margin-layout.d.ts +3 -2
  207. package/dist/features/comments/model.d.ts +3 -67
  208. package/dist/features/comments/model.js +2 -2
  209. package/dist/features/comments/model.js.map +1 -1
  210. package/dist/features/comments/react/CommentThreadView.d.ts +3 -2
  211. package/dist/features/comments/react/use-comments.d.ts +2 -1
  212. package/dist/features/comments/react.d.ts +1 -1
  213. package/dist/features/comments/schema.d.ts +2 -52
  214. package/dist/features/comments/schema.js.map +1 -1
  215. package/dist/features/comments/snapshot-equality.js.map +1 -1
  216. package/dist/features/comments/use-comments.d.ts +3 -2
  217. package/dist/features/comments/vue.d.ts +1 -1
  218. package/dist/features/composites/accepted-children.d.ts +1 -0
  219. package/dist/features/composites/accepted-children.js +16 -1
  220. package/dist/features/composites/accepted-children.js.map +1 -1
  221. package/dist/features/composites/ai-brief.js +31 -0
  222. package/dist/features/composites/ai-brief.js.map +1 -1
  223. package/dist/features/composites/bibliography-command.d.ts +11 -0
  224. package/dist/features/composites/bibliography-form.js +90 -0
  225. package/dist/features/composites/bibliography-form.js.map +1 -0
  226. package/dist/features/composites/bibliography.d.ts +33 -0
  227. package/dist/features/composites/bibliography.js +120 -0
  228. package/dist/features/composites/bibliography.js.map +1 -0
  229. package/dist/features/composites/components/BibliographyEditor.js +261 -0
  230. package/dist/features/composites/components/BibliographyEditor.js.map +1 -0
  231. package/dist/features/composites/components/CompositeChrome.js +75 -26
  232. package/dist/features/composites/components/CompositeChrome.js.map +1 -1
  233. package/dist/features/composites/components/CompositeDetails.js +46 -11
  234. package/dist/features/composites/components/CompositeDetails.js.map +1 -1
  235. package/dist/features/composites/components/CompositeDetails.vue.d.ts +2 -1
  236. package/dist/features/composites/components/CompositeFieldEditor.js +37 -4
  237. package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
  238. package/dist/features/composites/components/CompositeInspector.js +17 -5
  239. package/dist/features/composites/components/CompositeInspector.js.map +1 -1
  240. package/dist/features/composites/components/CompositeTable.js +13 -4
  241. package/dist/features/composites/components/CompositeTable.js.map +1 -1
  242. package/dist/features/composites/components/CompositeTagsEditor.js +1 -1
  243. package/dist/features/composites/components/CompositeView.js +11 -1
  244. package/dist/features/composites/components/CompositeView.js.map +1 -1
  245. package/dist/features/composites/components/DerivationBadge.js +103 -0
  246. package/dist/features/composites/components/DerivationBadge.js.map +1 -0
  247. package/dist/features/composites/components/ReferenceFieldSelect.js +60 -0
  248. package/dist/features/composites/components/ReferenceFieldSelect.js.map +1 -0
  249. package/dist/features/composites/components/ReferenceView.js +122 -0
  250. package/dist/features/composites/components/ReferenceView.js.map +1 -0
  251. package/dist/features/composites/components/ReferenceView.vue.d.ts +96 -0
  252. package/dist/features/composites/components/SettingsEditingTab.js +1 -1
  253. package/dist/features/composites/convert.js +2 -2
  254. package/dist/features/composites/convert.js.map +1 -1
  255. package/dist/features/composites/derivation.d.ts +61 -0
  256. package/dist/features/composites/derivation.js +98 -0
  257. package/dist/features/composites/derivation.js.map +1 -0
  258. package/dist/features/composites/derived-chrome.js +69 -0
  259. package/dist/features/composites/derived-chrome.js.map +1 -0
  260. package/dist/features/composites/feature.js +159 -109
  261. package/dist/features/composites/feature.js.map +1 -1
  262. package/dist/features/composites/fences.d.ts +4 -3
  263. package/dist/features/composites/fences.js +51 -7
  264. package/dist/features/composites/fences.js.map +1 -1
  265. package/dist/features/composites/index.d.ts +12 -3
  266. package/dist/features/composites/index.js +15 -6
  267. package/dist/features/composites/inspector-model.d.ts +4 -1
  268. package/dist/features/composites/inspector-model.js +8 -4
  269. package/dist/features/composites/inspector-model.js.map +1 -1
  270. package/dist/features/composites/kinds.d.ts +8 -1
  271. package/dist/features/composites/kinds.js +145 -6
  272. package/dist/features/composites/kinds.js.map +1 -1
  273. package/dist/features/composites/legacy.d.ts +4 -1
  274. package/dist/features/composites/legacy.js +56 -1
  275. package/dist/features/composites/legacy.js.map +1 -1
  276. package/dist/features/composites/meta-display.js +40 -1
  277. package/dist/features/composites/meta-display.js.map +1 -1
  278. package/dist/features/composites/meta-keys.d.ts +7 -0
  279. package/dist/features/composites/meta-keys.js +21 -0
  280. package/dist/features/composites/meta-keys.js.map +1 -0
  281. package/dist/features/composites/node-views.d.ts +6 -3
  282. package/dist/features/composites/node-views.js +7 -3
  283. package/dist/features/composites/node-views.js.map +1 -1
  284. package/dist/features/composites/nodes.d.ts +6 -1
  285. package/dist/features/composites/nodes.js +33 -7
  286. package/dist/features/composites/nodes.js.map +1 -1
  287. package/dist/features/composites/react/BibliographyEditor.js +229 -0
  288. package/dist/features/composites/react/BibliographyEditor.js.map +1 -0
  289. package/dist/features/composites/react/CompositeChrome.js +65 -17
  290. package/dist/features/composites/react/CompositeChrome.js.map +1 -1
  291. package/dist/features/composites/react/CompositeDetails.d.ts +2 -1
  292. package/dist/features/composites/react/CompositeDetails.js +45 -6
  293. package/dist/features/composites/react/CompositeDetails.js.map +1 -1
  294. package/dist/features/composites/react/CompositeFieldEditor.d.ts +1 -1
  295. package/dist/features/composites/react/CompositeFieldEditor.js +24 -1
  296. package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -1
  297. package/dist/features/composites/react/CompositeInspector.js +11 -5
  298. package/dist/features/composites/react/CompositeInspector.js.map +1 -1
  299. package/dist/features/composites/react/CompositeStatusMenu.js +1 -1
  300. package/dist/features/composites/react/CompositeSummaryEditor.js +1 -1
  301. package/dist/features/composites/react/CompositeTable.js +16 -2
  302. package/dist/features/composites/react/CompositeTable.js.map +1 -1
  303. package/dist/features/composites/react/CompositeTagsEditor.js +2 -2
  304. package/dist/features/composites/react/CompositeView.js +6 -1
  305. package/dist/features/composites/react/CompositeView.js.map +1 -1
  306. package/dist/features/composites/react/DerivationBadge.js +116 -0
  307. package/dist/features/composites/react/DerivationBadge.js.map +1 -0
  308. package/dist/features/composites/react/ReferenceFieldSelect.js +52 -0
  309. package/dist/features/composites/react/ReferenceFieldSelect.js.map +1 -0
  310. package/dist/features/composites/react/ReferenceView.d.ts +11 -0
  311. package/dist/features/composites/react/ReferenceView.js +115 -0
  312. package/dist/features/composites/react/ReferenceView.js.map +1 -0
  313. package/dist/features/composites/react/SettingsEditingTab.js +2 -2
  314. package/dist/features/composites/react.d.ts +2 -1
  315. package/dist/features/composites/react.js +7 -3
  316. package/dist/features/composites/react.js.map +1 -1
  317. package/dist/features/composites/reference-ai.js +16 -0
  318. package/dist/features/composites/reference-ai.js.map +1 -0
  319. package/dist/features/composites/reference-label.d.ts +19 -0
  320. package/dist/features/composites/reference-label.js +58 -0
  321. package/dist/features/composites/reference-label.js.map +1 -0
  322. package/dist/features/composites/reference-suggestion.d.ts +10 -0
  323. package/dist/features/composites/reference-suggestion.js +41 -0
  324. package/dist/features/composites/reference-suggestion.js.map +1 -0
  325. package/dist/features/composites/reference-text.d.ts +18 -0
  326. package/dist/features/composites/reference-text.js +89 -0
  327. package/dist/features/composites/reference-text.js.map +1 -0
  328. package/dist/features/composites/reference.d.ts +44 -0
  329. package/dist/features/composites/reference.js +148 -0
  330. package/dist/features/composites/reference.js.map +1 -0
  331. package/dist/features/composites/reveal-source.d.ts +32 -0
  332. package/dist/features/composites/reveal-source.js +89 -0
  333. package/dist/features/composites/reveal-source.js.map +1 -0
  334. package/dist/features/composites/rows.d.ts +21 -2
  335. package/dist/features/composites/rows.js +63 -9
  336. package/dist/features/composites/rows.js.map +1 -1
  337. package/dist/features/composites/schema.d.ts +9 -3
  338. package/dist/features/composites/schema.js +11 -4
  339. package/dist/features/composites/schema.js.map +1 -1
  340. package/dist/features/composites/table-model.js +12 -21
  341. package/dist/features/composites/table-model.js.map +1 -1
  342. package/dist/features/composites/vue.d.ts +3 -2
  343. package/dist/features/composites/vue.js +8 -4
  344. package/dist/features/composites/vue.js.map +1 -1
  345. package/dist/features/media/assets.d.ts +2 -0
  346. package/dist/features/media/assets.js +3 -2
  347. package/dist/features/media/assets.js.map +1 -1
  348. package/dist/features/media/components/FileView.js +13 -4
  349. package/dist/features/media/components/FileView.js.map +1 -1
  350. package/dist/features/media/feature.d.ts +3 -3
  351. package/dist/features/media/feature.js +21 -28
  352. package/dist/features/media/feature.js.map +1 -1
  353. package/dist/features/media/file-import.js +2 -2
  354. package/dist/features/media/file-import.js.map +1 -1
  355. package/dist/features/media/index.d.ts +2 -2
  356. package/dist/features/media/index.js +2 -2
  357. package/dist/features/media/react/FileView.js +19 -2
  358. package/dist/features/media/react/FileView.js.map +1 -1
  359. package/dist/features/media/schema.d.ts +8 -0
  360. package/dist/features/media/schema.js +126 -11
  361. package/dist/features/media/schema.js.map +1 -1
  362. package/dist/features/media/upload.d.ts +16 -14
  363. package/dist/features/media/upload.js +13 -19
  364. package/dist/features/media/upload.js.map +1 -1
  365. package/dist/features/mentions/index.d.ts +4 -3
  366. package/dist/features/mentions/index.js +4 -1
  367. package/dist/features/mentions/index.js.map +1 -1
  368. package/dist/features/mentions/mention-text.d.ts +1 -1
  369. package/dist/features/mentions/mention-text.js.map +1 -1
  370. package/dist/features/mentions/mention.d.ts +0 -6
  371. package/dist/features/mentions/mention.js.map +1 -1
  372. package/dist/features/mentions/metadata-defaults.d.ts +2 -2
  373. package/dist/features/mentions/metadata-defaults.js +3 -9
  374. package/dist/features/mentions/metadata-defaults.js.map +1 -1
  375. package/dist/features/mentions/schema.d.ts +2 -1
  376. package/dist/features/mentions/schema.js.map +1 -1
  377. package/dist/features/tables/feature.d.ts +2 -1
  378. package/dist/features/tables/feature.js +90 -70
  379. package/dist/features/tables/feature.js.map +1 -1
  380. package/dist/features/themes/components/SettingsAppearanceTab.js +6 -9
  381. package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
  382. package/dist/features/themes/feature.js +2 -0
  383. package/dist/features/themes/feature.js.map +1 -1
  384. package/dist/features/themes/index.d.ts +2 -1
  385. package/dist/features/themes/react/SettingsAppearanceTab.js +7 -10
  386. package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
  387. package/dist/features/themes/schema.d.ts +2 -6
  388. package/dist/features/themes/schema.js.map +1 -1
  389. package/dist/features/themes/theme-runtime.d.ts +1 -1
  390. package/dist/features/themes/theme-runtime.js +4 -2
  391. package/dist/features/themes/theme-runtime.js.map +1 -1
  392. package/dist/features/transcript/TranscriptSegmentView.js +48 -28
  393. package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
  394. package/dist/features/transcript/feature.js +21 -19
  395. package/dist/features/transcript/feature.js.map +1 -1
  396. package/dist/features/transcript/react/TranscriptSegmentView.js +55 -36
  397. package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
  398. package/dist/features/transcript/segment-node.d.ts +2 -0
  399. package/dist/features/transcript/segment-node.js +35 -7
  400. package/dist/features/transcript/segment-node.js.map +1 -1
  401. package/dist/i18n/de/composites.js +91 -1
  402. package/dist/i18n/de/composites.js.map +1 -1
  403. package/dist/i18n/de/integrations.js +49 -8
  404. package/dist/i18n/de/integrations.js.map +1 -1
  405. package/dist/i18n/de/media.js +0 -1
  406. package/dist/i18n/de/media.js.map +1 -1
  407. package/dist/i18n/de/palette.js +22 -0
  408. package/dist/i18n/de/palette.js.map +1 -0
  409. package/dist/i18n/de/tables.js +0 -1
  410. package/dist/i18n/de/tables.js.map +1 -1
  411. package/dist/i18n/de/transcript.js +2 -1
  412. package/dist/i18n/de/transcript.js.map +1 -1
  413. package/dist/i18n/de/tree.js +3 -0
  414. package/dist/i18n/de/tree.js.map +1 -1
  415. package/dist/i18n/de.d.ts +157 -11
  416. package/dist/i18n/de.js +3 -1
  417. package/dist/i18n/de.js.map +1 -1
  418. package/dist/i18n/en/composites.js +89 -1
  419. package/dist/i18n/en/composites.js.map +1 -1
  420. package/dist/i18n/en/integrations.js +49 -8
  421. package/dist/i18n/en/integrations.js.map +1 -1
  422. package/dist/i18n/en/media.js +0 -1
  423. package/dist/i18n/en/media.js.map +1 -1
  424. package/dist/i18n/en/palette.js +22 -0
  425. package/dist/i18n/en/palette.js.map +1 -0
  426. package/dist/i18n/en/tables.js +0 -1
  427. package/dist/i18n/en/tables.js.map +1 -1
  428. package/dist/i18n/en/transcript.js +2 -1
  429. package/dist/i18n/en/transcript.js.map +1 -1
  430. package/dist/i18n/en/tree.js +3 -0
  431. package/dist/i18n/en/tree.js.map +1 -1
  432. package/dist/i18n/en.js +3 -1
  433. package/dist/i18n/en.js.map +1 -1
  434. package/dist/index.d.ts +4 -3
  435. package/dist/integrations/composite-ai/bibliography-fetch.d.ts +31 -0
  436. package/dist/integrations/composite-ai/bibliography-fetch.js +159 -0
  437. package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -0
  438. package/dist/integrations/composite-ai/composite-fill.d.ts +30 -8
  439. package/dist/integrations/composite-ai/composite-fill.js +97 -56
  440. package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
  441. package/dist/integrations/composite-ai/composite-summary.d.ts +4 -12
  442. package/dist/integrations/composite-ai/composite-summary.js +7 -26
  443. package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
  444. package/dist/integrations/composite-ai/derive.d.ts +29 -0
  445. package/dist/integrations/composite-ai/derive.js +173 -0
  446. package/dist/integrations/composite-ai/derive.js.map +1 -0
  447. package/dist/integrations/composite-ai/index.d.ts +11 -7
  448. package/dist/integrations/composite-ai/index.js +72 -14
  449. package/dist/integrations/composite-ai/index.js.map +1 -1
  450. package/dist/integrations/composite-ai/rendition.d.ts +45 -0
  451. package/dist/integrations/composite-ai/rendition.js +314 -0
  452. package/dist/integrations/composite-ai/rendition.js.map +1 -0
  453. package/dist/integrations/composite-media/index.d.ts +7 -5
  454. package/dist/integrations/composite-media/index.js +86 -13
  455. package/dist/integrations/composite-media/index.js.map +1 -1
  456. package/dist/integrations/transcript-ai/index.d.ts +1 -1
  457. package/dist/integrations/transcript-ai/index.js +5 -2
  458. package/dist/integrations/transcript-ai/index.js.map +1 -1
  459. package/dist/integrations/transcript-ai/protocol.d.ts +2 -12
  460. package/dist/integrations/transcript-ai/protocol.js +0 -2
  461. package/dist/integrations/transcript-ai/protocol.js.map +1 -1
  462. package/dist/integrations/transcript-ai/transcript-cleanup.d.ts +5 -5
  463. package/dist/integrations/transcript-ai/transcript-cleanup.js +7 -10
  464. package/dist/integrations/transcript-ai/transcript-cleanup.js.map +1 -1
  465. package/dist/markdown/MarkdownSurface.vue.d.ts +6 -5
  466. package/dist/markdown/MarkdownSurface2.js +3 -6
  467. package/dist/markdown/MarkdownSurface2.js.map +1 -1
  468. package/dist/markdown/document.js +1 -1
  469. package/dist/markdown/source-mode.d.ts +1 -1
  470. package/dist/nuxt/module.d.ts +1402 -54
  471. package/dist/nuxt/module.js +6 -49
  472. package/dist/nuxt/module.js.map +1 -1
  473. package/dist/nuxt/runtime/install-context.d.ts +7 -3
  474. package/dist/nuxt/runtime/install-context.js +14 -8
  475. package/dist/nuxt/runtime/install-context.js.map +1 -1
  476. package/dist/nuxt/runtime/plugins/editor-context-nuxt-ui.client.ts +2 -2
  477. package/dist/nuxt/runtime/server/plugins/editor-server.js +20 -12
  478. package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
  479. package/dist/presets/compose.d.ts +94 -0
  480. package/dist/presets/compose.js +164 -0
  481. package/dist/presets/compose.js.map +1 -0
  482. package/dist/react/AdvancedEditor.d.ts +15 -15
  483. package/dist/react/AdvancedEditor.js +15 -13
  484. package/dist/react/AdvancedEditor.js.map +1 -1
  485. package/dist/react/EditorCommandPalette.d.ts +13 -0
  486. package/dist/react/EditorCommandPalette.js +118 -0
  487. package/dist/react/EditorCommandPalette.js.map +1 -0
  488. package/dist/react/EditorSettingsModal.d.ts +5 -5
  489. package/dist/react/EditorSettingsModal.js +7 -4
  490. package/dist/react/EditorSettingsModal.js.map +1 -1
  491. package/dist/react/EditorSettingsPanel.d.ts +7 -7
  492. package/dist/react/EditorSettingsPanel.js +8 -5
  493. package/dist/react/EditorSettingsPanel.js.map +1 -1
  494. package/dist/react/EditorSurface.d.ts +7 -7
  495. package/dist/react/EditorSurface.js +10 -6
  496. package/dist/react/EditorSurface.js.map +1 -1
  497. package/dist/react/EditorToolbar.d.ts +4 -6
  498. package/dist/react/EditorToolbar.js +29 -37
  499. package/dist/react/EditorToolbar.js.map +1 -1
  500. package/dist/react/SimpleEditor.d.ts +22 -23
  501. package/dist/react/SimpleEditor.js +21 -7
  502. package/dist/react/SimpleEditor.js.map +1 -1
  503. package/dist/react/advanced.d.ts +4 -3
  504. package/dist/react/advanced.js +2 -2
  505. package/dist/react/compose.d.ts +20 -0
  506. package/dist/react/compose.js +49 -0
  507. package/dist/react/compose.js.map +1 -0
  508. package/dist/react/composed-surface.d.ts +39 -0
  509. package/dist/react/composed-surface.js +71 -0
  510. package/dist/react/composed-surface.js.map +1 -0
  511. package/dist/react/context.js +6 -2
  512. package/dist/react/context.js.map +1 -1
  513. package/dist/react/index.d.ts +2 -1
  514. package/dist/react/index.js +2 -1
  515. package/dist/react/key-labels.js +15 -0
  516. package/dist/react/key-labels.js.map +1 -0
  517. package/dist/react/settings.d.ts +1 -1
  518. package/dist/react/simple.d.ts +2 -2
  519. package/dist/react/ui/command.js +36 -1
  520. package/dist/react/ui/command.js.map +1 -1
  521. package/dist/schema/advanced.d.ts +6 -6
  522. package/dist/schema/advanced.js +26 -29
  523. package/dist/schema/advanced.js.map +1 -1
  524. package/dist/server/ai.d.ts +5 -16
  525. package/dist/server/ai.js.map +1 -1
  526. package/dist/server/auth.d.ts +9 -0
  527. package/dist/server/collab-state.d.ts +1 -1
  528. package/dist/server/collab-state.js.map +1 -1
  529. package/dist/server/collab.d.ts +13 -13
  530. package/dist/server/collab.js +34 -18
  531. package/dist/server/collab.js.map +1 -1
  532. package/dist/server/crossref.js +78 -0
  533. package/dist/server/crossref.js.map +1 -0
  534. package/dist/server/errors.d.ts +1 -1
  535. package/dist/server/errors.js +1 -1
  536. package/dist/server/errors.js.map +1 -1
  537. package/dist/server/extract.js +308 -0
  538. package/dist/server/extract.js.map +1 -0
  539. package/dist/{nuxt/runtime/server/utils/editor-route.js → server/h3.js} +8 -16
  540. package/dist/server/h3.js.map +1 -0
  541. package/dist/server/handlers/ai.d.ts +30 -9
  542. package/dist/server/handlers/ai.js +170 -36
  543. package/dist/server/handlers/ai.js.map +1 -1
  544. package/dist/server/handlers/context.d.ts +12 -5
  545. package/dist/server/handlers/context.js +10 -4
  546. package/dist/server/handlers/context.js.map +1 -1
  547. package/dist/server/handlers/editor.d.ts +19 -13
  548. package/dist/server/handlers/editor.js +49 -38
  549. package/dist/server/handlers/editor.js.map +1 -1
  550. package/dist/server/handlers/index.d.ts +4 -4
  551. package/dist/server/handlers/index.js +4 -4
  552. package/dist/server/host.d.ts +16 -25
  553. package/dist/server/host.js +58 -41
  554. package/dist/server/host.js.map +1 -1
  555. package/dist/server/index.d.ts +6 -4
  556. package/dist/server/index.js +3 -2
  557. package/dist/server/pdf.js +97 -0
  558. package/dist/server/pdf.js.map +1 -0
  559. package/dist/server/{adapter.d.ts → ports.d.ts} +81 -30
  560. package/dist/server/ports.js +29 -0
  561. package/dist/server/ports.js.map +1 -0
  562. package/dist/server/routes.js +144 -0
  563. package/dist/server/routes.js.map +1 -0
  564. package/dist/settings/index.d.ts +1 -1
  565. package/dist/settings/index.js.map +1 -1
  566. package/dist/styles/composites.css +25 -0
  567. package/dist/tree/DocumentTree.js +56 -29
  568. package/dist/tree/DocumentTree.js.map +1 -1
  569. package/dist/tree/react/DocumentTree.js +23 -0
  570. package/dist/tree/react/DocumentTree.js.map +1 -1
  571. package/dist/tree/tree-host.js +2 -2
  572. package/dist/tree/tree-items.js +1 -1
  573. package/dist/vue/AdvancedEditor.js +30 -9
  574. package/dist/vue/AdvancedEditor.js.map +1 -1
  575. package/dist/vue/AdvancedEditor.vue.d.ts +28 -8
  576. package/dist/vue/SimpleEditor.js +62 -16
  577. package/dist/vue/SimpleEditor.js.map +1 -1
  578. package/dist/vue/SimpleEditor.vue.d.ts +37 -7
  579. package/dist/vue/advanced.d.ts +3 -2
  580. package/dist/vue/advanced.js +2 -2
  581. package/dist/vue/components/EditorCommandPalette.js +91 -0
  582. package/dist/vue/components/EditorCommandPalette.js.map +1 -0
  583. package/dist/vue/components/EditorCommandPalette.vue.d.ts +15 -0
  584. package/dist/vue/components/EditorSettingsModal.js +10 -6
  585. package/dist/vue/components/EditorSettingsModal.js.map +1 -1
  586. package/dist/vue/components/EditorSettingsModal.vue.d.ts +4 -4
  587. package/dist/vue/components/EditorSettingsPanel.js +10 -6
  588. package/dist/vue/components/EditorSettingsPanel.js.map +1 -1
  589. package/dist/vue/components/EditorSettingsPanel.vue.d.ts +6 -6
  590. package/dist/vue/components/EditorSurface.js +11 -6
  591. package/dist/vue/components/EditorSurface.js.map +1 -1
  592. package/dist/vue/components/EditorSurface.vue.d.ts +7 -7
  593. package/dist/vue/components/EditorToolbar.js +41 -49
  594. package/dist/vue/components/EditorToolbar.js.map +1 -1
  595. package/dist/vue/components/EditorToolbar.vue.d.ts +2 -4
  596. package/dist/vue/compose.d.ts +17 -0
  597. package/dist/vue/compose.js +51 -0
  598. package/dist/vue/compose.js.map +1 -0
  599. package/dist/vue/context-key.d.ts +6 -0
  600. package/dist/vue/context-key.js +6 -0
  601. package/dist/vue/context-key.js.map +1 -0
  602. package/dist/vue/context.d.ts +8 -6
  603. package/dist/vue/context.js +6 -5
  604. package/dist/vue/context.js.map +1 -1
  605. package/dist/vue/index.d.ts +10 -8
  606. package/dist/vue/index.js +10 -8
  607. package/dist/vue/kit/index.d.ts +2 -2
  608. package/dist/vue/kit/nuxt-ui.js +23 -0
  609. package/dist/vue/kit/nuxt-ui.js.map +1 -1
  610. package/dist/vue/kit/types.d.ts +40 -1
  611. package/package.json +30 -16
  612. package/dist/adapters/comments/client.d.ts +0 -64
  613. package/dist/adapters/comments/client.js.map +0 -1
  614. package/dist/adapters/routes.js.map +0 -1
  615. package/dist/core/toolbar.d.ts +0 -74
  616. package/dist/core/toolbar.js +0 -45
  617. package/dist/core/toolbar.js.map +0 -1
  618. package/dist/integrations/composite-ai/document-transcribe.d.ts +0 -14
  619. package/dist/integrations/composite-ai/document-transcribe.js +0 -89
  620. package/dist/integrations/composite-ai/document-transcribe.js.map +0 -1
  621. package/dist/nuxt/runtime/server/api/ai/chat.post.d.ts +0 -5
  622. package/dist/nuxt/runtime/server/api/ai/chat.post.js +0 -8
  623. package/dist/nuxt/runtime/server/api/ai/chat.post.js.map +0 -1
  624. package/dist/nuxt/runtime/server/api/ai/complete.post.d.ts +0 -5
  625. package/dist/nuxt/runtime/server/api/ai/complete.post.js +0 -8
  626. package/dist/nuxt/runtime/server/api/ai/complete.post.js.map +0 -1
  627. package/dist/nuxt/runtime/server/api/ai/generate.post.d.ts +0 -5
  628. package/dist/nuxt/runtime/server/api/ai/generate.post.js +0 -8
  629. package/dist/nuxt/runtime/server/api/ai/generate.post.js.map +0 -1
  630. package/dist/nuxt/runtime/server/api/ai/models.get.d.ts +0 -5
  631. package/dist/nuxt/runtime/server/api/ai/models.get.js +0 -8
  632. package/dist/nuxt/runtime/server/api/ai/models.get.js.map +0 -1
  633. package/dist/nuxt/runtime/server/api/ai/transcribe.post.d.ts +0 -5
  634. package/dist/nuxt/runtime/server/api/ai/transcribe.post.js +0 -8
  635. package/dist/nuxt/runtime/server/api/ai/transcribe.post.js.map +0 -1
  636. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.d.ts +0 -5
  637. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js +0 -8
  638. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js.map +0 -1
  639. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.d.ts +0 -5
  640. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js +0 -8
  641. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js.map +0 -1
  642. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.d.ts +0 -5
  643. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js +0 -8
  644. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js.map +0 -1
  645. package/dist/nuxt/runtime/server/api/editor/settings.get.d.ts +0 -5
  646. package/dist/nuxt/runtime/server/api/editor/settings.get.js +0 -8
  647. package/dist/nuxt/runtime/server/api/editor/settings.get.js.map +0 -1
  648. package/dist/nuxt/runtime/server/api/editor/settings.put.d.ts +0 -5
  649. package/dist/nuxt/runtime/server/api/editor/settings.put.js +0 -8
  650. package/dist/nuxt/runtime/server/api/editor/settings.put.js.map +0 -1
  651. package/dist/nuxt/runtime/server/api/editor/tags.get.d.ts +0 -5
  652. package/dist/nuxt/runtime/server/api/editor/tags.get.js +0 -8
  653. package/dist/nuxt/runtime/server/api/editor/tags.get.js.map +0 -1
  654. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.d.ts +0 -5
  655. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js +0 -8
  656. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js.map +0 -1
  657. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.d.ts +0 -5
  658. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js +0 -8
  659. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js.map +0 -1
  660. package/dist/nuxt/runtime/server/api/editor/themes/index.get.d.ts +0 -5
  661. package/dist/nuxt/runtime/server/api/editor/themes/index.get.js +0 -8
  662. package/dist/nuxt/runtime/server/api/editor/themes/index.get.js.map +0 -1
  663. package/dist/nuxt/runtime/server/api/editor/themes/index.post.d.ts +0 -5
  664. package/dist/nuxt/runtime/server/api/editor/themes/index.post.js +0 -8
  665. package/dist/nuxt/runtime/server/api/editor/themes/index.post.js.map +0 -1
  666. package/dist/nuxt/runtime/server/api/editor/users.get.d.ts +0 -5
  667. package/dist/nuxt/runtime/server/api/editor/users.get.js +0 -8
  668. package/dist/nuxt/runtime/server/api/editor/users.get.js.map +0 -1
  669. package/dist/nuxt/runtime/server/plugins/collab.d.ts +0 -5
  670. package/dist/nuxt/runtime/server/plugins/collab.js +0 -10
  671. package/dist/nuxt/runtime/server/plugins/collab.js.map +0 -1
  672. package/dist/nuxt/runtime/server/routes/collab/[id].d.ts +0 -5
  673. package/dist/nuxt/runtime/server/routes/collab/[id].js +0 -35
  674. package/dist/nuxt/runtime/server/routes/collab/[id].js.map +0 -1
  675. package/dist/nuxt/runtime/server/utils/editor-route.d.ts +0 -11
  676. package/dist/nuxt/runtime/server/utils/editor-route.js.map +0 -1
  677. package/dist/presets/advanced.d.ts +0 -33
  678. package/dist/presets/advanced.js +0 -50
  679. package/dist/presets/advanced.js.map +0 -1
  680. package/dist/presets/simple.d.ts +0 -9
  681. package/dist/presets/simple.js +0 -12
  682. package/dist/presets/simple.js.map +0 -1
  683. package/dist/react/advanced-preset.d.ts +0 -11
  684. package/dist/react/advanced-preset.js +0 -31
  685. package/dist/react/advanced-preset.js.map +0 -1
  686. package/dist/vue/advanced-preset.d.ts +0 -13
  687. package/dist/vue/advanced-preset.js +0 -33
  688. package/dist/vue/advanced-preset.js.map +0 -1
  689. package/docs/extending.md +0 -538
  690. package/docs/integration.md +0 -632
  691. package/docs/llms.md +0 -383
  692. package/docs/usage.md +0 -1076
@@ -36,12 +36,12 @@ function FormField({ label, description, required, small, horizontal, children }
36
36
  /** „Darstellung“: the Obsidian theme of the whole app (`appearance.theme`); „Standard“ is the normal look. */
37
37
  function SettingsAppearanceTab() {
38
38
  const { settings, update } = useEditorSettings();
39
- const { fetch, notify } = useEditorContext();
39
+ const { backend, notify } = useEditorContext();
40
40
  const { t } = useEditorI18n();
41
41
  const [themes, setThemes] = useState([]);
42
42
  const loadThemes = useCallback(async () => {
43
- setThemes(await fetch("/api/editor/themes"));
44
- }, [fetch]);
43
+ setThemes(await backend.themes?.list() ?? []);
44
+ }, [backend]);
45
45
  useEffect(() => {
46
46
  loadThemes().catch((error) => {
47
47
  console.warn("tiptap-editor: Die Themes konnten nicht geladen werden", error);
@@ -57,14 +57,11 @@ function SettingsAppearanceTab() {
57
57
  const [inputKey, setInputKey] = useState(0);
58
58
  async function upload() {
59
59
  if (!cssFile || uploading) return;
60
- const body = new FormData();
61
- body.append("css", cssFile);
62
- if (manifestFile) body.append("manifest", manifestFile);
63
60
  setUploading(true);
64
61
  try {
65
- const record = await fetch("/api/editor/themes", {
66
- method: "POST",
67
- body
62
+ const record = await backend.themes.store({
63
+ css: cssFile,
64
+ ...manifestFile && { manifest: manifestFile }
68
65
  });
69
66
  setCssFile(null);
70
67
  setManifestFile(null);
@@ -94,7 +91,7 @@ function SettingsAppearanceTab() {
94
91
  if (removing) return;
95
92
  setRemoving(record.id);
96
93
  try {
97
- await fetch(`/api/editor/themes/${encodeURIComponent(record.id)}`, { method: "DELETE" });
94
+ await backend.themes.remove(record.id);
98
95
  if (settings.appearance.theme === record.id) setTheme(null);
99
96
  notify({
100
97
  title: t("themes.delete.done", { name: record.name }),
@@ -1 +1 @@
1
- {"version":3,"file":"SettingsAppearanceTab.js","names":[],"sources":["../../../../src/features/themes/react/SettingsAppearanceTab.tsx"],"sourcesContent":["import { type ReactNode, useCallback, useEffect, useState } from 'react'\nimport { fetchErrorMessage } from '../../../core/fetch-error'\nimport { useEditorContext } from '../../../react/context'\nimport { Icon } from '../../../react/Icon'\nimport { useEditorSettings } from '../../../react/settings'\nimport { SettingsSection } from '../../../react/SettingsSection'\nimport { Badge } from '../../../react/ui/badge'\nimport { Button } from '../../../react/ui/button'\nimport { Input } from '../../../react/ui/input'\nimport { Label } from '../../../react/ui/label'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../../react/ui/select'\nimport { useEditorI18n } from '../../../react/use-editor-i18n'\nimport type { ThemeRecord } from '../schema'\n\n/** Radix Select has no `null`/empty value: „Standard“ travels as this sentinel (no theme id can contain underscores). */\nconst STANDARD = '__standard__'\n\n/** A label (+ description) next to or above its control; the counterpart of Nuxt UI's `UFormField`. */\nfunction FormField({ label, description, required, small, horizontal, children }: {\n label: string\n description?: string\n required?: boolean\n small?: boolean\n horizontal?: boolean\n children: ReactNode\n}) {\n return (\n <div className={horizontal ? 'flex items-start justify-between gap-4 py-3' : 'flex flex-col gap-1.5'}>\n <div className={horizontal ? 'min-w-0 flex-1' : undefined}>\n <Label className={small ? 'text-xs font-medium text-foreground' : 'text-sm font-medium text-foreground'}>\n {label}\n {required && <span className=\"ms-0.5 text-destructive\">*</span>}\n </Label>\n {description && <p className=\"text-sm text-muted-foreground\">{description}</p>}\n </div>\n {children}\n </div>\n )\n}\n\n/** „Darstellung“: the Obsidian theme of the whole app (`appearance.theme`); „Standard“ is the normal look. */\nexport function SettingsAppearanceTab() {\n const { settings, update } = useEditorSettings()\n const { fetch, notify } = useEditorContext()\n const { t } = useEditorI18n()\n\n const [themes, setThemes] = useState<ThemeRecord[]>([])\n\n const loadThemes = useCallback(async () => {\n setThemes(await fetch<ThemeRecord[]>('/api/editor/themes'))\n }, [fetch])\n\n useEffect(() => {\n loadThemes().catch((error: unknown) => {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n })\n }, [loadThemes])\n\n const setTheme = (value: string | null) => update((draft) => { draft.appearance.theme = value })\n\n const [cssFile, setCssFile] = useState<File | null>(null)\n const [manifestFile, setManifestFile] = useState<File | null>(null)\n const [uploading, setUploading] = useState(false)\n /** Bumped after an upload to remount the file inputs (a file input cannot be cleared through its value). */\n const [inputKey, setInputKey] = useState(0)\n\n async function upload() {\n if (!cssFile || uploading) return\n const body = new FormData()\n body.append('css', cssFile)\n if (manifestFile) body.append('manifest', manifestFile)\n setUploading(true)\n try {\n const record = await fetch<ThemeRecord>('/api/editor/themes', { method: 'POST', body })\n setCssFile(null)\n setManifestFile(null)\n setInputKey(key => key + 1)\n setTheme(record.id)\n notify({ title: t('themes.upload.done', { name: record.name }), kind: 'success' })\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n }\n catch (error) {\n notify({ title: t('themes.upload.failed'), description: fetchErrorMessage(error), kind: 'error' })\n }\n finally {\n setUploading(false)\n }\n }\n\n const [removing, setRemoving] = useState<string | null>(null)\n\n async function remove(record: ThemeRecord) {\n if (removing) return\n setRemoving(record.id)\n try {\n await fetch(`/api/editor/themes/${encodeURIComponent(record.id)}`, { method: 'DELETE' })\n if (settings.appearance.theme === record.id) setTheme(null)\n notify({ title: t('themes.delete.done', { name: record.name }), kind: 'success' })\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n }\n catch (error) {\n notify({ title: t('themes.delete.failed'), description: fetchErrorMessage(error), kind: 'error' })\n }\n finally {\n setRemoving(null)\n }\n }\n\n return (\n <div className=\"flex flex-col gap-8\">\n <SettingsSection title={t('themes.title')}>\n <FormField\n horizontal\n label={t('themes.select.label')}\n description={t('themes.select.description')}\n >\n <Select value={settings.appearance.theme ?? STANDARD} onValueChange={value => setTheme(value === STANDARD ? null : value)}>\n <SelectTrigger className=\"w-64\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={STANDARD}>{t('themes.standard')}</SelectItem>\n {themes.map(({ id, name, author }) => (\n <SelectItem key={id} value={id}>{author ? `${name} – ${author}` : name}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n </FormField>\n\n <FormField\n horizontal\n label={t('themes.upload.label')}\n description={t('themes.upload.description')}\n >\n <form\n className=\"flex w-64 flex-col gap-2\"\n onSubmit={(event) => {\n event.preventDefault()\n void upload()\n }}\n >\n <FormField label={t('themes.upload.css')} required small>\n <Input\n key={`css-${inputKey}`}\n type=\"file\"\n accept=\".css,text/css\"\n className=\"w-full\"\n disabled={uploading}\n onChange={event => setCssFile(event.target.files?.[0] ?? null)}\n />\n </FormField>\n <FormField label={t('themes.upload.manifest')} small>\n <Input\n key={`manifest-${inputKey}`}\n type=\"file\"\n accept=\".json,application/json\"\n className=\"w-full\"\n disabled={uploading}\n onChange={event => setManifestFile(event.target.files?.[0] ?? null)}\n />\n </FormField>\n <Button type=\"submit\" className=\"self-start\" disabled={!cssFile || uploading}>\n <Icon name={uploading ? 'i-lucide-loader-circle' : 'i-lucide-upload'} className={uploading ? 'animate-spin' : undefined} />\n {t('themes.upload.submit')}\n </Button>\n </form>\n </FormField>\n </SettingsSection>\n\n {themes.length > 0 && (\n <SettingsSection title={t('themes.library')}>\n {themes.map(record => (\n <div key={record.id} className=\"flex items-center gap-3 py-2\">\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <span className=\"truncate text-sm font-medium text-foreground\">{record.name}</span>\n {(record.version || record.author) && (\n <span className=\"truncate text-xs text-muted-foreground\">\n {[record.version && t('themes.version', { version: record.version }), record.author && t('themes.byAuthor', { author: record.author })].filter(Boolean).join(' · ')}\n </span>\n )}\n </div>\n {record.builtin\n ? <Badge variant=\"secondary\">{t('themes.builtin')}</Badge>\n : (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"text-destructive\"\n aria-label={t('themes.delete.label', { name: record.name })}\n disabled={removing !== null}\n onClick={() => void remove(record)}\n >\n <Icon name={removing === record.id ? 'i-lucide-loader-circle' : 'i-lucide-trash-2'} className={removing === record.id ? 'animate-spin' : undefined} />\n </Button>\n )}\n </div>\n ))}\n </SettingsSection>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SettingsAppearanceTab.js","names":[],"sources":["../../../../src/features/themes/react/SettingsAppearanceTab.tsx"],"sourcesContent":["import { type ReactNode, useCallback, useEffect, useState } from 'react'\nimport { fetchErrorMessage } from '../../../core/fetch-error'\nimport { useEditorContext } from '../../../react/context'\nimport { Icon } from '../../../react/Icon'\nimport { useEditorSettings } from '../../../react/settings'\nimport { SettingsSection } from '../../../react/SettingsSection'\nimport { Badge } from '../../../react/ui/badge'\nimport { Button } from '../../../react/ui/button'\nimport { Input } from '../../../react/ui/input'\nimport { Label } from '../../../react/ui/label'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../../react/ui/select'\nimport { useEditorI18n } from '../../../react/use-editor-i18n'\nimport type { ThemeRecord } from '../schema'\n\n/** Radix Select has no `null`/empty value: „Standard“ travels as this sentinel (no theme id can contain underscores). */\nconst STANDARD = '__standard__'\n\n/** A label (+ description) next to or above its control; the counterpart of Nuxt UI's `UFormField`. */\nfunction FormField({ label, description, required, small, horizontal, children }: {\n label: string\n description?: string\n required?: boolean\n small?: boolean\n horizontal?: boolean\n children: ReactNode\n}) {\n return (\n <div className={horizontal ? 'flex items-start justify-between gap-4 py-3' : 'flex flex-col gap-1.5'}>\n <div className={horizontal ? 'min-w-0 flex-1' : undefined}>\n <Label className={small ? 'text-xs font-medium text-foreground' : 'text-sm font-medium text-foreground'}>\n {label}\n {required && <span className=\"ms-0.5 text-destructive\">*</span>}\n </Label>\n {description && <p className=\"text-sm text-muted-foreground\">{description}</p>}\n </div>\n {children}\n </div>\n )\n}\n\n/** „Darstellung“: the Obsidian theme of the whole app (`appearance.theme`); „Standard“ is the normal look. */\nexport function SettingsAppearanceTab() {\n const { settings, update } = useEditorSettings()\n const { backend, notify } = useEditorContext()\n const { t } = useEditorI18n()\n\n const [themes, setThemes] = useState<ThemeRecord[]>([])\n\n const loadThemes = useCallback(async () => {\n setThemes(await backend.themes?.list() ?? [])\n }, [backend])\n\n useEffect(() => {\n loadThemes().catch((error: unknown) => {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n })\n }, [loadThemes])\n\n const setTheme = (value: string | null) => update((draft) => { draft.appearance.theme = value })\n\n const [cssFile, setCssFile] = useState<File | null>(null)\n const [manifestFile, setManifestFile] = useState<File | null>(null)\n const [uploading, setUploading] = useState(false)\n /** Bumped after an upload to remount the file inputs (a file input cannot be cleared through its value). */\n const [inputKey, setInputKey] = useState(0)\n\n async function upload() {\n if (!cssFile || uploading) return\n setUploading(true)\n try {\n // The themes feature needs the `themes` capability, so the tab is only mounted where the backend has it.\n const record = await backend.themes!.store({ css: cssFile, ...(manifestFile && { manifest: manifestFile }) })\n setCssFile(null)\n setManifestFile(null)\n setInputKey(key => key + 1)\n setTheme(record.id)\n notify({ title: t('themes.upload.done', { name: record.name }), kind: 'success' })\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n }\n catch (error) {\n notify({ title: t('themes.upload.failed'), description: fetchErrorMessage(error), kind: 'error' })\n }\n finally {\n setUploading(false)\n }\n }\n\n const [removing, setRemoving] = useState<string | null>(null)\n\n async function remove(record: ThemeRecord) {\n if (removing) return\n setRemoving(record.id)\n try {\n await backend.themes!.remove(record.id)\n if (settings.appearance.theme === record.id) setTheme(null)\n notify({ title: t('themes.delete.done', { name: record.name }), kind: 'success' })\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n }\n catch (error) {\n notify({ title: t('themes.delete.failed'), description: fetchErrorMessage(error), kind: 'error' })\n }\n finally {\n setRemoving(null)\n }\n }\n\n return (\n <div className=\"flex flex-col gap-8\">\n <SettingsSection title={t('themes.title')}>\n <FormField\n horizontal\n label={t('themes.select.label')}\n description={t('themes.select.description')}\n >\n <Select value={settings.appearance.theme ?? STANDARD} onValueChange={value => setTheme(value === STANDARD ? null : value)}>\n <SelectTrigger className=\"w-64\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={STANDARD}>{t('themes.standard')}</SelectItem>\n {themes.map(({ id, name, author }) => (\n <SelectItem key={id} value={id}>{author ? `${name} – ${author}` : name}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n </FormField>\n\n <FormField\n horizontal\n label={t('themes.upload.label')}\n description={t('themes.upload.description')}\n >\n <form\n className=\"flex w-64 flex-col gap-2\"\n onSubmit={(event) => {\n event.preventDefault()\n void upload()\n }}\n >\n <FormField label={t('themes.upload.css')} required small>\n <Input\n key={`css-${inputKey}`}\n type=\"file\"\n accept=\".css,text/css\"\n className=\"w-full\"\n disabled={uploading}\n onChange={event => setCssFile(event.target.files?.[0] ?? null)}\n />\n </FormField>\n <FormField label={t('themes.upload.manifest')} small>\n <Input\n key={`manifest-${inputKey}`}\n type=\"file\"\n accept=\".json,application/json\"\n className=\"w-full\"\n disabled={uploading}\n onChange={event => setManifestFile(event.target.files?.[0] ?? null)}\n />\n </FormField>\n <Button type=\"submit\" className=\"self-start\" disabled={!cssFile || uploading}>\n <Icon name={uploading ? 'i-lucide-loader-circle' : 'i-lucide-upload'} className={uploading ? 'animate-spin' : undefined} />\n {t('themes.upload.submit')}\n </Button>\n </form>\n </FormField>\n </SettingsSection>\n\n {themes.length > 0 && (\n <SettingsSection title={t('themes.library')}>\n {themes.map(record => (\n <div key={record.id} className=\"flex items-center gap-3 py-2\">\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <span className=\"truncate text-sm font-medium text-foreground\">{record.name}</span>\n {(record.version || record.author) && (\n <span className=\"truncate text-xs text-muted-foreground\">\n {[record.version && t('themes.version', { version: record.version }), record.author && t('themes.byAuthor', { author: record.author })].filter(Boolean).join(' · ')}\n </span>\n )}\n </div>\n {record.builtin\n ? <Badge variant=\"secondary\">{t('themes.builtin')}</Badge>\n : (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"text-destructive\"\n aria-label={t('themes.delete.label', { name: record.name })}\n disabled={removing !== null}\n onClick={() => void remove(record)}\n >\n <Icon name={removing === record.id ? 'i-lucide-loader-circle' : 'i-lucide-trash-2'} className={removing === record.id ? 'animate-spin' : undefined} />\n </Button>\n )}\n </div>\n ))}\n </SettingsSection>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { THEME_ID } from "../../settings/index.js";
2
+ import { ThemeRecord } from "../../core/backend-types.js";
2
3
  import { z } from "zod";
3
4
  //#region src/features/themes/schema.d.ts
4
5
  /** Upper bound for a theme stylesheet (bytes). */
@@ -11,11 +12,6 @@ export declare const themeManifestSchema: z.ZodObject<{
11
12
  authorUrl: z.ZodOptional<z.ZodString>;
12
13
  }, z.core.$strip>;
13
14
  export type ThemeManifest = z.infer<typeof themeManifestSchema>;
14
- /** A theme in the shared library; `builtin` themes are seeded by the host and cannot be deleted. */
15
- export interface ThemeRecord extends ThemeManifest {
16
- id: string;
17
- builtin: boolean;
18
- }
19
15
  //#endregion
20
- export { THEME_ID };
16
+ export { THEME_ID, type ThemeRecord };
21
17
  //# sourceMappingURL=schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","names":[],"sources":["../../../src/features/themes/schema.ts"],"sourcesContent":["import { z } from 'zod'\nimport { THEME_ID } from '../../settings'\n\n/** Valid theme id (pattern defined in `settings`, which validates `appearance.theme` with it). */\nexport { THEME_ID }\n\n/** Upper bound for a theme stylesheet (bytes). */\nexport const MAX_THEME_BYTES = 2 * 1024 * 1024\n\n/** The subset of an Obsidian `manifest.json` the editor keeps. */\nexport const themeManifestSchema = z.object({\n name: z.string().min(1).max(120),\n version: z.string().max(40).optional(),\n author: z.string().max(120).optional(),\n authorUrl: z.string().max(400).optional(),\n})\nexport type ThemeManifest = z.infer<typeof themeManifestSchema>\n\n/** A theme in the shared library; `builtin` themes are seeded by the host and cannot be deleted. */\nexport interface ThemeRecord extends ThemeManifest {\n id: string\n builtin: boolean\n}\n"],"mappings":";;;;AAOA,MAAa,kBAAkB;;AAG/B,MAAa,sBAAsB,EAAE,OAAO;CAC1C,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG;CAC/B,SAAS,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,SAAS;CACrC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,SAAS;CACrC,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,SAAS;AAC1C,CAAC"}
1
+ {"version":3,"file":"schema.js","names":[],"sources":["../../../src/features/themes/schema.ts"],"sourcesContent":["import { z } from 'zod'\nimport { THEME_ID } from '../../settings'\n\n/** Valid theme id (pattern defined in `settings`, which validates `appearance.theme` with it). */\nexport { THEME_ID }\n\n/** Upper bound for a theme stylesheet (bytes). */\nexport const MAX_THEME_BYTES = 2 * 1024 * 1024\n\n/** The subset of an Obsidian `manifest.json` the editor keeps. */\nexport const themeManifestSchema = z.object({\n name: z.string().min(1).max(120),\n version: z.string().max(40).optional(),\n author: z.string().max(120).optional(),\n authorUrl: z.string().max(400).optional(),\n})\nexport type ThemeManifest = z.infer<typeof themeManifestSchema>\n\n/** A theme in the shared library (`ThemeManifest` + `id` + `builtin`); defined in core because `EditorBackend` lists them. */\nexport type { ThemeRecord } from '../../core/backend-types'\n"],"mappings":";;;;AAOA,MAAa,kBAAkB;;AAG/B,MAAa,sBAAsB,EAAE,OAAO;CAC1C,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG;CAC/B,SAAS,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,SAAS;CACrC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,SAAS;CACrC,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,SAAS;AAC1C,CAAC"}
@@ -4,7 +4,7 @@ import { FeatureContext } from "../../core/feature.js";
4
4
  export declare const THEME_SCOPE_CLASS = "obsidian-theme";
5
5
  /**
6
6
  * The theme runtime of the app `ctx` belongs to: applies `settings.appearance.theme` (fetches the stylesheet through
7
- * `ctx.fetch`, scopes it, keeps one `<style data-tiptap-theme>` in the document head, puts `obsidian-theme theme-dark|theme-light`
7
+ * `ctx.backend.themes`, scopes it, keeps one `<style data-tiptap-theme>` in the document head, puts `obsidian-theme theme-dark|theme-light`
8
8
  * on `<html>`) and follows the app's colour mode.
9
9
  * Reference-counted per app: the first call starts the runtime, every call returns an idempotent release, the last release
10
10
  * stops it (removing the style element and the root classes). Surfaces of one app share a single runtime.
@@ -51,7 +51,9 @@ function createRuntime(ctx) {
51
51
  async function load(id) {
52
52
  const cached = cache.get(id);
53
53
  if (cached !== void 0) return cached;
54
- const css = await ctx.fetch(`/api/editor/themes/${encodeURIComponent(id)}`, { responseType: "text" });
54
+ const themes = ctx.backend.themes;
55
+ if (!themes) throw new Error(`@ecsplico/editor: backend "${ctx.backend.name}" has no themes capability`);
56
+ const css = await themes.css(id);
55
57
  const scoped = scopeThemeCss(css, `.${THEME_SCOPE_CLASS}`);
56
58
  cache.set(id, scoped);
57
59
  return scoped;
@@ -115,7 +117,7 @@ function createRuntime(ctx) {
115
117
  }
116
118
  /**
117
119
  * The theme runtime of the app `ctx` belongs to: applies `settings.appearance.theme` (fetches the stylesheet through
118
- * `ctx.fetch`, scopes it, keeps one `<style data-tiptap-theme>` in the document head, puts `obsidian-theme theme-dark|theme-light`
120
+ * `ctx.backend.themes`, scopes it, keeps one `<style data-tiptap-theme>` in the document head, puts `obsidian-theme theme-dark|theme-light`
119
121
  * on `<html>`) and follows the app's colour mode.
120
122
  * Reference-counted per app: the first call starts the runtime, every call returns an idempotent release, the last release
121
123
  * stops it (removing the style element and the root classes). Surfaces of one app share a single runtime.
@@ -1 +1 @@
1
- {"version":3,"file":"theme-runtime.js","names":[],"sources":["../../../src/features/themes/theme-runtime.ts"],"sourcesContent":["import type { FeatureContext } from '../../core/feature'\nimport { scopeThemeCss } from './scope-css'\n\n/** Class that scopes a theme's rules (`scopeThemeCss`) and the bridge stylesheet; the runtime puts it on `<html>`. */\nexport const THEME_SCOPE_CLASS = 'obsidian-theme'\n\n/** Marks the single `<style>` element in `document.head` that carries the active theme. */\nconst STYLE_ATTRIBUTE = 'data-tiptap-theme'\n\n// One runtime per app (keyed by the app's settings store): every surface of the page shares the style element and the\n// cache. Reference-counted: the last release stops it.\nconst runtimes = new Map<object, { count: number, stop: () => void }>()\n\nconst PREFERS_DARK = '(prefers-color-scheme: dark)'\n\n/** Whether the app is in dark mode: `html.dark` (Nuxt colour mode), `html.light`, else the system preference. */\nfunction readDark(): boolean {\n if (typeof document === 'undefined') return false\n const classes = document.documentElement.classList\n if (classes.contains('dark')) return true\n if (classes.contains('light')) return false\n return typeof matchMedia === 'function' && matchMedia(PREFERS_DARK).matches\n}\n\nfunction createRuntime(ctx: FeatureContext): () => void {\n let dark = readDark()\n /** The theme whose style is in the document; null = standard look. */\n let applied: string | null = null\n const cache = new Map<string, string>()\n let style: HTMLStyleElement | undefined\n let latest = 0\n let teardown: (() => void) | undefined\n let stopTheme: (() => void) | undefined\n\n /** `obsidian-theme theme-dark|theme-light` on `<html>` while a theme is applied; none of the three otherwise (standard look). */\n function applyRootClasses() {\n if (typeof document === 'undefined') return\n const root = document.documentElement.classList\n const on = applied !== null\n root.toggle(THEME_SCOPE_CLASS, on)\n root.toggle('theme-dark', on && dark)\n root.toggle('theme-light', on && !dark)\n }\n\n function removeRootClasses() {\n if (typeof document === 'undefined') return\n document.documentElement.classList.remove(THEME_SCOPE_CLASS, 'theme-dark', 'theme-light')\n }\n\n function removeStyle() {\n style?.remove()\n style = undefined\n }\n\n function showStyle(css: string) {\n if (!style) {\n style = document.createElement('style')\n style.setAttribute(STYLE_ATTRIBUTE, '')\n document.head.appendChild(style)\n }\n style.textContent = css\n }\n\n async function load(id: string): Promise<string> {\n const cached = cache.get(id)\n if (cached !== undefined) return cached\n const css: string = await ctx.fetch(`/api/editor/themes/${encodeURIComponent(id)}`, { responseType: 'text' })\n const scoped = scopeThemeCss(css, `.${THEME_SCOPE_CLASS}`)\n cache.set(id, scoped)\n return scoped\n }\n\n async function select(id: string | null) {\n const ticket = ++latest\n if (id === null) {\n removeStyle()\n applied = null\n applyRootClasses()\n return\n }\n try {\n const css = await load(id)\n if (ticket !== latest) return\n showStyle(css)\n applied = id\n applyRootClasses()\n }\n catch (error) {\n if (ticket !== latest) return\n // The stored setting stays untouched: the theme may come back (host restored it), the session shows the standard look.\n console.warn(`tiptap-editor: Theme „${id}“ konnte nicht geladen werden, die Standard-Darstellung wird verwendet`, error)\n removeStyle()\n applied = null\n applyRootClasses()\n }\n }\n\n if (typeof document !== 'undefined') {\n void select(ctx.settings.get().appearance.theme)\n let lastTheme = ctx.settings.get().appearance.theme\n stopTheme = ctx.settings.subscribe((value) => {\n if (value.appearance.theme === lastTheme) return\n lastTheme = value.appearance.theme\n void select(lastTheme)\n })\n applyRootClasses()\n const onDarkChange = () => {\n const next = readDark()\n if (next === dark) return\n dark = next\n applyRootClasses()\n }\n const observer = new MutationObserver(onDarkChange)\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })\n const media = typeof matchMedia === 'function' ? matchMedia(PREFERS_DARK) : undefined\n media?.addEventListener('change', onDarkChange)\n teardown = () => {\n observer.disconnect()\n media?.removeEventListener('change', onDarkChange)\n removeStyle()\n }\n }\n\n return () => {\n latest++\n stopTheme?.()\n teardown?.()\n removeRootClasses()\n }\n}\n\n/**\n * The theme runtime of the app `ctx` belongs to: applies `settings.appearance.theme` (fetches the stylesheet through\n * `ctx.fetch`, scopes it, keeps one `<style data-tiptap-theme>` in the document head, puts `obsidian-theme theme-dark|theme-light`\n * on `<html>`) and follows the app's colour mode.\n * Reference-counted per app: the first call starts the runtime, every call returns an idempotent release, the last release\n * stops it (removing the style element and the root classes). Surfaces of one app share a single runtime.\n */\nexport function startObsidianTheme(ctx: FeatureContext): () => void {\n const key: object = ctx.settings\n let entry = runtimes.get(key)\n if (!entry) {\n entry = { count: 0, stop: createRuntime(ctx) }\n runtimes.set(key, entry)\n }\n entry.count++\n const held = entry\n let released = false\n return () => {\n if (released) return\n released = true\n if (--held.count > 0) return\n held.stop()\n if (runtimes.get(key) === held) runtimes.delete(key)\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"theme-runtime.js","names":[],"sources":["../../../src/features/themes/theme-runtime.ts"],"sourcesContent":["import type { FeatureContext } from '../../core/feature'\nimport { scopeThemeCss } from './scope-css'\n\n/** Class that scopes a theme's rules (`scopeThemeCss`) and the bridge stylesheet; the runtime puts it on `<html>`. */\nexport const THEME_SCOPE_CLASS = 'obsidian-theme'\n\n/** Marks the single `<style>` element in `document.head` that carries the active theme. */\nconst STYLE_ATTRIBUTE = 'data-tiptap-theme'\n\n// One runtime per app (keyed by the app's settings store): every surface of the page shares the style element and the\n// cache. Reference-counted: the last release stops it.\nconst runtimes = new Map<object, { count: number, stop: () => void }>()\n\nconst PREFERS_DARK = '(prefers-color-scheme: dark)'\n\n/** Whether the app is in dark mode: `html.dark` (Nuxt colour mode), `html.light`, else the system preference. */\nfunction readDark(): boolean {\n if (typeof document === 'undefined') return false\n const classes = document.documentElement.classList\n if (classes.contains('dark')) return true\n if (classes.contains('light')) return false\n return typeof matchMedia === 'function' && matchMedia(PREFERS_DARK).matches\n}\n\nfunction createRuntime(ctx: FeatureContext): () => void {\n let dark = readDark()\n /** The theme whose style is in the document; null = standard look. */\n let applied: string | null = null\n const cache = new Map<string, string>()\n let style: HTMLStyleElement | undefined\n let latest = 0\n let teardown: (() => void) | undefined\n let stopTheme: (() => void) | undefined\n\n /** `obsidian-theme theme-dark|theme-light` on `<html>` while a theme is applied; none of the three otherwise (standard look). */\n function applyRootClasses() {\n if (typeof document === 'undefined') return\n const root = document.documentElement.classList\n const on = applied !== null\n root.toggle(THEME_SCOPE_CLASS, on)\n root.toggle('theme-dark', on && dark)\n root.toggle('theme-light', on && !dark)\n }\n\n function removeRootClasses() {\n if (typeof document === 'undefined') return\n document.documentElement.classList.remove(THEME_SCOPE_CLASS, 'theme-dark', 'theme-light')\n }\n\n function removeStyle() {\n style?.remove()\n style = undefined\n }\n\n function showStyle(css: string) {\n if (!style) {\n style = document.createElement('style')\n style.setAttribute(STYLE_ATTRIBUTE, '')\n document.head.appendChild(style)\n }\n style.textContent = css\n }\n\n async function load(id: string): Promise<string> {\n const cached = cache.get(id)\n if (cached !== undefined) return cached\n const themes = ctx.backend.themes\n if (!themes) throw new Error(`@ecsplico/editor: backend \"${ctx.backend.name}\" has no themes capability`)\n const css = await themes.css(id)\n const scoped = scopeThemeCss(css, `.${THEME_SCOPE_CLASS}`)\n cache.set(id, scoped)\n return scoped\n }\n\n async function select(id: string | null) {\n const ticket = ++latest\n if (id === null) {\n removeStyle()\n applied = null\n applyRootClasses()\n return\n }\n try {\n const css = await load(id)\n if (ticket !== latest) return\n showStyle(css)\n applied = id\n applyRootClasses()\n }\n catch (error) {\n if (ticket !== latest) return\n // The stored setting stays untouched: the theme may come back (host restored it), the session shows the standard look.\n console.warn(`tiptap-editor: Theme „${id}“ konnte nicht geladen werden, die Standard-Darstellung wird verwendet`, error)\n removeStyle()\n applied = null\n applyRootClasses()\n }\n }\n\n if (typeof document !== 'undefined') {\n void select(ctx.settings.get().appearance.theme)\n let lastTheme = ctx.settings.get().appearance.theme\n stopTheme = ctx.settings.subscribe((value) => {\n if (value.appearance.theme === lastTheme) return\n lastTheme = value.appearance.theme\n void select(lastTheme)\n })\n applyRootClasses()\n const onDarkChange = () => {\n const next = readDark()\n if (next === dark) return\n dark = next\n applyRootClasses()\n }\n const observer = new MutationObserver(onDarkChange)\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })\n const media = typeof matchMedia === 'function' ? matchMedia(PREFERS_DARK) : undefined\n media?.addEventListener('change', onDarkChange)\n teardown = () => {\n observer.disconnect()\n media?.removeEventListener('change', onDarkChange)\n removeStyle()\n }\n }\n\n return () => {\n latest++\n stopTheme?.()\n teardown?.()\n removeRootClasses()\n }\n}\n\n/**\n * The theme runtime of the app `ctx` belongs to: applies `settings.appearance.theme` (fetches the stylesheet through\n * `ctx.backend.themes`, scopes it, keeps one `<style data-tiptap-theme>` in the document head, puts `obsidian-theme theme-dark|theme-light`\n * on `<html>`) and follows the app's colour mode.\n * Reference-counted per app: the first call starts the runtime, every call returns an idempotent release, the last release\n * stops it (removing the style element and the root classes). Surfaces of one app share a single runtime.\n */\nexport function startObsidianTheme(ctx: FeatureContext): () => void {\n const key: object = ctx.settings\n let entry = runtimes.get(key)\n if (!entry) {\n entry = { count: 0, stop: createRuntime(ctx) }\n runtimes.set(key, entry)\n }\n entry.count++\n const held = entry\n let released = false\n return () => {\n if (released) return\n released = true\n if (--held.count > 0) return\n held.stop()\n if (runtimes.get(key) === held) runtimes.delete(key)\n }\n}\n"],"mappings":"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"}
@@ -1,8 +1,9 @@
1
1
  import { metadataOf } from "../../core/metadata.js";
2
2
  import { METADATA_LIST } from "../../core/metadata-types.js";
3
+ import { formatSegmentTime } from "./segment-node.js";
3
4
  import { useEditorI18n } from "../../vue/use-editor-i18n.js";
4
5
  import _sfc_main$1 from "../../vue/components/SuggestionList.js";
5
- import { computed, createBlock, createCommentVNode, createElementVNode, createVNode, defineComponent, normalizeClass, onBeforeUnmount, openBlock, ref, unref, useTemplateRef, watch, withCtx } from "vue";
6
+ import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, normalizeClass, onBeforeUnmount, openBlock, ref, toDisplayString, unref, useTemplateRef, watch, withCtx, withModifiers } from "vue";
6
7
  import { NodeViewContent, NodeViewWrapper, nodeViewProps } from "@tiptap/vue-3";
7
8
  //#region src/features/transcript/TranscriptSegmentView.vue
8
9
  const _hoisted_1 = { class: "grid grid-cols-[5.5rem_1fr] items-start gap-2" };
@@ -18,6 +19,7 @@ const _hoisted_3 = [
18
19
  "aria-expanded",
19
20
  "readonly"
20
21
  ];
22
+ const _hoisted_4 = ["title", "aria-label"];
21
23
  const _sfc_main = /*@__PURE__*/ defineComponent({
22
24
  __name: "TranscriptSegmentView",
23
25
  props: nodeViewProps,
@@ -125,39 +127,57 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
125
127
  props.editor.commands.focus(props.getPos() + props.node.nodeSize - 1);
126
128
  }
127
129
  }
130
+ /** Seeks and plays the source recording of the enclosing Baustein (a no-op without the composites feature or a source). */
131
+ function seek() {
132
+ const start = props.node.attrs.start;
133
+ const pos = props.getPos();
134
+ if (typeof start === "number" && pos !== void 0) props.editor.commands.seekSource?.(pos, start);
135
+ }
128
136
  return (_ctx, _cache) => {
129
137
  return openBlock(), createBlock(unref(NodeViewWrapper), {
130
138
  class: normalizeClass(["transcript-segment my-2", { "is-selected rounded-md ring-1 ring-[var(--sk-primary)]": _ctx.selected }]),
131
139
  "data-type": "transcript-segment",
132
140
  "data-id": _ctx.node.attrs.id || void 0
133
141
  }, {
134
- default: withCtx(() => [createElementVNode("div", _hoisted_1, [createElementVNode("div", _hoisted_2, [createElementVNode("input", {
135
- value: draft.value,
136
- title: draft.value || void 0,
137
- placeholder: unref(t)("transcript.speaker"),
138
- "aria-label": unref(t)("transcript.speaker"),
139
- autocomplete: "off",
140
- role: "combobox",
141
- "aria-expanded": open.value && items.value.length > 0,
142
- readonly: readonly.value,
143
- class: "w-full truncate rounded bg-transparent px-1 text-sm text-[var(--sk-muted)] outline-none placeholder:text-[var(--sk-fg-dimmed)] focus:bg-[color-mix(in_oklab,var(--sk-bg-accented)_50%,transparent)] focus:text-[var(--sk-fg-highlighted)]",
144
- onFocus,
145
- onBlur,
146
- onMousedown: _cache[0] || (_cache[0] = ($event) => openList("")),
147
- onInput,
148
- onKeydown: onKeyDown
149
- }, null, 40, _hoisted_3), open.value && items.value.length ? (openBlock(), createBlock(_sfc_main$1, {
150
- key: 0,
151
- ref_key: "list",
152
- ref: list,
153
- class: "absolute top-full left-0 z-50 mt-1",
154
- items: items.value,
155
- display: (item) => ({ label: item.label }),
156
- command: pick,
157
- loading: false,
158
- "loading-text": "",
159
- "empty-text": ""
160
- }, null, 8, ["items", "display"])) : createCommentVNode("v-if", true)]), createVNode(unref(NodeViewContent), { class: "transcript-segment__text" })])]),
142
+ default: withCtx(() => [createElementVNode("div", _hoisted_1, [createElementVNode("div", _hoisted_2, [
143
+ createElementVNode("input", {
144
+ value: draft.value,
145
+ title: draft.value || void 0,
146
+ placeholder: unref(t)("transcript.speaker"),
147
+ "aria-label": unref(t)("transcript.speaker"),
148
+ autocomplete: "off",
149
+ role: "combobox",
150
+ "aria-expanded": open.value && items.value.length > 0,
151
+ readonly: readonly.value,
152
+ class: "w-full truncate rounded bg-transparent px-1 text-sm text-[var(--sk-muted)] outline-none placeholder:text-[var(--sk-fg-dimmed)] focus:bg-[color-mix(in_oklab,var(--sk-bg-accented)_50%,transparent)] focus:text-[var(--sk-fg-highlighted)]",
153
+ onFocus,
154
+ onBlur,
155
+ onMousedown: _cache[0] || (_cache[0] = ($event) => openList("")),
156
+ onInput,
157
+ onKeydown: onKeyDown
158
+ }, null, 40, _hoisted_3),
159
+ _ctx.node.attrs.start != null ? (openBlock(), createElementBlock("button", {
160
+ key: 0,
161
+ type: "button",
162
+ class: "transcript-segment__time block rounded px-1 text-xs tabular-nums text-[var(--sk-muted)] hover:text-[var(--sk-primary)] hover:underline",
163
+ title: unref(t)("transcript.seek", { time: unref(formatSegmentTime)(_ctx.node.attrs.start) }),
164
+ "aria-label": unref(t)("transcript.seek", { time: unref(formatSegmentTime)(_ctx.node.attrs.start) }),
165
+ onMousedown: _cache[1] || (_cache[1] = withModifiers(() => {}, ["prevent"])),
166
+ onClick: seek
167
+ }, toDisplayString(unref(formatSegmentTime)(_ctx.node.attrs.start)), 41, _hoisted_4)) : createCommentVNode("v-if", true),
168
+ open.value && items.value.length ? (openBlock(), createBlock(_sfc_main$1, {
169
+ key: 1,
170
+ ref_key: "list",
171
+ ref: list,
172
+ class: "absolute top-full left-0 z-50 mt-1",
173
+ items: items.value,
174
+ display: (item) => ({ label: item.label }),
175
+ command: pick,
176
+ loading: false,
177
+ "loading-text": "",
178
+ "empty-text": ""
179
+ }, null, 8, ["items", "display"])) : createCommentVNode("v-if", true)
180
+ ]), createVNode(unref(NodeViewContent), { class: "transcript-segment__text" })])]),
161
181
  _: 1
162
182
  }, 8, ["class", "data-id"]);
163
183
  };
@@ -1 +1 @@
1
- {"version":3,"file":"TranscriptSegmentView.js","names":["selected","node","_openBlock","_createBlock","_unref","_normalizeClass","_withCtx","_createElementVNode","SuggestionList","_createCommentVNode","_createVNode"],"sources":["../../../src/features/transcript/TranscriptSegmentView.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { NodeViewContent, NodeViewWrapper, nodeViewProps } from '@tiptap/vue-3'\nimport { computed, onBeforeUnmount, ref, useTemplateRef, watch } from 'vue'\nimport { METADATA_LIST, type MetadataItem, metadataOf } from '../../core'\nimport SuggestionList from '../../vue/components/SuggestionList.vue'\nimport { useEditorI18n } from '../../vue/use-editor-i18n'\n\nconst props = defineProps(nodeViewProps)\nconst { t } = useEditorI18n()\n\nconst speaker = computed(() => String(props.node.attrs.speaker ?? ''))\n// Typed text is a draft until committed (blur, Enter, picking a suggestion): one undo step per name, and the\n// speaker list is only extended with whole names, not with every prefix typed on the way.\nconst draft = ref(speaker.value)\nconst focused = ref(false)\nwatch(speaker, (value) => {\n if (!focused.value) draft.value = value\n})\n// `editor.isEditable` is not reactive, so a read-only editor is detected when the input gets focus.\nconst readonly = ref(false)\n\nconst open = ref(false)\nconst items = ref<MetadataItem[]>([])\nconst list = useTemplateRef<InstanceType<typeof SuggestionList>>('list')\n\n// Same loading as the composite tags: 150 ms after typing pauses, a newer search aborts the older one (sources may be remote).\nlet timer: ReturnType<typeof setTimeout> | undefined\nlet pending: AbortController | undefined\nfunction scheduleSearch(query: string) {\n clearTimeout(timer)\n timer = setTimeout(async () => {\n pending?.abort()\n const controller = pending = new AbortController()\n try {\n const found = await metadataOf(props.editor).search(METADATA_LIST.speakers, { query, editor: props.editor, signal: controller.signal })\n if (!controller.signal.aborted) items.value = found.filter(item => item.label.trim())\n }\n catch (error) {\n if (!controller.signal.aborted) console.warn('Sprecher-Vorschläge konnten nicht geladen werden', error)\n }\n }, 150)\n}\nonBeforeUnmount(() => {\n clearTimeout(timer)\n pending?.abort()\n})\n\nfunction openList(query: string) {\n if (!props.editor.isEditable) return\n open.value = true\n scheduleSearch(query)\n}\n\nfunction closeList() {\n open.value = false\n clearTimeout(timer)\n pending?.abort()\n}\n\nfunction onFocus() {\n focused.value = true\n readonly.value = !props.editor.isEditable\n}\n\nfunction onInput(e: Event) {\n draft.value = (e.target as HTMLInputElement).value\n openList(draft.value)\n}\n\n/** Writes the draft to the node; a name the speaker list does not know yet is added to it (reported via `metadataCreate`). */\nasync function commit(name = draft.value.trim()) {\n draft.value = name\n if (readonly.value || name === speaker.value) return\n // Searched before the node changes: `search` calls the source synchronously, so a list derived from the document\n // (the default `speakers`) does not already contain the name just typed here.\n const found = name ? metadataOf(props.editor).search(METADATA_LIST.speakers, { query: name, editor: props.editor }) : null\n props.updateAttributes({ speaker: name })\n if (!found) return\n if (!(await found).some(item => item.label.toLowerCase() === name.toLowerCase())) {\n metadataOf(props.editor).create(METADATA_LIST.speakers, { id: name, label: name })\n }\n}\n\nfunction pick(item: unknown) {\n closeList()\n void commit((item as MetadataItem).label)\n}\n\nfunction onBlur() {\n focused.value = false\n closeList()\n void commit()\n}\n\n/** The speaker field of the segment `step` places before/after this one (document order), if any. */\nfunction neighbourField(input: HTMLInputElement, step: -1 | 1): HTMLInputElement | undefined {\n const fields = [...props.editor.view.dom.querySelectorAll<HTMLInputElement>('.transcript-segment__speaker input')]\n return fields[fields.indexOf(input) + step]\n}\n\n// An open list takes ArrowUp/ArrowDown/Enter/Tab; otherwise ArrowUp/ArrowDown move to the neighbouring segment's\n// speaker and Enter continues in this segment's text.\nfunction onKeyDown(e: KeyboardEvent) {\n if (open.value && items.value.length && list.value?.onKeyDown(e)) {\n e.preventDefault()\n return\n }\n const input = e.target as HTMLInputElement\n if (e.key === 'Escape' && open.value) {\n e.preventDefault()\n closeList()\n }\n else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n const next = neighbourField(input, e.key === 'ArrowUp' ? -1 : 1)\n if (!next) return\n e.preventDefault()\n next.focus()\n next.setSelectionRange(next.value.length, next.value.length)\n }\n else if (e.key === 'Enter') {\n e.preventDefault()\n input.blur()\n props.editor.commands.focus(props.getPos()! + props.node.nodeSize - 1)\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n :class=\"['transcript-segment my-2', { 'is-selected rounded-md ring-1 ring-[var(--sk-primary)]': selected }]\"\n data-type=\"transcript-segment\"\n :data-id=\"node.attrs.id || undefined\"\n >\n <div class=\"grid grid-cols-[5.5rem_1fr] items-start gap-2\">\n <div class=\"transcript-segment__speaker relative pt-0.5\" contenteditable=\"false\">\n <input\n :value=\"draft\"\n :title=\"draft || undefined\"\n :placeholder=\"t('transcript.speaker')\"\n :aria-label=\"t('transcript.speaker')\"\n autocomplete=\"off\"\n role=\"combobox\"\n :aria-expanded=\"open && items.length > 0\"\n :readonly=\"readonly\"\n class=\"w-full truncate rounded bg-transparent px-1 text-sm text-[var(--sk-muted)] outline-none placeholder:text-[var(--sk-fg-dimmed)] focus:bg-[color-mix(in_oklab,var(--sk-bg-accented)_50%,transparent)] focus:text-[var(--sk-fg-highlighted)]\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n @mousedown=\"openList('')\"\n @input=\"onInput\"\n @keydown=\"onKeyDown\"\n >\n <SuggestionList\n v-if=\"open && items.length\"\n ref=\"list\"\n class=\"absolute top-full left-0 z-50 mt-1\"\n :items=\"items\"\n :display=\"item => ({ label: (item as MetadataItem).label })\"\n :command=\"pick\"\n :loading=\"false\"\n loading-text=\"\"\n empty-text=\"\"\n />\n </div>\n <NodeViewContent class=\"transcript-segment__text\" />\n </div>\n </NodeViewWrapper>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"TranscriptSegmentView.js","names":["selected","node","_openBlock","_createBlock","_unref","_normalizeClass","_withCtx","_createElementVNode","_createElementBlock","_withModifiers","_toDisplayString","_createCommentVNode","SuggestionList","_createVNode"],"sources":["../../../src/features/transcript/TranscriptSegmentView.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { NodeViewContent, NodeViewWrapper, nodeViewProps } from '@tiptap/vue-3'\nimport { computed, onBeforeUnmount, ref, useTemplateRef, watch } from 'vue'\nimport { METADATA_LIST, type MetadataItem, metadataOf } from '../../core'\nimport SuggestionList from '../../vue/components/SuggestionList.vue'\nimport { useEditorI18n } from '../../vue/use-editor-i18n'\nimport { formatSegmentTime } from './segment-node'\n\nconst props = defineProps(nodeViewProps)\nconst { t } = useEditorI18n()\n\nconst speaker = computed(() => String(props.node.attrs.speaker ?? ''))\n// Typed text is a draft until committed (blur, Enter, picking a suggestion): one undo step per name, and the\n// speaker list is only extended with whole names, not with every prefix typed on the way.\nconst draft = ref(speaker.value)\nconst focused = ref(false)\nwatch(speaker, (value) => {\n if (!focused.value) draft.value = value\n})\n// `editor.isEditable` is not reactive, so a read-only editor is detected when the input gets focus.\nconst readonly = ref(false)\n\nconst open = ref(false)\nconst items = ref<MetadataItem[]>([])\nconst list = useTemplateRef<InstanceType<typeof SuggestionList>>('list')\n\n// Same loading as the composite tags: 150 ms after typing pauses, a newer search aborts the older one (sources may be remote).\nlet timer: ReturnType<typeof setTimeout> | undefined\nlet pending: AbortController | undefined\nfunction scheduleSearch(query: string) {\n clearTimeout(timer)\n timer = setTimeout(async () => {\n pending?.abort()\n const controller = pending = new AbortController()\n try {\n const found = await metadataOf(props.editor).search(METADATA_LIST.speakers, { query, editor: props.editor, signal: controller.signal })\n if (!controller.signal.aborted) items.value = found.filter(item => item.label.trim())\n }\n catch (error) {\n if (!controller.signal.aborted) console.warn('Sprecher-Vorschläge konnten nicht geladen werden', error)\n }\n }, 150)\n}\nonBeforeUnmount(() => {\n clearTimeout(timer)\n pending?.abort()\n})\n\nfunction openList(query: string) {\n if (!props.editor.isEditable) return\n open.value = true\n scheduleSearch(query)\n}\n\nfunction closeList() {\n open.value = false\n clearTimeout(timer)\n pending?.abort()\n}\n\nfunction onFocus() {\n focused.value = true\n readonly.value = !props.editor.isEditable\n}\n\nfunction onInput(e: Event) {\n draft.value = (e.target as HTMLInputElement).value\n openList(draft.value)\n}\n\n/** Writes the draft to the node; a name the speaker list does not know yet is added to it (reported via `metadataCreate`). */\nasync function commit(name = draft.value.trim()) {\n draft.value = name\n if (readonly.value || name === speaker.value) return\n // Searched before the node changes: `search` calls the source synchronously, so a list derived from the document\n // (the default `speakers`) does not already contain the name just typed here.\n const found = name ? metadataOf(props.editor).search(METADATA_LIST.speakers, { query: name, editor: props.editor }) : null\n props.updateAttributes({ speaker: name })\n if (!found) return\n if (!(await found).some(item => item.label.toLowerCase() === name.toLowerCase())) {\n metadataOf(props.editor).create(METADATA_LIST.speakers, { id: name, label: name })\n }\n}\n\nfunction pick(item: unknown) {\n closeList()\n void commit((item as MetadataItem).label)\n}\n\nfunction onBlur() {\n focused.value = false\n closeList()\n void commit()\n}\n\n/** The speaker field of the segment `step` places before/after this one (document order), if any. */\nfunction neighbourField(input: HTMLInputElement, step: -1 | 1): HTMLInputElement | undefined {\n const fields = [...props.editor.view.dom.querySelectorAll<HTMLInputElement>('.transcript-segment__speaker input')]\n return fields[fields.indexOf(input) + step]\n}\n\n// An open list takes ArrowUp/ArrowDown/Enter/Tab; otherwise ArrowUp/ArrowDown move to the neighbouring segment's\n// speaker and Enter continues in this segment's text.\nfunction onKeyDown(e: KeyboardEvent) {\n if (open.value && items.value.length && list.value?.onKeyDown(e)) {\n e.preventDefault()\n return\n }\n const input = e.target as HTMLInputElement\n if (e.key === 'Escape' && open.value) {\n e.preventDefault()\n closeList()\n }\n else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n const next = neighbourField(input, e.key === 'ArrowUp' ? -1 : 1)\n if (!next) return\n e.preventDefault()\n next.focus()\n next.setSelectionRange(next.value.length, next.value.length)\n }\n else if (e.key === 'Enter') {\n e.preventDefault()\n input.blur()\n props.editor.commands.focus(props.getPos()! + props.node.nodeSize - 1)\n }\n}\n\n/** Seeks and plays the source recording of the enclosing Baustein (a no-op without the composites feature or a source). */\nfunction seek() {\n const start = props.node.attrs.start\n const pos = props.getPos()\n // `seekSource` is contributed by the composites feature; without it the editor has no command and the click does nothing.\n if (typeof start === 'number' && pos !== undefined) props.editor.commands.seekSource?.(pos, start)\n}\n</script>\n\n<template>\n <NodeViewWrapper\n :class=\"['transcript-segment my-2', { 'is-selected rounded-md ring-1 ring-[var(--sk-primary)]': selected }]\"\n data-type=\"transcript-segment\"\n :data-id=\"node.attrs.id || undefined\"\n >\n <div class=\"grid grid-cols-[5.5rem_1fr] items-start gap-2\">\n <div class=\"transcript-segment__speaker relative pt-0.5\" contenteditable=\"false\">\n <input\n :value=\"draft\"\n :title=\"draft || undefined\"\n :placeholder=\"t('transcript.speaker')\"\n :aria-label=\"t('transcript.speaker')\"\n autocomplete=\"off\"\n role=\"combobox\"\n :aria-expanded=\"open && items.length > 0\"\n :readonly=\"readonly\"\n class=\"w-full truncate rounded bg-transparent px-1 text-sm text-[var(--sk-muted)] outline-none placeholder:text-[var(--sk-fg-dimmed)] focus:bg-[color-mix(in_oklab,var(--sk-bg-accented)_50%,transparent)] focus:text-[var(--sk-fg-highlighted)]\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n @mousedown=\"openList('')\"\n @input=\"onInput\"\n @keydown=\"onKeyDown\"\n >\n <button\n v-if=\"node.attrs.start != null\"\n type=\"button\"\n class=\"transcript-segment__time block rounded px-1 text-xs tabular-nums text-[var(--sk-muted)] hover:text-[var(--sk-primary)] hover:underline\"\n :title=\"t('transcript.seek', { time: formatSegmentTime(node.attrs.start) })\"\n :aria-label=\"t('transcript.seek', { time: formatSegmentTime(node.attrs.start) })\"\n @mousedown.prevent\n @click=\"seek\"\n >\n {{ formatSegmentTime(node.attrs.start) }}\n </button>\n <SuggestionList\n v-if=\"open && items.length\"\n ref=\"list\"\n class=\"absolute top-full left-0 z-50 mt-1\"\n :items=\"items\"\n :display=\"item => ({ label: (item as MetadataItem).label })\"\n :command=\"pick\"\n :loading=\"false\"\n loading-text=\"\"\n empty-text=\"\"\n />\n </div>\n <NodeViewContent class=\"transcript-segment__text\" />\n </div>\n </NodeViewWrapper>\n</template>\n"],"mappings":"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"}
@@ -1,8 +1,9 @@
1
1
  import { ABILITY } from "../../core/schema/abilities.js";
2
2
  import { filterMetadataItems } from "../../core/metadata.js";
3
- import { commandItem } from "../../core/toolbar.js";
3
+ import { command } from "../../core/commands.js";
4
4
  import { speakersIn } from "./speakers.js";
5
5
  import { TranscriptSegmentNode } from "./segment-node.js";
6
+ import { transcriptSchemaExtensions } from "./schema.js";
6
7
  import { speakerConversions } from "./convert.js";
7
8
  import { segmentSpeakerLines } from "./segment.js";
8
9
  //#region src/features/transcript/feature.ts
@@ -26,25 +27,26 @@ function transcriptFeatureCore(ui) {
26
27
  const TranscriptSegment = transcriptSegment(ui.nodeViews);
27
28
  return {
28
29
  id: "transcript",
30
+ dimension: "content",
31
+ schemaExtensions: transcriptSchemaExtensions,
29
32
  extensions: () => [TranscriptSegment],
30
- toolbar: (ctx) => [{
31
- id: "inserts",
32
- order: 50,
33
- items: [commandItem({
34
- id: "speaker",
35
- order: 10,
36
- icon: "i-lucide-speech",
37
- label: ctx.t("transcript.toolbar.speaker"),
38
- kbds: [
39
- "meta",
40
- "shift",
41
- "s"
42
- ],
43
- ability: ABILITY.transcript,
44
- command: (c) => c.toggleTranscriptSegment(),
45
- isActive: (e) => e.isActive("transcriptSegment")
46
- })]
47
- }],
33
+ commands: (ctx) => [command({
34
+ id: "speaker",
35
+ toolbar: {
36
+ group: "inserts",
37
+ order: 10
38
+ },
39
+ icon: "i-lucide-speech",
40
+ label: ctx.t("transcript.toolbar.speaker"),
41
+ kbds: [
42
+ "meta",
43
+ "shift",
44
+ "s"
45
+ ],
46
+ ability: ABILITY.transcript,
47
+ command: (c) => c.toggleTranscriptSegment(),
48
+ active: (e) => e.isActive("transcriptSegment")
49
+ })],
48
50
  metadataDefaults: () => ({ speakers: ({ query, editor }) => filterMetadataItems(speakersIn(editor.state.doc).map((s) => ({
49
51
  id: s,
50
52
  label: s
@@ -1 +1 @@
1
- {"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/transcript/feature.ts"],"sourcesContent":["import type { NodeViewRenderer } from '@tiptap/core'\nimport type { EditorFeature } from '../../core'\nimport { commandItem, filterMetadataItems } from '../../core'\nimport { ABILITY } from '../../core/schema'\nimport { speakerConversions } from './convert'\nimport { segmentSpeakerLines } from './segment'\nimport { TranscriptSegmentNode } from './segment-node'\nimport { speakersIn } from './speakers'\n\n/** What a framework binding supplies to the transcript feature: the node view of the speaker segment. */\nexport interface TranscriptUi {\n nodeViews: {\n transcriptSegment: NodeViewRenderer\n }\n}\n\n/**\n * The speaker segment node with the node view of a framework binding (`VueNodeViewRenderer(TranscriptSegmentView)`,\n * `ReactNodeViewRenderer(TranscriptSegmentView)`). TipTap's default `NodeView.stopEvent` already hands INPUT events to\n * the speaker field, not ProseMirror.\n */\nexport function transcriptSegment(nodeViews: TranscriptUi['nodeViews']) {\n return TranscriptSegmentNode.extend({\n // Explicit return type: keeps the declaration independent of the framework view's inferred types.\n addNodeView(): NodeViewRenderer {\n return nodeViews.transcriptSegment\n },\n })\n}\n\n/**\n * Speaker segments: the node with its speaker field, the „Sprecherblock“ toolbar button (Mod-Shift-S), the conversion\n * into speaker lines and a built-in `speakers` list (speakers already in the document, no backend). Contributes the\n * `segment` stage: `**Name:** text` lines of imported or AI-written Markdown become segments. The only framework part is\n * the node view (`ui.nodeViews`), so the descriptor carries no component slot.\n */\nexport function transcriptFeatureCore(ui: TranscriptUi): EditorFeature<never> {\n const TranscriptSegment = transcriptSegment(ui.nodeViews)\n return {\n id: 'transcript',\n extensions: () => [TranscriptSegment],\n toolbar: ctx => [{\n id: 'inserts',\n order: 50,\n items: [\n commandItem({\n id: 'speaker',\n order: 10,\n icon: 'i-lucide-speech',\n label: ctx.t('transcript.toolbar.speaker'),\n kbds: ['meta', 'shift', 's'],\n ability: ABILITY.transcript,\n command: c => c.toggleTranscriptSegment(),\n isActive: e => e.isActive('transcriptSegment'),\n }),\n ],\n }],\n metadataDefaults: () => ({\n speakers: ({ query, editor }) => filterMetadataItems(speakersIn(editor.state.doc).map(s => ({ id: s, label: s })), query),\n }),\n conversions: [speakerConversions],\n blocks: { segment: segmentSpeakerLines },\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAqBA,SAAgB,kBAAkB,WAAsC;CACtE,OAAO,sBAAsB,OAAO,EAElC,cAAgC;EAC9B,OAAO,UAAU;CACnB,EACF,CAAC;AACH;;;;;;;AAQA,SAAgB,sBAAsB,IAAwC;CAC5E,MAAM,oBAAoB,kBAAkB,GAAG,SAAS;CACxD,OAAO;EACL,IAAI;EACJ,kBAAkB,CAAC,iBAAiB;EACpC,UAAS,QAAO,CAAC;GACf,IAAI;GACJ,OAAO;GACP,OAAO,CACL,YAAY;IACV,IAAI;IACJ,OAAO;IACP,MAAM;IACN,OAAO,IAAI,EAAE,4BAA4B;IACzC,MAAM;KAAC;KAAQ;KAAS;IAAG;IAC3B,SAAS,QAAQ;IACjB,UAAS,MAAK,EAAE,wBAAwB;IACxC,WAAU,MAAK,EAAE,SAAS,mBAAmB;GAC/C,CAAC,CACH;EACF,CAAC;EACD,yBAAyB,EACvB,WAAW,EAAE,OAAO,aAAa,oBAAoB,WAAW,OAAO,MAAM,GAAG,CAAC,CAAC,KAAI,OAAM;GAAE,IAAI;GAAG,OAAO;EAAE,EAAE,GAAG,KAAK,EAC1H;EACA,aAAa,CAAC,kBAAkB;EAChC,QAAQ,EAAE,SAAS,oBAAoB;CACzC;AACF"}
1
+ {"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/transcript/feature.ts"],"sourcesContent":["import type { NodeViewRenderer } from '@tiptap/core'\nimport type { EditorFeature } from '../../core'\nimport { command, filterMetadataItems } from '../../core'\nimport { ABILITY } from '../../core/schema'\nimport { speakerConversions } from './convert'\nimport { segmentSpeakerLines } from './segment'\nimport { transcriptSchemaExtensions } from './schema'\nimport { TranscriptSegmentNode } from './segment-node'\nimport { speakersIn } from './speakers'\n\n/** What a framework binding supplies to the transcript feature: the node view of the speaker segment. */\nexport interface TranscriptUi {\n nodeViews: {\n transcriptSegment: NodeViewRenderer\n }\n}\n\n/**\n * The speaker segment node with the node view of a framework binding (`VueNodeViewRenderer(TranscriptSegmentView)`,\n * `ReactNodeViewRenderer(TranscriptSegmentView)`). TipTap's default `NodeView.stopEvent` already hands INPUT events to\n * the speaker field, not ProseMirror.\n */\nexport function transcriptSegment(nodeViews: TranscriptUi['nodeViews']) {\n return TranscriptSegmentNode.extend({\n // Explicit return type: keeps the declaration independent of the framework view's inferred types.\n addNodeView(): NodeViewRenderer {\n return nodeViews.transcriptSegment\n },\n })\n}\n\n/**\n * Speaker segments: the node with its speaker field, the „Sprecherblock“ toolbar button (Mod-Shift-S), the conversion\n * into speaker lines and a built-in `speakers` list (speakers already in the document, no backend). Contributes the\n * `segment` stage: `**Name:** text` lines of imported or AI-written Markdown become segments. The only framework part is\n * the node view (`ui.nodeViews`), so the descriptor carries no component slot.\n */\nexport function transcriptFeatureCore(ui: TranscriptUi): EditorFeature<never> {\n const TranscriptSegment = transcriptSegment(ui.nodeViews)\n return {\n id: 'transcript',\n dimension: 'content',\n schemaExtensions: transcriptSchemaExtensions,\n extensions: () => [TranscriptSegment],\n commands: ctx => [\n command({\n id: 'speaker',\n toolbar: { group: 'inserts', order: 10 },\n icon: 'i-lucide-speech',\n label: ctx.t('transcript.toolbar.speaker'),\n kbds: ['meta', 'shift', 's'],\n ability: ABILITY.transcript,\n command: c => c.toggleTranscriptSegment(),\n active: e => e.isActive('transcriptSegment'),\n }),\n ],\n metadataDefaults: () => ({\n speakers: ({ query, editor }) => filterMetadataItems(speakersIn(editor.state.doc).map(s => ({ id: s, label: s })), query),\n }),\n conversions: [speakerConversions],\n blocks: { segment: segmentSpeakerLines },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAsBA,SAAgB,kBAAkB,WAAsC;CACtE,OAAO,sBAAsB,OAAO,EAElC,cAAgC;EAC9B,OAAO,UAAU;CACnB,EACF,CAAC;AACH;;;;;;;AAQA,SAAgB,sBAAsB,IAAwC;CAC5E,MAAM,oBAAoB,kBAAkB,GAAG,SAAS;CACxD,OAAO;EACL,IAAI;EACJ,WAAW;EACX,kBAAkB;EAClB,kBAAkB,CAAC,iBAAiB;EACpC,WAAU,QAAO,CACf,QAAQ;GACN,IAAI;GACJ,SAAS;IAAE,OAAO;IAAW,OAAO;GAAG;GACvC,MAAM;GACN,OAAO,IAAI,EAAE,4BAA4B;GACzC,MAAM;IAAC;IAAQ;IAAS;GAAG;GAC3B,SAAS,QAAQ;GACjB,UAAS,MAAK,EAAE,wBAAwB;GACxC,SAAQ,MAAK,EAAE,SAAS,mBAAmB;EAC7C,CAAC,CACH;EACA,yBAAyB,EACvB,WAAW,EAAE,OAAO,aAAa,oBAAoB,WAAW,OAAO,MAAM,GAAG,CAAC,CAAC,KAAI,OAAM;GAAE,IAAI;GAAG,OAAO;EAAE,EAAE,GAAG,KAAK,EAC1H;EACA,aAAa,CAAC,kBAAkB;EAChC,QAAQ,EAAE,SAAS,oBAAoB;CACzC;AACF"}
@@ -1,5 +1,6 @@
1
1
  import { metadataOf } from "../../../core/metadata.js";
2
2
  import { METADATA_LIST } from "../../../core/metadata-types.js";
3
+ import { formatSegmentTime } from "../segment-node.js";
3
4
  import { useEditorI18n } from "../../../react/use-editor-i18n.js";
4
5
  import { cn } from "../../../react/ui/utils.js";
5
6
  import { SuggestionList } from "../../../react/suggestion-renderer.js";
@@ -99,6 +100,12 @@ function TranscriptSegmentView({ node, editor, selected, updateAttributes, getPo
99
100
  editor.commands.focus(getPos() + node.nodeSize - 1);
100
101
  }
101
102
  };
103
+ /** Seeks and plays the source recording of the enclosing Baustein (a no-op without the composites feature or a source). */
104
+ const seek = () => {
105
+ const start = node.attrs.start;
106
+ const pos = getPos();
107
+ if (typeof start === "number" && pos !== void 0) editor.commands.seekSource?.(pos, start);
108
+ };
102
109
  return /* @__PURE__ */ jsx(NodeViewWrapper, {
103
110
  className: cn("transcript-segment my-2", selected && "is-selected rounded-md ring-1 ring-primary"),
104
111
  "data-type": "transcript-segment",
@@ -108,43 +115,55 @@ function TranscriptSegmentView({ node, editor, selected, updateAttributes, getPo
108
115
  children: [/* @__PURE__ */ jsxs("div", {
109
116
  className: "transcript-segment__speaker relative pt-0.5",
110
117
  contentEditable: false,
111
- children: [/* @__PURE__ */ jsx("input", {
112
- value: draft,
113
- title: draft || void 0,
114
- placeholder: t("transcript.speaker"),
115
- "aria-label": t("transcript.speaker"),
116
- autoComplete: "off",
117
- role: "combobox",
118
- "aria-expanded": open && items.length > 0,
119
- readOnly: readonly,
120
- className: "w-full truncate rounded bg-transparent px-1 text-sm text-muted-foreground outline-none placeholder:text-muted-foreground/60 focus:bg-muted focus:text-foreground",
121
- onFocus: () => {
122
- focused.current = true;
123
- setReadonly(!editor.isEditable);
124
- },
125
- onBlur: () => {
126
- focused.current = false;
127
- closeList();
128
- commit();
129
- },
130
- onMouseDown: () => openList(""),
131
- onChange: (e) => {
132
- setDraft(e.target.value);
133
- openList(e.target.value);
134
- },
135
- onKeyDown
136
- }), open && items.length > 0 && /* @__PURE__ */ jsx("div", {
137
- className: "absolute top-full left-0 z-50 mt-1",
138
- children: /* @__PURE__ */ jsx(SuggestionList, {
139
- ref: list,
140
- items,
141
- display: displayItem,
142
- command: pick,
143
- loading: false,
144
- loadingText: "",
145
- emptyText: ""
118
+ children: [
119
+ /* @__PURE__ */ jsx("input", {
120
+ value: draft,
121
+ title: draft || void 0,
122
+ placeholder: t("transcript.speaker"),
123
+ "aria-label": t("transcript.speaker"),
124
+ autoComplete: "off",
125
+ role: "combobox",
126
+ "aria-expanded": open && items.length > 0,
127
+ readOnly: readonly,
128
+ className: "w-full truncate rounded bg-transparent px-1 text-sm text-muted-foreground outline-none placeholder:text-muted-foreground/60 focus:bg-muted focus:text-foreground",
129
+ onFocus: () => {
130
+ focused.current = true;
131
+ setReadonly(!editor.isEditable);
132
+ },
133
+ onBlur: () => {
134
+ focused.current = false;
135
+ closeList();
136
+ commit();
137
+ },
138
+ onMouseDown: () => openList(""),
139
+ onChange: (e) => {
140
+ setDraft(e.target.value);
141
+ openList(e.target.value);
142
+ },
143
+ onKeyDown
144
+ }),
145
+ node.attrs.start != null && /* @__PURE__ */ jsx("button", {
146
+ type: "button",
147
+ className: "transcript-segment__time block rounded px-1 text-xs tabular-nums text-muted-foreground hover:text-primary hover:underline",
148
+ title: t("transcript.seek", { time: formatSegmentTime(node.attrs.start) }),
149
+ "aria-label": t("transcript.seek", { time: formatSegmentTime(node.attrs.start) }),
150
+ onMouseDown: (e) => e.preventDefault(),
151
+ onClick: seek,
152
+ children: formatSegmentTime(node.attrs.start)
153
+ }),
154
+ open && items.length > 0 && /* @__PURE__ */ jsx("div", {
155
+ className: "absolute top-full left-0 z-50 mt-1",
156
+ children: /* @__PURE__ */ jsx(SuggestionList, {
157
+ ref: list,
158
+ items,
159
+ display: displayItem,
160
+ command: pick,
161
+ loading: false,
162
+ loadingText: "",
163
+ emptyText: ""
164
+ })
146
165
  })
147
- })]
166
+ ]
148
167
  }), /* @__PURE__ */ jsx(NodeViewContent, { className: "transcript-segment__text" })]
149
168
  })
150
169
  });