@rynt/sdk 0.9.53

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 (332) hide show
  1. package/README.md +122 -0
  2. package/REGISTRIES.md +189 -0
  3. package/env.d.ts +11 -0
  4. package/host-shims.d.ts +30 -0
  5. package/package.json +88 -0
  6. package/src/extension-marketplace/api-types.ts +141 -0
  7. package/src/extension-marketplace/client.ts +296 -0
  8. package/src/extension-marketplace/index.ts +22 -0
  9. package/src/extension-marketplace/schemas.ts +178 -0
  10. package/src/extensions/ExtensionRoutePage.vue +17 -0
  11. package/src/extensions/context.ts +37 -0
  12. package/src/extensions/disabled-folder.ts +21 -0
  13. package/src/extensions/extension-expose-map.ts +5 -0
  14. package/src/extensions/extension-expose.ts +48 -0
  15. package/src/extensions/graph.ts +67 -0
  16. package/src/extensions/index.ts +251 -0
  17. package/src/extensions/invite-handler/types.ts +20 -0
  18. package/src/extensions/launcher-entities/create-launcher-entity.ts +25 -0
  19. package/src/extensions/launcher-entities/keys.ts +46 -0
  20. package/src/extensions/launcher-entities/launcher-entity-components.ts +177 -0
  21. package/src/extensions/launcher-entities/props-map.ts +69 -0
  22. package/src/extensions/launcher-entities/registry.ts +32 -0
  23. package/src/extensions/launcher-models/apis/accounts-contracts.ts +102 -0
  24. package/src/extensions/launcher-models/apis/launcher-model-apis.ts +553 -0
  25. package/src/extensions/launcher-models/keys.ts +23 -0
  26. package/src/extensions/launcher-models/public.ts +9 -0
  27. package/src/extensions/launcher-models/registry-core.ts +34 -0
  28. package/src/extensions/manifest-types.ts +22 -0
  29. package/src/extensions/manifest.ts +46 -0
  30. package/src/extensions/marketplace-open-key.ts +26 -0
  31. package/src/extensions/plugin-types.ts +44 -0
  32. package/src/extensions/plugin.ts +62 -0
  33. package/src/extensions/registries/bootstrap.ts +11 -0
  34. package/src/extensions/registries/builtins/account-provider.ts +6 -0
  35. package/src/extensions/registries/builtins/app-topbar-left-widgets.ts +6 -0
  36. package/src/extensions/registries/builtins/app-topbar-right-widgets.ts +6 -0
  37. package/src/extensions/registries/builtins/app-topbar-status-widgets.ts +6 -0
  38. package/src/extensions/registries/builtins/build-card-actions.ts +6 -0
  39. package/src/extensions/registries/builtins/build-card-after-meta.ts +6 -0
  40. package/src/extensions/registries/builtins/build-card-before-media.ts +6 -0
  41. package/src/extensions/registries/builtins/build-card-before-meta.ts +6 -0
  42. package/src/extensions/registries/builtins/build-card-footer-actions.ts +6 -0
  43. package/src/extensions/registries/builtins/build-detail-after-content.ts +6 -0
  44. package/src/extensions/registries/builtins/build-detail-before-content.ts +6 -0
  45. package/src/extensions/registries/builtins/build-detail-before-hero.ts +6 -0
  46. package/src/extensions/registries/builtins/build-detail-header-actions.ts +6 -0
  47. package/src/extensions/registries/builtins/build-detail-mod-row-actions.ts +6 -0
  48. package/src/extensions/registries/builtins/build-detail-resourcepack-row-actions.ts +6 -0
  49. package/src/extensions/registries/builtins/build-detail-right-column-bottom.ts +6 -0
  50. package/src/extensions/registries/builtins/build-detail-right-column-top.ts +6 -0
  51. package/src/extensions/registries/builtins/dialog-footer-actions.ts +6 -0
  52. package/src/extensions/registries/builtins/feed-after-content.ts +6 -0
  53. package/src/extensions/registries/builtins/feed-before-content.ts +6 -0
  54. package/src/extensions/registries/builtins/file-editor.ts +19 -0
  55. package/src/extensions/registries/builtins/friends-after-list.ts +6 -0
  56. package/src/extensions/registries/builtins/friends-before-list.ts +6 -0
  57. package/src/extensions/registries/builtins/index.ts +141 -0
  58. package/src/extensions/registries/builtins/invite-handler.ts +7 -0
  59. package/src/extensions/registries/builtins/library-after-content.ts +6 -0
  60. package/src/extensions/registries/builtins/library-before-content.ts +6 -0
  61. package/src/extensions/registries/builtins/loader.ts +8 -0
  62. package/src/extensions/registries/builtins/map-card-actions.ts +6 -0
  63. package/src/extensions/registries/builtins/map-card-after-meta.ts +6 -0
  64. package/src/extensions/registries/builtins/map-card-before-meta.ts +6 -0
  65. package/src/extensions/registries/builtins/map-card-footer-actions.ts +6 -0
  66. package/src/extensions/registries/builtins/map-detail-after-content.ts +6 -0
  67. package/src/extensions/registries/builtins/map-detail-before-content.ts +6 -0
  68. package/src/extensions/registries/builtins/map-detail-header-actions.ts +6 -0
  69. package/src/extensions/registries/builtins/markdown-editor-tiptap-extensions.ts +7 -0
  70. package/src/extensions/registries/builtins/markdown-editor-toolbar-actions.ts +6 -0
  71. package/src/extensions/registries/builtins/markdown-renderer-after-content.ts +6 -0
  72. package/src/extensions/registries/builtins/markdown-renderer-before-content.ts +6 -0
  73. package/src/extensions/registries/builtins/mod-details-footer-actions.ts +6 -0
  74. package/src/extensions/registries/builtins/mod-manage-actions.ts +6 -0
  75. package/src/extensions/registries/builtins/mod-provider.ts +5 -0
  76. package/src/extensions/registries/builtins/nav.ts +7 -0
  77. package/src/extensions/registries/builtins/page.ts +13 -0
  78. package/src/extensions/registries/builtins/projects-after-content.ts +6 -0
  79. package/src/extensions/registries/builtins/projects-before-content.ts +6 -0
  80. package/src/extensions/registries/builtins/resourcepack-manage-actions.ts +7 -0
  81. package/src/extensions/registries/builtins/server-card-actions.ts +6 -0
  82. package/src/extensions/registries/builtins/server-card-after-meta.ts +6 -0
  83. package/src/extensions/registries/builtins/server-card-before-meta.ts +6 -0
  84. package/src/extensions/registries/builtins/server-card-footer-actions.ts +6 -0
  85. package/src/extensions/registries/builtins/server-detail-after-content.ts +6 -0
  86. package/src/extensions/registries/builtins/server-detail-before-content.ts +6 -0
  87. package/src/extensions/registries/builtins/server-detail-header-actions.ts +6 -0
  88. package/src/extensions/registries/builtins/settings-after-sections.ts +6 -0
  89. package/src/extensions/registries/builtins/settings-before-sections.ts +6 -0
  90. package/src/extensions/registries/builtins/settings-section-widgets.ts +6 -0
  91. package/src/extensions/registries/builtins/shaderpack-manage-actions.ts +7 -0
  92. package/src/extensions/registries/builtins/shell.ts +5 -0
  93. package/src/extensions/registries/builtins/sidebar-after-content.ts +6 -0
  94. package/src/extensions/registries/builtins/sidebar-before-content.ts +6 -0
  95. package/src/extensions/registries/builtins/sidebar-footer-widgets.ts +6 -0
  96. package/src/extensions/registries/builtins/sidebar-header-widgets.ts +6 -0
  97. package/src/extensions/registries/builtins/sidebar.ts +11 -0
  98. package/src/extensions/registries/builtins/theme.ts +5 -0
  99. package/src/extensions/registries/builtins/user-card-after-meta.ts +6 -0
  100. package/src/extensions/registries/builtins/user-card-before-meta.ts +6 -0
  101. package/src/extensions/registries/builtins/user-menu-actions.ts +6 -0
  102. package/src/extensions/registries/builtins/user-menu-after-actions.ts +6 -0
  103. package/src/extensions/registries/builtins/user-menu-before-actions.ts +6 -0
  104. package/src/extensions/registries/builtins/user-strip.ts +5 -0
  105. package/src/extensions/registries/clear-extension-ui-registries.ts +15 -0
  106. package/src/extensions/registries/define-extension-registry.ts +58 -0
  107. package/src/extensions/registries/extension-host-api.ts +41 -0
  108. package/src/extensions/registries/extension-registry-api.ts +103 -0
  109. package/src/extensions/registries/extension-registry-payload-map.ts +9 -0
  110. package/src/extensions/registries/extension-scope.ts +41 -0
  111. package/src/extensions/registries/get-registry.ts +23 -0
  112. package/src/extensions/registries/index.ts +58 -0
  113. package/src/extensions/registries/manifest-rynt.ts +193 -0
  114. package/src/extensions/registries/registry-slot.ts +40 -0
  115. package/src/extensions/registries/registry-value-map.ts +89 -0
  116. package/src/extensions/registries/store.ts +206 -0
  117. package/src/extensions/resolve-extensions.ts +245 -0
  118. package/src/extensions/router-bridge.ts +103 -0
  119. package/src/extensions/session.ts +6 -0
  120. package/src/extensions/slug.ts +23 -0
  121. package/src/extensions/version.ts +147 -0
  122. package/src/host/extensions-composables.ts +33 -0
  123. package/src/host/extensions-init.ts +194 -0
  124. package/src/host/index.ts +11 -0
  125. package/src/host/launcher-models/index.ts +4 -0
  126. package/src/index.ts +229 -0
  127. package/src/minecraft-loader/base-loader.ts +102 -0
  128. package/src/minecraft-loader/index.ts +11 -0
  129. package/src/minecraft-loader/loader-registry.ts +72 -0
  130. package/src/shared/api/assets.ts +112 -0
  131. package/src/shared/api/auth.ts +283 -0
  132. package/src/shared/api/builds.ts +647 -0
  133. package/src/shared/api/config.ts +19 -0
  134. package/src/shared/api/download-stats.ts +103 -0
  135. package/src/shared/api/downloads.ts +36 -0
  136. package/src/shared/api/entity-authorship.ts +60 -0
  137. package/src/shared/api/events.ts +393 -0
  138. package/src/shared/api/friends.ts +140 -0
  139. package/src/shared/api/graphql.ts +87 -0
  140. package/src/shared/api/index.ts +23 -0
  141. package/src/shared/api/invites.ts +262 -0
  142. package/src/shared/api/library.ts +44 -0
  143. package/src/shared/api/maps.ts +385 -0
  144. package/src/shared/api/notify-websocket.ts +140 -0
  145. package/src/shared/api/posts.ts +357 -0
  146. package/src/shared/api/projectServers.ts +379 -0
  147. package/src/shared/api/serverMembers.ts +173 -0
  148. package/src/shared/api/users.ts +294 -0
  149. package/src/shared/composables/buildEditor/useBuildEditor.ts +66 -0
  150. package/src/shared/composables/buildManifest/buildManifest.ts +447 -0
  151. package/src/shared/composables/filesEditor/filesEditor.ts +346 -0
  152. package/src/shared/composables/index.ts +10 -0
  153. package/src/shared/composables/modsEditor/modsEditor.ts +1678 -0
  154. package/src/shared/composables/registrySlot/registry-slot-utils.ts +25 -0
  155. package/src/shared/composables/registrySlot/useRegistrySlotMissing.ts +35 -0
  156. package/src/shared/composables/resourcePacksEditor/resourcePacksEditor.ts +448 -0
  157. package/src/shared/composables/shaderPacksEditor/shaderPacksEditor.ts +395 -0
  158. package/src/shared/composables/useSkinRender.ts +70 -0
  159. package/src/shared/composables/useZlDeepLink.ts +178 -0
  160. package/src/shared/definitions/defineGraphCache.ts +216 -0
  161. package/src/shared/definitions/defineStore.ts +32 -0
  162. package/src/shared/definitions/index.ts +2 -0
  163. package/src/shared/minecraft-types/build-manifest.ts +611 -0
  164. package/src/shared/minecraft-types/index.ts +3 -0
  165. package/src/shared/minecraft-types/launcher-versions.ts +32 -0
  166. package/src/shared/minecraft-types/minecraft-launcher-types.ts +276 -0
  167. package/src/shared/mocks/index.ts +1 -0
  168. package/src/shared/mocks/navigation.ts +17 -0
  169. package/src/shared/mods/http.ts +45 -0
  170. package/src/shared/mods/index.ts +5 -0
  171. package/src/shared/mods/marketplace-editor-search.ts +266 -0
  172. package/src/shared/mods/marketplace-search-utils.ts +42 -0
  173. package/src/shared/mods/mod-marketplace-registry.ts +66 -0
  174. package/src/shared/mods/mod-marketplace-types.ts +28 -0
  175. package/src/shared/mods/providers/curseforge.ts +464 -0
  176. package/src/shared/mods/providers/index.ts +8 -0
  177. package/src/shared/mods/providers/modrinth.ts +402 -0
  178. package/src/shared/mods/resolve-mods-provider-loader-ids.ts +77 -0
  179. package/src/shared/mods/types.ts +76 -0
  180. package/src/shared/styles/index.css +713 -0
  181. package/src/shared/themes/index.ts +23 -0
  182. package/src/shared/themes/theme-tokens-black.json +126 -0
  183. package/src/shared/themes/theme-tokens-classic.json +126 -0
  184. package/src/shared/themes/theme-tokens-pink.json +126 -0
  185. package/src/shared/themes/theme-tokens.json +126 -0
  186. package/src/shared/themes/types.ts +85 -0
  187. package/src/shared/types/API_DOCUMENTATION.md +422 -0
  188. package/src/shared/types/account.ts +40 -0
  189. package/src/shared/types/build.ts +8 -0
  190. package/src/shared/types/entities.ts +181 -0
  191. package/src/shared/types/index.ts +6 -0
  192. package/src/shared/types/invite-payloads.ts +60 -0
  193. package/src/shared/types/navigation.ts +16 -0
  194. package/src/shared/types/running-build.ts +51 -0
  195. package/src/shared/types/serverMember.ts +17 -0
  196. package/src/shared/types/user.ts +55 -0
  197. package/src/shared/ui/base/Avatar.vue +262 -0
  198. package/src/shared/ui/base/Badge.vue +47 -0
  199. package/src/shared/ui/base/Button.vue +78 -0
  200. package/src/shared/ui/base/Divider.vue +42 -0
  201. package/src/shared/ui/base/Icon.vue +597 -0
  202. package/src/shared/ui/base/StatusIndicator.vue +44 -0
  203. package/src/shared/ui/base/index.ts +7 -0
  204. package/src/shared/ui/cards/InviteCard.vue +47 -0
  205. package/src/shared/ui/cards/index.ts +2 -0
  206. package/src/shared/ui/dialog/Dialog.vue +71 -0
  207. package/src/shared/ui/dialog/DialogContent.vue +31 -0
  208. package/src/shared/ui/dialog/DialogFooter.vue +14 -0
  209. package/src/shared/ui/dialog/DialogHeader.vue +41 -0
  210. package/src/shared/ui/dialog/index.ts +5 -0
  211. package/src/shared/ui/editors/AttachmentImagesEditor.vue +133 -0
  212. package/src/shared/ui/editors/ContentAttachmentsDisplay.vue +76 -0
  213. package/src/shared/ui/editors/MarkdownEditor.vue +956 -0
  214. package/src/shared/ui/editors/MarkdownRenderer.vue +299 -0
  215. package/src/shared/ui/editors/RichContentImageViewer.vue +85 -0
  216. package/src/shared/ui/editors/SocialPostMediaZone.vue +320 -0
  217. package/src/shared/ui/editors/index.ts +6 -0
  218. package/src/shared/ui/editors/markdown-editor-gallery.ts +234 -0
  219. package/src/shared/ui/editors/markdown-editor-image.ts +178 -0
  220. package/src/shared/ui/form/Checkbox.vue +38 -0
  221. package/src/shared/ui/form/FormField.vue +30 -0
  222. package/src/shared/ui/form/FormGrid.vue +38 -0
  223. package/src/shared/ui/form/ImageEditor.vue +598 -0
  224. package/src/shared/ui/form/Input.vue +72 -0
  225. package/src/shared/ui/form/Range.vue +65 -0
  226. package/src/shared/ui/form/Select.vue +76 -0
  227. package/src/shared/ui/form/Switch.vue +38 -0
  228. package/src/shared/ui/form/Textarea.vue +144 -0
  229. package/src/shared/ui/form/index.ts +9 -0
  230. package/src/shared/ui/index.ts +9 -0
  231. package/src/shared/ui/layout/BusyOverlay.vue +31 -0
  232. package/src/shared/ui/layout/Callout.vue +44 -0
  233. package/src/shared/ui/layout/Card.vue +38 -0
  234. package/src/shared/ui/layout/Container.vue +36 -0
  235. package/src/shared/ui/layout/EmptyState.vue +99 -0
  236. package/src/shared/ui/layout/EntityMediaRow.vue +54 -0
  237. package/src/shared/ui/layout/FilterResultsLayout.vue +22 -0
  238. package/src/shared/ui/layout/FloatingPanel.vue +37 -0
  239. package/src/shared/ui/layout/FullscreenDimmer.vue +11 -0
  240. package/src/shared/ui/layout/Grid.vue +40 -0
  241. package/src/shared/ui/layout/Inline.vue +59 -0
  242. package/src/shared/ui/layout/LoadingState.vue +39 -0
  243. package/src/shared/ui/layout/MediaBox.vue +47 -0
  244. package/src/shared/ui/layout/OverlayPanel.vue +28 -0
  245. package/src/shared/ui/layout/OverlayWaitPanel.vue +22 -0
  246. package/src/shared/ui/layout/PageSection.vue +43 -0
  247. package/src/shared/ui/layout/PageToolbar.vue +29 -0
  248. package/src/shared/ui/layout/Panel.vue +39 -0
  249. package/src/shared/ui/layout/ProgressBar.vue +49 -0
  250. package/src/shared/ui/layout/Section.vue +30 -0
  251. package/src/shared/ui/layout/SegmentedControl.vue +43 -0
  252. package/src/shared/ui/layout/SelectableCard.vue +46 -0
  253. package/src/shared/ui/layout/SelectableRow.vue +41 -0
  254. package/src/shared/ui/layout/Skeleton.vue +25 -0
  255. package/src/shared/ui/layout/SkeletonAvatar.vue +30 -0
  256. package/src/shared/ui/layout/SkeletonEntityCard.vue +20 -0
  257. package/src/shared/ui/layout/SkeletonFeedPost.vue +22 -0
  258. package/src/shared/ui/layout/SkeletonGrid.vue +18 -0
  259. package/src/shared/ui/layout/SkeletonListRow.vue +31 -0
  260. package/src/shared/ui/layout/SkeletonText.vue +25 -0
  261. package/src/shared/ui/layout/Stack.vue +42 -0
  262. package/src/shared/ui/layout/StateBlock.vue +44 -0
  263. package/src/shared/ui/layout/TwoPaneLayout.vue +35 -0
  264. package/src/shared/ui/layout/VirtualList.vue +160 -0
  265. package/src/shared/ui/layout/index.ts +35 -0
  266. package/src/shared/ui/layout/skeletonSurfaceStyles.ts +24 -0
  267. package/src/shared/ui/navigation/NavItem.vue +139 -0
  268. package/src/shared/ui/navigation/Tab.vue +61 -0
  269. package/src/shared/ui/navigation/Tabs.vue +37 -0
  270. package/src/shared/ui/navigation/index.ts +4 -0
  271. package/src/shared/ui/primitives/Action.vue +19 -0
  272. package/src/shared/ui/primitives/Block.vue +28 -0
  273. package/src/shared/ui/primitives/CanvasView.vue +19 -0
  274. package/src/shared/ui/primitives/Control.vue +24 -0
  275. package/src/shared/ui/primitives/ControlSelect.vue +19 -0
  276. package/src/shared/ui/primitives/ControlTextarea.vue +17 -0
  277. package/src/shared/ui/primitives/FieldLabel.vue +19 -0
  278. package/src/shared/ui/primitives/Form.vue +19 -0
  279. package/src/shared/ui/primitives/Heading.vue +29 -0
  280. package/src/shared/ui/primitives/Image.vue +17 -0
  281. package/src/shared/ui/primitives/LineBreak.vue +3 -0
  282. package/src/shared/ui/primitives/Link.vue +19 -0
  283. package/src/shared/ui/primitives/List.vue +28 -0
  284. package/src/shared/ui/primitives/ListItem.vue +19 -0
  285. package/src/shared/ui/primitives/OptionItem.vue +19 -0
  286. package/src/shared/ui/primitives/Text.vue +28 -0
  287. package/src/shared/ui/primitives/VideoView.vue +19 -0
  288. package/src/shared/ui/primitives/index.ts +19 -0
  289. package/src/shared/ui/primitives/resolveElement.ts +25 -0
  290. package/src/shared/ui/special/AngularAccent.vue +106 -0
  291. package/src/shared/ui/special/ExtensionRegistrySlotButton.vue +143 -0
  292. package/src/shared/ui/special/InfoRow.vue +39 -0
  293. package/src/shared/ui/special/LogViewer.vue +53 -0
  294. package/src/shared/ui/special/PageHeader.vue +23 -0
  295. package/src/shared/ui/special/RegistrySlotMissingCallout.vue +48 -0
  296. package/src/shared/ui/special/WelcomeCard.vue +32 -0
  297. package/src/shared/ui/special/index.ts +9 -0
  298. package/src/shared/utils/app-paths.ts +50 -0
  299. package/src/shared/utils/attachments.ts +16 -0
  300. package/src/shared/utils/autostart.ts +213 -0
  301. package/src/shared/utils/build-files.ts +439 -0
  302. package/src/shared/utils/build-manifest-init.ts +176 -0
  303. package/src/shared/utils/cloudinary.ts +67 -0
  304. package/src/shared/utils/cn.ts +7 -0
  305. package/src/shared/utils/download-stats-week.ts +165 -0
  306. package/src/shared/utils/entity-api-to-cache.ts +84 -0
  307. package/src/shared/utils/entity-build-from-api.ts +1 -0
  308. package/src/shared/utils/entity-display.ts +27 -0
  309. package/src/shared/utils/entity-map-from-api.ts +1 -0
  310. package/src/shared/utils/file-hash.ts +65 -0
  311. package/src/shared/utils/formatSize.ts +5 -0
  312. package/src/shared/utils/formatTime.ts +157 -0
  313. package/src/shared/utils/getAccountSkinRender.ts +32 -0
  314. package/src/shared/utils/index.ts +34 -0
  315. package/src/shared/utils/local-mods.ts +678 -0
  316. package/src/shared/utils/local-settings.ts +217 -0
  317. package/src/shared/utils/member-join-stats.ts +35 -0
  318. package/src/shared/utils/platform.ts +86 -0
  319. package/src/shared/utils/play-host-slug.ts +92 -0
  320. package/src/shared/utils/rich-content.ts +294 -0
  321. package/src/shared/utils/safeRequest.ts +23 -0
  322. package/src/shared/utils/semver.ts +81 -0
  323. package/src/shared/utils/serverPermissions.ts +155 -0
  324. package/src/shared/utils/skin-render-cache.ts +372 -0
  325. package/src/shared/utils/stripMarkdown.ts +45 -0
  326. package/src/shared/utils/transliterate.ts +74 -0
  327. package/src/shared/utils/updateAccountSkinRender.ts +64 -0
  328. package/src/shared/utils/updater.ts +218 -0
  329. package/src/shared/utils/uploadImage.ts +195 -0
  330. package/src/shared/utils/user-status.ts +9 -0
  331. package/src/tiptap/index.ts +7 -0
  332. package/tsconfig.json +13 -0
@@ -0,0 +1,299 @@
1
+ <template>
2
+ <div class="rich-content-renderer">
3
+ <template
4
+ v-for="entry in beforeContentRegistryEntries"
5
+ :key="`${entry.extensionId}:${entry.registryKey}`"
6
+ >
7
+ <component
8
+ :is="entry.value"
9
+ :content="props.content"
10
+ :variant="props.variant"
11
+ :images="images"
12
+ />
13
+ </template>
14
+ <div
15
+ v-if="textHtml"
16
+ class="markdown-renderer prose prose-invert prose-headings:text-foreground prose-p:text-muted-foreground prose-a:text-accent-secondary prose-strong:text-foreground prose-code:text-accent-secondary max-w-none"
17
+ :class="{ 'markdown-renderer--rich': variant === 'rich' }"
18
+ v-html="textHtml"
19
+ @click="handleContentClick"
20
+ />
21
+
22
+ <div v-if="variant === 'social' && images.length" class="rich-content-attachments">
23
+ <button
24
+ v-for="(image, index) in images"
25
+ :key="`${image.src}-${index}`"
26
+ type="button"
27
+ class="rich-content-attachment"
28
+ :title="image.alt || 'Открыть изображение'"
29
+ @click="openViewer(index)"
30
+ >
31
+ <img :src="image.src" :alt="image.alt || ''" loading="lazy" />
32
+ </button>
33
+ </div>
34
+
35
+ <RichContentImageViewer
36
+ :open="viewerOpen"
37
+ :images="viewerImages"
38
+ :initial-index="viewerIndex"
39
+ @close="viewerOpen = false"
40
+ />
41
+ <template
42
+ v-for="entry in afterContentRegistryEntries"
43
+ :key="`${entry.extensionId}:${entry.registryKey}`"
44
+ >
45
+ <component
46
+ :is="entry.value"
47
+ :content="props.content"
48
+ :variant="props.variant"
49
+ :images="images"
50
+ />
51
+ </template>
52
+ </div>
53
+ </template>
54
+
55
+ <script setup lang="ts">
56
+ import { computed, ref } from 'vue';
57
+ import type { RichContentImage } from '../../utils/rich-content';
58
+ import {
59
+ prepareRichVariantContent,
60
+ renderRichContent,
61
+ splitRichContentImages,
62
+ } from '../../utils/rich-content';
63
+ import RichContentImageViewer from './RichContentImageViewer.vue';
64
+ import {
65
+ useMarkdownRendererAfterContentRegistry,
66
+ useMarkdownRendererBeforeContentRegistry,
67
+ } from '../../../extensions/registries/builtins';
68
+
69
+ interface Props {
70
+ content: string;
71
+ variant?: 'rich' | 'social';
72
+ }
73
+
74
+ const props = withDefaults(defineProps<Props>(), {
75
+ variant: 'rich',
76
+ });
77
+
78
+ const viewerOpen = ref(false);
79
+ const viewerIndex = ref(0);
80
+
81
+ const socialParsed = computed(() =>
82
+ props.variant === 'social'
83
+ ? splitRichContentImages(props.content || '')
84
+ : { html: '', images: [] as RichContentImage[] },
85
+ );
86
+
87
+ const richParsed = computed(() =>
88
+ props.variant === 'rich'
89
+ ? prepareRichVariantContent(props.content || '')
90
+ : { html: '', images: [] as RichContentImage[] },
91
+ );
92
+
93
+ const textHtml = computed(() => {
94
+ if (props.variant === 'social') {
95
+ if (!socialParsed.value.html) return '';
96
+ return renderRichContent(socialParsed.value.html);
97
+ }
98
+ return richParsed.value.html;
99
+ });
100
+
101
+ const images = computed(() =>
102
+ props.variant === 'social'
103
+ ? socialParsed.value.images
104
+ : richParsed.value.images,
105
+ );
106
+ const beforeContentRegistryEntries = computed(() =>
107
+ useMarkdownRendererBeforeContentRegistry().entries(),
108
+ );
109
+ const afterContentRegistryEntries = computed(() =>
110
+ useMarkdownRendererAfterContentRegistry().entries(),
111
+ );
112
+
113
+ const viewerImages = computed(() => images.value);
114
+
115
+ const openViewer = (index: number) => {
116
+ viewerIndex.value = index;
117
+ viewerOpen.value = true;
118
+ };
119
+
120
+ const handleContentClick = (event: MouseEvent) => {
121
+ if (props.variant !== 'rich') return;
122
+
123
+ const target = event.target as HTMLElement | null;
124
+ const img = target?.closest('img');
125
+ if (!img) return;
126
+
127
+ const src = img.getAttribute('src') || '';
128
+ if (!src) return;
129
+
130
+ const index = images.value.findIndex((image) => image.src === src);
131
+ if (index >= 0) {
132
+ event.preventDefault();
133
+ openViewer(index);
134
+ }
135
+ };
136
+ </script>
137
+
138
+ <style scoped>
139
+ .rich-content-renderer {
140
+ color: var(--muted-foreground);
141
+ }
142
+
143
+ .markdown-renderer :deep(h1),
144
+ .markdown-renderer :deep(h2),
145
+ .markdown-renderer :deep(h3),
146
+ .markdown-renderer :deep(h4),
147
+ .markdown-renderer :deep(h5),
148
+ .markdown-renderer :deep(h6) {
149
+ color: var(--foreground);
150
+ font-weight: 600;
151
+ margin-top: 1.5em;
152
+ margin-bottom: 0.5em;
153
+ }
154
+
155
+ .markdown-renderer :deep(p) {
156
+ margin-bottom: 1em;
157
+ }
158
+
159
+ .markdown-renderer :deep(ul),
160
+ .markdown-renderer :deep(ol) {
161
+ padding-left: 1.5em;
162
+ margin-bottom: 1em;
163
+ }
164
+
165
+ .markdown-renderer :deep(ul) {
166
+ list-style-type: disc;
167
+ }
168
+
169
+ .markdown-renderer :deep(ol) {
170
+ list-style-type: decimal;
171
+ }
172
+
173
+ .markdown-renderer :deep(li) {
174
+ margin-bottom: 0.5em;
175
+ }
176
+
177
+ .markdown-renderer :deep(li > p) {
178
+ margin-bottom: 0;
179
+ }
180
+
181
+ .markdown-renderer :deep(li::marker) {
182
+ color: var(--subtle-fg);
183
+ }
184
+
185
+ .markdown-renderer :deep(a) {
186
+ color: var(--accent-secondary);
187
+ text-decoration: underline;
188
+ text-underline-offset: 2px;
189
+ }
190
+
191
+ .markdown-renderer :deep(a:hover) {
192
+ color: #a0a8ee;
193
+ }
194
+
195
+ .markdown-renderer :deep(code) {
196
+ background-color: var(--card);
197
+ padding: 0.2em 0.4em;
198
+ border-radius: var(--radius-ui-lg);
199
+ font-size: 0.9em;
200
+ }
201
+
202
+ .markdown-renderer :deep(pre) {
203
+ background-color: var(--card);
204
+ padding: 1em;
205
+ border-radius: var(--radius-ui-xl);
206
+ overflow-x: auto;
207
+ margin-bottom: 1em;
208
+ }
209
+
210
+ .markdown-renderer :deep(pre code) {
211
+ background-color: transparent;
212
+ padding: 0;
213
+ }
214
+
215
+ .markdown-renderer :deep(blockquote) {
216
+ border-left: 4px solid var(--primary);
217
+ padding-left: 1em;
218
+ margin: 1em 0;
219
+ color: var(--subtle-fg);
220
+ font-style: italic;
221
+ }
222
+
223
+ .markdown-renderer :deep(table) {
224
+ width: 100%;
225
+ border-collapse: collapse;
226
+ margin: 1em 0;
227
+ }
228
+
229
+ .markdown-renderer :deep(th),
230
+ .markdown-renderer :deep(td) {
231
+ border: 1px solid color-mix(in srgb, var(--primary) 20%, transparent);
232
+ padding: 0.5em;
233
+ }
234
+
235
+ .markdown-renderer :deep(th) {
236
+ background-color: var(--card);
237
+ font-weight: 600;
238
+ }
239
+
240
+ .markdown-renderer--rich :deep(img) {
241
+ max-width: 100%;
242
+ height: auto;
243
+ border-radius: var(--radius-ui-lg);
244
+ margin: 0.5rem 0;
245
+ cursor: zoom-in;
246
+ }
247
+
248
+ .markdown-renderer--rich :deep(.md-gallery) {
249
+ display: grid;
250
+ grid-template-columns: repeat(auto-fill, minmax(6rem, 1fr));
251
+ gap: 0.5rem;
252
+ margin: 0.75rem 0;
253
+ padding: 0.5rem;
254
+ border: 1px solid var(--border);
255
+ border-radius: var(--radius-ui-lg);
256
+ background: var(--card);
257
+ }
258
+
259
+ .markdown-renderer--rich :deep(.md-gallery img),
260
+ .markdown-renderer--rich :deep(.md-gallery__image) {
261
+ width: 100%;
262
+ aspect-ratio: 1;
263
+ object-fit: cover;
264
+ margin: 0;
265
+ cursor: zoom-in;
266
+ }
267
+
268
+ .rich-content-attachments {
269
+ display: flex;
270
+ flex-wrap: wrap;
271
+ gap: 0.5rem;
272
+ margin-top: 1rem;
273
+ padding-top: 0.75rem;
274
+ border-top: 1px solid var(--border);
275
+ }
276
+
277
+ .rich-content-attachment {
278
+ position: relative;
279
+ overflow: hidden;
280
+ width: 5.5rem;
281
+ height: 5.5rem;
282
+ border: 1px solid var(--border);
283
+ border-radius: var(--radius-ui-lg);
284
+ background: var(--card);
285
+ cursor: zoom-in;
286
+ transition: border-color 0.15s ease;
287
+ }
288
+
289
+ .rich-content-attachment:hover {
290
+ border-color: var(--accent-secondary);
291
+ }
292
+
293
+ .rich-content-attachment img {
294
+ width: 100%;
295
+ height: 100%;
296
+ object-fit: cover;
297
+ display: block;
298
+ }
299
+ </style>
@@ -0,0 +1,85 @@
1
+ <template>
2
+ <Teleport to="body">
3
+ <div
4
+ v-if="open"
5
+ class="fixed inset-0 z-[9999] flex items-center justify-center bg-black/60"
6
+ @click.self="close"
7
+ >
8
+ <button
9
+ type="button"
10
+ class="absolute top-4 right-4 text-2xl text-primary-foreground/80 hover:text-foreground"
11
+ aria-label="Закрыть"
12
+ @click="close"
13
+ >
14
+
15
+ </button>
16
+
17
+ <button
18
+ v-if="images.length > 1"
19
+ type="button"
20
+ class="absolute left-4 flex h-10 w-10 items-center justify-center bg-black/80 cursor-pointer text-3xl text-primary-foreground/80 hover:text-foreground md:left-8 md:h-12 md:w-12"
21
+ aria-label="Предыдущее изображение"
22
+ @click.stop="showPrev"
23
+ >
24
+
25
+ </button>
26
+
27
+ <img
28
+ :src="images[currentIndex]?.src"
29
+ :alt="images[currentIndex]?.alt || ''"
30
+ class="max-h-[90vh] max-w-[95vw] object-contain"
31
+ />
32
+
33
+ <button
34
+ v-if="images.length > 1"
35
+ type="button"
36
+ class="absolute right-4 flex h-10 w-10 items-center justify-center bg-black/80 cursor-pointer text-3xl text-primary-foreground/80 hover:text-foreground md:right-8 md:h-12 md:w-12"
37
+ aria-label="Следующее изображение"
38
+ @click.stop="showNext"
39
+ >
40
+
41
+ </button>
42
+ </div>
43
+ </Teleport>
44
+ </template>
45
+
46
+ <script setup lang="ts">
47
+ import { ref, watch } from 'vue';
48
+ import type { RichContentImage } from '../../utils/rich-content';
49
+
50
+ const props = defineProps<{
51
+ images: RichContentImage[];
52
+ initialIndex?: number;
53
+ open: boolean;
54
+ }>();
55
+
56
+ const emit = defineEmits<{
57
+ close: [];
58
+ }>();
59
+
60
+ const currentIndex = ref(0);
61
+
62
+ watch(
63
+ () => [props.open, props.initialIndex] as const,
64
+ ([isOpen, index]) => {
65
+ if (!isOpen) return;
66
+ currentIndex.value = index ?? 0;
67
+ },
68
+ { immediate: true },
69
+ );
70
+
71
+ const close = () => {
72
+ emit('close');
73
+ };
74
+
75
+ const showPrev = () => {
76
+ if (!props.images.length) return;
77
+ currentIndex.value =
78
+ (currentIndex.value - 1 + props.images.length) % props.images.length;
79
+ };
80
+
81
+ const showNext = () => {
82
+ if (!props.images.length) return;
83
+ currentIndex.value = (currentIndex.value + 1) % props.images.length;
84
+ };
85
+ </script>
@@ -0,0 +1,320 @@
1
+ <template>
2
+ <div
3
+ class="social-post-media-zone"
4
+ :class="{ 'social-post-media-zone--dragover': isDragOver }"
5
+ @paste.capture="handlePaste"
6
+ @dragover.prevent="handleDragOver"
7
+ @dragleave="handleDragLeave"
8
+ @drop.prevent="handleDrop"
9
+ >
10
+ <div
11
+ v-if="showCover"
12
+ class="social-post-media-zone__cover group relative mb-3 overflow-hidden rounded border border-border bg-card"
13
+ >
14
+ <div
15
+ class="aspect-video w-full bg-gradient-to-br from-primary/20 to-accent-secondary/20"
16
+ >
17
+ <img
18
+ v-if="coverUrl"
19
+ :src="coverUrl"
20
+ alt=""
21
+ class="h-full w-full object-cover"
22
+ />
23
+ </div>
24
+ <div
25
+ v-if="coverUploading"
26
+ class="absolute inset-0 flex items-center justify-center bg-background/50"
27
+ >
28
+ <div
29
+ class="h-6 w-6 animate-spin rounded-full border-2 border-foreground/25 border-t-accent-secondary"
30
+ />
31
+ </div>
32
+ <button
33
+ v-if="!coverUploading"
34
+ type="button"
35
+ class="absolute right-2 top-2 flex h-7 w-7 items-center justify-center rounded bg-background/80 text-subtle-fg opacity-0 shadow transition-opacity hover:text-destructive group-hover:opacity-100"
36
+ title="Удалить главное фото"
37
+ @mousedown.prevent="removeCover"
38
+ >
39
+ <Icon name="x" size="sm" />
40
+ </button>
41
+ </div>
42
+
43
+ <slot />
44
+
45
+ <div
46
+ v-if="showStrip || showHint"
47
+ class="social-post-media-zone__footer mt-3 space-y-2"
48
+ >
49
+ <div v-if="showStrip" class="flex flex-wrap gap-2">
50
+ <div
51
+ v-for="item in attachmentItems"
52
+ :key="item.key"
53
+ class="group relative h-20 w-20 overflow-hidden rounded border border-border bg-card"
54
+ >
55
+ <img
56
+ v-if="item.url"
57
+ :src="item.url"
58
+ alt=""
59
+ class="h-full w-full object-cover"
60
+ />
61
+ <div
62
+ v-if="item.uploading"
63
+ class="absolute inset-0 flex items-center justify-center bg-background/50"
64
+ >
65
+ <div
66
+ class="h-5 w-5 animate-spin rounded-full border-2 border-foreground/25 border-t-accent-secondary"
67
+ />
68
+ </div>
69
+ <button
70
+ v-if="!item.uploading"
71
+ type="button"
72
+ class="absolute right-1 top-1 flex h-5 w-5 items-center justify-center rounded bg-background/80 text-subtle-fg opacity-0 transition-opacity hover:text-destructive group-hover:opacity-100"
73
+ title="Удалить"
74
+ @mousedown.prevent="removeAttachment(item.index)"
75
+ >
76
+ <Icon name="x" size="xs" />
77
+ </button>
78
+ </div>
79
+ </div>
80
+
81
+ <p v-if="showHint" class="text-xs text-subtle-fg">
82
+ {{ hintText }}
83
+ </p>
84
+ </div>
85
+ </div>
86
+ </template>
87
+
88
+ <script setup lang="ts">
89
+ import { computed, ref } from 'vue';
90
+ import { Icon } from '..';
91
+ import { uploadImage } from '../../utils/uploadImage';
92
+
93
+ type ZoneMode = 'profile' | 'attachments-only';
94
+
95
+ interface Props {
96
+ mode: ZoneMode;
97
+ attachments: string[];
98
+ uploadFolder?: string;
99
+ maxImages?: number;
100
+ image?: string;
101
+ showHint?: boolean;
102
+ }
103
+
104
+ const props = withDefaults(defineProps<Props>(), {
105
+ uploadFolder: 'posts',
106
+ maxImages: 5,
107
+ image: '',
108
+ showHint: true,
109
+ });
110
+
111
+ const emit = defineEmits<{
112
+ 'update:attachments': [value: string[]];
113
+ 'update:image': [value: string];
114
+ }>();
115
+
116
+ const isDragOver = ref(false);
117
+ const coverUploading = ref(false);
118
+ const uploadingAttachmentKeys = ref(new Set<string>());
119
+
120
+ const coverUrl = computed(() => props.image || '');
121
+
122
+ const slotsUsed = computed(() => {
123
+ if (props.mode === 'profile') {
124
+ return (props.image ? 1 : 0) + props.attachments.length;
125
+ }
126
+ return props.attachments.length;
127
+ });
128
+
129
+ const slotsLeft = computed(() =>
130
+ Math.max(0, props.maxImages - slotsUsed.value),
131
+ );
132
+
133
+ const showCover = computed(
134
+ () => props.mode === 'profile' && (coverUrl.value || coverUploading.value),
135
+ );
136
+
137
+ const showStrip = computed(
138
+ () =>
139
+ props.attachments.length > 0 || uploadingAttachmentKeys.value.size > 0,
140
+ );
141
+
142
+ const hintText = computed(() => {
143
+ if (props.mode === 'profile') {
144
+ return `Ctrl+V или перетащите фото (до ${props.maxImages}, первая — главная)`;
145
+ }
146
+ return `Ctrl+V или перетащите фото (до ${props.maxImages})`;
147
+ });
148
+
149
+ const attachmentItems = computed(() =>
150
+ props.attachments.map((url, index) => ({
151
+ key: `att-${index}-${url}`,
152
+ index,
153
+ url,
154
+ uploading: uploadingAttachmentKeys.value.has(`att-${index}`),
155
+ })),
156
+ );
157
+
158
+ const extractImageFiles = (fileList: FileList | File[]): File[] => {
159
+ return Array.from(fileList).filter((file) =>
160
+ file.type.startsWith('image/'),
161
+ );
162
+ };
163
+
164
+ const assignTargets = (
165
+ files: File[],
166
+ ): Array<{ file: File; target: 'cover' | 'attachment' }> => {
167
+ const result: Array<{ file: File; target: 'cover' | 'attachment' }> = [];
168
+ let left = slotsLeft.value;
169
+ if (!left) return result;
170
+
171
+ let coverTaken = props.mode === 'profile' ? Boolean(props.image) : true;
172
+
173
+ for (const file of files) {
174
+ if (left <= 0) break;
175
+
176
+ if (props.mode === 'profile' && !coverTaken) {
177
+ result.push({ file, target: 'cover' });
178
+ coverTaken = true;
179
+ left -= 1;
180
+ continue;
181
+ }
182
+
183
+ result.push({ file, target: 'attachment' });
184
+ left -= 1;
185
+ }
186
+
187
+ return result;
188
+ };
189
+
190
+ const uploadToCover = async (file: File) => {
191
+ const preview = URL.createObjectURL(file);
192
+ emit('update:image', preview);
193
+ coverUploading.value = true;
194
+
195
+ try {
196
+ const result = await uploadImage(file, {
197
+ folder: props.uploadFolder,
198
+ maxSizeBytes: 10 * 1024 * 1024,
199
+ });
200
+ emit('update:image', result.url);
201
+ } catch (error) {
202
+ console.error('[SocialPostMediaZone] cover upload failed:', error);
203
+ emit('update:image', '');
204
+ } finally {
205
+ coverUploading.value = false;
206
+ URL.revokeObjectURL(preview);
207
+ }
208
+ };
209
+
210
+ const uploadToAttachment = async (file: File, insertIndex: number) => {
211
+ const key = `att-${insertIndex}`;
212
+ const next = [...props.attachments];
213
+ const preview = URL.createObjectURL(file);
214
+ next.splice(insertIndex, 0, preview);
215
+ emit('update:attachments', next);
216
+
217
+ const pending = new Set(uploadingAttachmentKeys.value);
218
+ pending.add(key);
219
+ uploadingAttachmentKeys.value = pending;
220
+
221
+ try {
222
+ const result = await uploadImage(file, {
223
+ folder: props.uploadFolder,
224
+ maxSizeBytes: 10 * 1024 * 1024,
225
+ });
226
+ const updated = [...props.attachments];
227
+ if (updated[insertIndex] === preview) {
228
+ updated[insertIndex] = result.url;
229
+ } else {
230
+ const previewIndex = updated.indexOf(preview);
231
+ if (previewIndex >= 0) updated[previewIndex] = result.url;
232
+ }
233
+ emit('update:attachments', updated.filter(Boolean));
234
+ } catch (error) {
235
+ console.error('[SocialPostMediaZone] attachment upload failed:', error);
236
+ const updated = props.attachments.filter((url) => url !== preview);
237
+ emit('update:attachments', updated);
238
+ } finally {
239
+ const done = new Set(uploadingAttachmentKeys.value);
240
+ done.delete(key);
241
+ uploadingAttachmentKeys.value = done;
242
+ URL.revokeObjectURL(preview);
243
+ }
244
+ };
245
+
246
+ const addFiles = async (files: File[]) => {
247
+ const imageFiles = extractImageFiles(files);
248
+ if (!imageFiles.length || slotsLeft.value <= 0) return;
249
+
250
+ const assignments = assignTargets(imageFiles);
251
+ for (const { file, target } of assignments) {
252
+ if (target === 'cover') {
253
+ await uploadToCover(file);
254
+ } else {
255
+ await uploadToAttachment(file, props.attachments.length);
256
+ }
257
+ }
258
+ };
259
+
260
+ const handlePaste = (event: ClipboardEvent) => {
261
+ const items = event.clipboardData?.items;
262
+ if (!items?.length) return;
263
+
264
+ const imageFiles = Array.from(items)
265
+ .filter((item) => item.type.startsWith('image/'))
266
+ .map((item) => item.getAsFile())
267
+ .filter((file): file is File => file !== null);
268
+
269
+ if (!imageFiles.length) return;
270
+
271
+ event.preventDefault();
272
+ event.stopPropagation();
273
+ void addFiles(imageFiles);
274
+ };
275
+
276
+ const handleDragOver = () => {
277
+ if (slotsLeft.value > 0) {
278
+ isDragOver.value = true;
279
+ }
280
+ };
281
+
282
+ const handleDragLeave = (event: DragEvent) => {
283
+ const related = event.relatedTarget as Node | null;
284
+ const current = event.currentTarget as HTMLElement | null;
285
+ if (current?.contains(related)) return;
286
+ isDragOver.value = false;
287
+ };
288
+
289
+ const handleDrop = (event: DragEvent) => {
290
+ isDragOver.value = false;
291
+ const files = event.dataTransfer?.files;
292
+ if (!files?.length) return;
293
+ void addFiles(extractImageFiles(files));
294
+ };
295
+
296
+ const removeCover = () => {
297
+ emit('update:image', '');
298
+ };
299
+
300
+ const removeAttachment = (index: number) => {
301
+ const next = [...props.attachments];
302
+ next.splice(index, 1);
303
+ emit('update:attachments', next);
304
+ };
305
+ </script>
306
+
307
+ <style scoped>
308
+ .social-post-media-zone {
309
+ position: relative;
310
+ border-radius: var(--radius-ui-lg);
311
+ transition:
312
+ box-shadow 0.15s ease,
313
+ background-color 0.15s ease;
314
+ }
315
+
316
+ .social-post-media-zone--dragover {
317
+ box-shadow: inset 0 0 0 2px var(--accent-secondary);
318
+ background: color-mix(in srgb, var(--accent-secondary) 6%, transparent);
319
+ }
320
+ </style>
@@ -0,0 +1,6 @@
1
+ export { default as AttachmentImagesEditor } from './AttachmentImagesEditor.vue';
2
+ export { default as SocialPostMediaZone } from './SocialPostMediaZone.vue';
3
+ export { default as ContentAttachmentsDisplay } from './ContentAttachmentsDisplay.vue';
4
+ export { default as MarkdownEditor } from './MarkdownEditor.vue';
5
+ export { default as MarkdownRenderer } from './MarkdownRenderer.vue';
6
+