frappe-ui 1.0.0-beta.2 → 1.0.0-beta.21

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/experimental/FloatingWindow/FloatingWindow.cy.ts +328 -0
  2. package/experimental/FloatingWindow/FloatingWindow.vue +303 -0
  3. package/experimental/FloatingWindow/index.ts +8 -0
  4. package/experimental/FloatingWindow/types.ts +31 -0
  5. package/experimental/FloatingWindow/useFloatingWindow.ts +236 -0
  6. package/experimental/MultiEmailInput/MultiEmailInput.cy.ts +155 -0
  7. package/experimental/MultiEmailInput/MultiEmailInput.vue +472 -0
  8. package/experimental/MultiEmailInput/index.ts +17 -0
  9. package/experimental/MultiEmailInput/stories/AsyncSuggestions.vue +54 -0
  10. package/experimental/MultiEmailInput/stories/Basic.vue +18 -0
  11. package/experimental/MultiEmailInput/stories/CustomChip.vue +39 -0
  12. package/experimental/MultiEmailInput/stories/Labeling.vue +28 -0
  13. package/experimental/MultiEmailInput/types.ts +138 -0
  14. package/experimental/MultiEmailInput/utils.ts +60 -0
  15. package/experimental.ts +12 -0
  16. package/frappe/Billing/SignupBanner.vue +1 -1
  17. package/frappe/Billing/TrialBanner.vue +1 -1
  18. package/frappe/DataImport/DataImport.vue +1 -1
  19. package/frappe/DataImport/DataImportList.vue +1 -1
  20. package/frappe/DataImport/ImportSteps.vue +6 -6
  21. package/frappe/DataImport/MappingStep.vue +1 -1
  22. package/frappe/DataImport/PreviewStep.vue +9 -9
  23. package/frappe/DataImport/TemplateModal.vue +1 -1
  24. package/frappe/DataImport/UploadStep.vue +5 -5
  25. package/frappe/Filter/Filter.vue +1 -1
  26. package/frappe/Help/HelpModal.vue +1 -1
  27. package/frappe/Link/stories/Suffix.vue +2 -2
  28. package/frappe/Onboarding/GettingStartedBanner.vue +1 -1
  29. package/frappe/drive/components/AppSwitcher.vue +1 -1
  30. package/frappe/drive/components/InfoDialog.vue +3 -3
  31. package/frappe/drive/components/MoveDialog.vue +2 -2
  32. package/frappe/drive/components/Select/Select.vue +2 -2
  33. package/frappe/drive/components/ShareDialog.vue +1 -1
  34. package/frappe/telemetry/index.ts +88 -14
  35. package/frappe/telemetry/pulse.ts +78 -164
  36. package/frappe/telemetry/telemetry.test.ts +13 -79
  37. package/icons/IconPicker.vue +1 -1
  38. package/package.json +104 -34
  39. package/src/components/Alert/Alert.md +4 -0
  40. package/src/components/Alert/Alert.vue +4 -4
  41. package/src/components/Autocomplete/Autocomplete.vue +6 -6
  42. package/src/components/Avatar/Avatar.md +4 -0
  43. package/src/components/Avatar/Avatar.vue +23 -4
  44. package/src/components/Badge/Badge.api.md +1 -1
  45. package/src/components/Badge/Badge.cy.ts +34 -16
  46. package/src/components/Badge/Badge.md +20 -4
  47. package/src/components/Badge/Badge.vue +60 -45
  48. package/src/components/Badge/stories/CallMeta.vue +35 -0
  49. package/src/components/Badge/stories/EventStatus.vue +16 -0
  50. package/src/components/Badge/stories/ListStatus.vue +91 -0
  51. package/src/components/Badge/stories/Reactions.vue +33 -0
  52. package/src/components/Badge/stories/RowTags.vue +39 -0
  53. package/src/components/Badge/types.ts +1 -2
  54. package/src/components/BottomSheet/BottomSheet.api.md +57 -0
  55. package/src/components/BottomSheet/BottomSheet.vue +232 -0
  56. package/src/components/BottomSheet/index.ts +6 -0
  57. package/src/components/BottomSheet/types.ts +23 -0
  58. package/src/components/Breadcrumbs/Breadcrumbs.md +4 -0
  59. package/src/components/Breadcrumbs/Breadcrumbs.vue +3 -3
  60. package/src/components/Button/Button.api.md +37 -32
  61. package/src/components/Button/Button.cy.ts +18 -2
  62. package/src/components/Button/Button.md +20 -8
  63. package/src/components/Button/Button.vue +304 -284
  64. package/src/components/Button/stories/InlineActions.vue +10 -0
  65. package/src/components/Button/stories/LiveClassCard.vue +34 -0
  66. package/src/components/Button/stories/SectionAction.vue +17 -0
  67. package/src/components/Button/stories/SectionControls.vue +28 -0
  68. package/src/components/Button/stories/SelectionToolbar.vue +20 -0
  69. package/src/components/Button/stories/StackedActions.vue +10 -0
  70. package/src/components/Button/types.ts +38 -32
  71. package/src/components/Calendar/Calendar.api.md +16 -16
  72. package/src/components/Calendar/Calendar.vue +100 -73
  73. package/src/components/Calendar/CalendarDaily.vue +21 -20
  74. package/src/components/Calendar/CalendarMonthEvent.vue +30 -9
  75. package/src/components/Calendar/CalendarMonthly.vue +60 -39
  76. package/src/components/Calendar/CalendarTimeMarker.vue +9 -8
  77. package/src/components/Calendar/CalendarWeekDayEvent.vue +98 -42
  78. package/src/components/Calendar/CalendarWeekly.vue +77 -41
  79. package/src/components/Calendar/EventModalContent.vue +32 -16
  80. package/src/components/Calendar/NewEventModal.vue +19 -16
  81. package/src/components/Calendar/ShowMoreCalendarEvent.vue +11 -17
  82. package/src/components/Calendar/{calendarUtils.js → calendarUtils.ts} +76 -52
  83. package/src/components/Calendar/composables/{useCalendarData.js → useCalendarData.ts} +23 -11
  84. package/src/components/Calendar/composables/{useEventModal.js → useEventModal.ts} +16 -12
  85. package/src/components/Calendar/index.ts +12 -2
  86. package/src/components/Calendar/stories/CustomHeader.vue +1 -1
  87. package/src/components/Calendar/types.ts +106 -0
  88. package/src/components/Calendar/{useEventBase.js → useEventBase.ts} +40 -26
  89. package/src/components/Card.vue +2 -2
  90. package/src/components/Charts/ECharts.vue +1 -1
  91. package/src/components/Charts/NumberChart.vue +7 -7
  92. package/src/components/Charts/eChartOptions.ts +28 -13
  93. package/src/components/Checkbox/Checkbox.api.md +6 -0
  94. package/src/components/Checkbox/Checkbox.cy.ts +10 -2
  95. package/src/components/Checkbox/Checkbox.md +28 -0
  96. package/src/components/Checkbox/Checkbox.vue +9 -22
  97. package/src/components/Checkbox/stories/Group.vue +27 -0
  98. package/src/components/Checkbox/stories/HorizontalGroup.vue +24 -0
  99. package/src/components/Checkbox/stories/SettingRow.vue +23 -0
  100. package/src/components/Checkbox/stories/WithDescription.vue +27 -0
  101. package/src/components/Checkbox/types.ts +7 -0
  102. package/src/components/CodeEditor/CodeEditor.api.md +139 -0
  103. package/src/components/CodeEditor/CodeEditor.cy.ts +160 -0
  104. package/src/components/CodeEditor/CodeEditor.md +104 -0
  105. package/src/components/CodeEditor/CodeEditor.vue +556 -0
  106. package/src/components/CodeEditor/CodePreview.test.ts +49 -0
  107. package/src/components/CodeEditor/CodePreview.vue +45 -0
  108. package/src/components/CodeEditor/index.ts +17 -0
  109. package/src/components/CodeEditor/languages.ts +63 -0
  110. package/src/components/CodeEditor/stories/Default.vue +16 -0
  111. package/src/components/CodeEditor/stories/Labeling.vue +25 -0
  112. package/src/components/CodeEditor/stories/MaxHeight.vue +24 -0
  113. package/src/components/CodeEditor/stories/Preview.vue +36 -0
  114. package/src/components/CodeEditor/stories/Sizes.vue +15 -0
  115. package/src/components/CodeEditor/stories/Variants.vue +13 -0
  116. package/src/components/CodeEditor/theme.ts +272 -0
  117. package/src/components/CodeEditor/types.ts +69 -0
  118. package/src/components/Combobox/Combobox.api.md +19 -20
  119. package/src/components/Combobox/Combobox.cy.ts +54 -14
  120. package/src/components/Combobox/Combobox.md +4 -0
  121. package/src/components/Combobox/Combobox.vue +233 -247
  122. package/src/components/Combobox/ComboboxResults.vue +1 -1
  123. package/src/components/Combobox/stories/Clearable.vue +2 -2
  124. package/src/components/Combobox/stories/CreateNew.vue +1 -1
  125. package/src/components/Combobox/stories/Footer.vue +10 -2
  126. package/src/components/Combobox/stories/InDialog.vue +1 -1
  127. package/src/components/Combobox/types.ts +8 -29
  128. package/src/components/CommandPalette/CommandPaletteItem.vue +1 -1
  129. package/src/components/ContextMenu/ContextMenu.api.md +52 -0
  130. package/src/components/ContextMenu/ContextMenu.cy.ts +191 -0
  131. package/src/components/ContextMenu/ContextMenu.md +25 -0
  132. package/src/components/ContextMenu/ContextMenu.vue +106 -0
  133. package/src/components/ContextMenu/index.ts +2 -0
  134. package/src/components/ContextMenu/stories/FileList.vue +105 -0
  135. package/src/components/ContextMenu/stories/Groups.vue +95 -0
  136. package/src/components/ContextMenu/stories/Simple.vue +72 -0
  137. package/src/components/ContextMenu/types.ts +39 -0
  138. package/src/components/DatePicker/CalendarPanel.vue +128 -86
  139. package/src/components/DatePicker/DatePicker.vue +3 -9
  140. package/src/components/DatePicker/DateRangePicker.vue +4 -8
  141. package/src/components/DatePicker/DateTimePicker.cy.ts +4 -2
  142. package/src/components/DatePicker/DateTimePicker.vue +3 -9
  143. package/src/components/DatePicker/composables.ts +15 -37
  144. package/src/components/DatePicker/stories/Range.vue +1 -1
  145. package/src/components/DatePicker/types.ts +1 -1
  146. package/src/components/DesktopShell/DesktopShell.api.md +40 -0
  147. package/src/components/DesktopShell/DesktopShell.cy.ts +39 -0
  148. package/src/components/DesktopShell/DesktopShell.md +31 -0
  149. package/src/components/DesktopShell/DesktopShell.vue +81 -0
  150. package/src/components/DesktopShell/index.ts +3 -0
  151. package/src/components/DesktopShell/stories/Default.vue +97 -0
  152. package/src/components/DesktopShell/types.ts +2 -0
  153. package/src/components/Dialog/Dialog.api.md +7 -8
  154. package/src/components/Dialog/Dialog.md +4 -0
  155. package/src/components/Dialog/Dialog.vue +8 -7
  156. package/src/components/Dialog/stories/CommandPalette.vue +1 -1
  157. package/src/components/Dialog/stories/Wizard.vue +1 -1
  158. package/src/components/Dialogs.vue +11 -9
  159. package/src/components/Divider/Divider.api.md +1 -1
  160. package/src/components/Divider/Divider.md +4 -0
  161. package/src/components/Dropdown/Dropdown.api.md +10 -35
  162. package/src/components/Dropdown/Dropdown.md +10 -6
  163. package/src/components/Dropdown/Dropdown.vue +33 -47
  164. package/src/components/Dropdown/stories/{05_KebabMenu.vue → KebabMenu.vue} +3 -1
  165. package/src/components/Dropdown/stories/{06_UserMenu.vue → UserMenu.vue} +1 -1
  166. package/src/components/Dropdown/types.ts +25 -189
  167. package/src/components/Duration/Duration.api.md +92 -0
  168. package/src/components/Duration/Duration.cy.ts +183 -0
  169. package/src/components/Duration/Duration.md +98 -0
  170. package/src/components/Duration/Duration.vue +114 -0
  171. package/src/components/Duration/duration.ts +185 -0
  172. package/src/components/Duration/index.ts +8 -0
  173. package/src/components/Duration/stories/Default.vue +10 -0
  174. package/src/components/Duration/stories/Formats.vue +16 -0
  175. package/src/components/Duration/stories/RealWorld.vue +50 -0
  176. package/src/components/Duration/stories/Sizes.vue +15 -0
  177. package/src/components/Duration/stories/States.vue +21 -0
  178. package/src/components/Duration/types.ts +52 -0
  179. package/src/components/ErrorMessage/ErrorMessage.cy.ts +1 -1
  180. package/src/components/ErrorMessage/ErrorMessage.md +4 -0
  181. package/src/components/ErrorMessage/ErrorMessage.vue +1 -1
  182. package/src/components/FileUploader/FileUploader.api.md +36 -11
  183. package/src/components/FileUploader/FileUploader.cy.ts +66 -0
  184. package/src/components/FileUploader/FileUploader.md +4 -1
  185. package/src/components/FileUploader/FileUploader.vue +147 -106
  186. package/src/components/FileUploader/index.ts +6 -0
  187. package/src/components/FileUploader/types.ts +43 -0
  188. package/src/components/FormControl/FormControl.api.md +3 -4
  189. package/src/components/FormControl/FormControl.md +4 -0
  190. package/src/components/FormControl/stories/RealForm.vue +3 -3
  191. package/src/components/FormLabel.vue +5 -8
  192. package/src/components/HoverCard/HoverCard.api.md +100 -0
  193. package/src/components/HoverCard/HoverCard.cy.ts +103 -0
  194. package/src/components/HoverCard/HoverCard.md +52 -0
  195. package/src/components/HoverCard/HoverCard.vue +91 -0
  196. package/src/components/HoverCard/index.ts +2 -0
  197. package/src/components/HoverCard/stories/Arrow.vue +28 -0
  198. package/src/components/HoverCard/stories/Delays.vue +16 -0
  199. package/src/components/HoverCard/stories/Example.vue +28 -0
  200. package/src/components/HoverCard/types.ts +47 -0
  201. package/src/components/Icon/Icon.cy.ts +78 -0
  202. package/src/components/Icon/Icon.vue +50 -0
  203. package/src/components/Icon/index.ts +1 -0
  204. package/src/components/InputLabeling/InputDescription.vue +1 -1
  205. package/src/components/InputLabeling/InputError.vue +1 -1
  206. package/src/components/InputLabeling/InputLabel.vue +21 -7
  207. package/src/components/InputLabeling/RequiredIndicator.vue +1 -1
  208. package/src/components/ItemListRow/ItemListRow.vue +1 -1
  209. package/src/components/ItemListRow/stories/RowStates.vue +1 -1
  210. package/src/components/KeyboardShortcut.vue +4 -8
  211. package/src/components/KeyboardShortcutsModal/KeyboardShortcutsModal.api.md +22 -14
  212. package/src/components/KeyboardShortcutsModal/KeyboardShortcutsModal.vue +2 -2
  213. package/src/components/ListFilter/ListFilter.api.md +5 -6
  214. package/src/components/ListFilter/ListFilter.vue +161 -57
  215. package/src/components/ListFilter/NestedPopover.vue +17 -10
  216. package/src/components/ListFilter/SearchComplete.vue +31 -38
  217. package/src/components/ListItem.vue +1 -1
  218. package/src/components/ListView/ListEmptyState.vue +1 -1
  219. package/src/components/ListView/ListGroupHeader.vue +2 -2
  220. package/src/components/ListView/ListRow.vue +1 -1
  221. package/src/components/ListView/ListSelectBanner.vue +1 -1
  222. package/src/components/ListView/ListView.api.md +322 -6
  223. package/src/components/ListView/index.ts +25 -0
  224. package/src/components/ListView/stories/CustomList.vue +1 -1
  225. package/src/components/ListView/stories/GroupedRows.vue +1 -1
  226. package/src/components/LoadingIndicator.vue +13 -22
  227. package/src/components/LoadingText.vue +3 -13
  228. package/src/components/{Dropdown/DropdownMenuList.vue → Menu/Menu.vue} +110 -75
  229. package/src/components/{Dropdown/DropdownMenuItemContent.vue → Menu/MenuItemContent.vue} +23 -33
  230. package/src/components/{Dropdown/DropdownRenderContent.vue → Menu/MenuRenderContent.vue} +1 -1
  231. package/src/components/Menu/types.ts +225 -0
  232. package/src/components/{Dropdown → Menu}/utils.ts +36 -40
  233. package/src/components/MobileNav/MobileNav.api.md +70 -0
  234. package/src/components/MobileNav/MobileNav.cy.ts +89 -0
  235. package/src/components/MobileNav/MobileNav.md +34 -0
  236. package/src/components/MobileNav/MobileNav.vue +15 -0
  237. package/src/components/MobileNav/MobileNavItem.vue +125 -0
  238. package/src/components/MobileNav/index.ts +4 -0
  239. package/src/components/MobileNav/stories/Default.vue +40 -0
  240. package/src/components/MobileNav/types.ts +33 -0
  241. package/src/components/MobileShell/MobileShell.api.md +23 -0
  242. package/src/components/MobileShell/MobileShell.cy.ts +25 -0
  243. package/src/components/MobileShell/MobileShell.md +24 -0
  244. package/src/components/MobileShell/MobileShell.vue +41 -0
  245. package/src/components/MobileShell/index.ts +3 -0
  246. package/src/components/MobileShell/stories/Default.vue +75 -0
  247. package/src/components/MobileShell/types.ts +7 -0
  248. package/src/components/MonthPicker/MonthPicker.cy.ts +16 -0
  249. package/src/components/MonthPicker/MonthPicker.vue +19 -8
  250. package/src/components/MultiSelect/MultiSelect.api.md +3 -4
  251. package/src/components/MultiSelect/MultiSelect.md +42 -10
  252. package/src/components/MultiSelect/MultiSelect.vue +199 -227
  253. package/src/components/MultiSelect/MultiSelectResults.vue +1 -1
  254. package/src/components/MultiSelect/stories/Members.vue +1 -1
  255. package/src/components/MultiSelect/stories/TagsTrigger.vue +3 -3
  256. package/src/components/MultiSelect/types.ts +3 -3
  257. package/src/components/PageHeader/PageHeader.api.md +112 -0
  258. package/src/components/PageHeader/PageHeader.cy.ts +68 -0
  259. package/src/components/PageHeader/PageHeader.md +25 -0
  260. package/src/components/PageHeader/PageHeader.vue +13 -0
  261. package/src/components/PageHeader/PageHeaderBackButton.vue +29 -0
  262. package/src/components/PageHeader/PageHeaderBase.vue +51 -0
  263. package/src/components/PageHeader/PageHeaderMobile.vue +68 -0
  264. package/src/components/PageHeader/PageHeaderMobileTitle.vue +16 -0
  265. package/src/components/PageHeader/PageHeaderTarget.vue +13 -0
  266. package/src/components/PageHeader/PageHeaderTitle.vue +12 -0
  267. package/src/components/PageHeader/index.ts +7 -0
  268. package/src/components/PageHeader/stories/Default.vue +19 -0
  269. package/src/components/PageHeader/stories/Mobile.vue +24 -0
  270. package/src/components/PageHeader/target.ts +19 -0
  271. package/src/components/PageHeader/types.ts +19 -0
  272. package/src/components/Password/Password.md +4 -0
  273. package/src/components/Password/Password.vue +2 -2
  274. package/src/components/Popover/Popover.api.md +126 -42
  275. package/src/components/Popover/Popover.cy.ts +425 -41
  276. package/src/components/Popover/Popover.md +99 -4
  277. package/src/components/Popover/Popover.vue +288 -215
  278. package/src/components/Popover/index.ts +8 -1
  279. package/src/components/Popover/stories/Arrow.vue +16 -0
  280. package/src/components/Popover/stories/Bare.vue +20 -0
  281. package/src/components/Popover/stories/Click.vue +7 -4
  282. package/src/components/Popover/stories/Controlled.vue +22 -0
  283. package/src/components/Popover/stories/Dismissible.vue +19 -0
  284. package/src/components/Popover/stories/MatchTriggerWidth.vue +16 -0
  285. package/src/components/Popover/stories/SideAlign.vue +17 -0
  286. package/src/components/Popover/types.ts +101 -23
  287. package/src/components/Progress/Progress.cy.ts +2 -2
  288. package/src/components/Progress/Progress.md +4 -0
  289. package/src/components/Progress/Progress.vue +4 -4
  290. package/src/components/Rail/Rail.api.md +91 -0
  291. package/src/components/Rail/Rail.cy.ts +84 -0
  292. package/src/components/Rail/Rail.md +30 -0
  293. package/src/components/Rail/Rail.vue +16 -0
  294. package/src/components/Rail/RailItem.vue +134 -0
  295. package/src/components/Rail/RailItemBadge.vue +64 -0
  296. package/src/components/Rail/index.ts +2 -0
  297. package/src/components/Rail/stories/Default.vue +44 -0
  298. package/src/components/Rail/types.ts +37 -0
  299. package/src/components/Rating/Rating.md +4 -0
  300. package/src/components/Rating/Rating.vue +5 -8
  301. package/src/components/Rating/stories/CustomSlot.vue +2 -2
  302. package/src/components/ScrollArea/ScrollArea.api.md +59 -0
  303. package/src/components/ScrollArea/ScrollArea.vue +46 -0
  304. package/src/components/ScrollArea/ScrollBar.vue +101 -0
  305. package/src/components/ScrollArea/index.ts +2 -0
  306. package/src/components/Select/Select.cy.ts +16 -15
  307. package/src/components/Select/Select.md +4 -0
  308. package/src/components/Select/Select.vue +217 -203
  309. package/src/components/Select/stories/Example.vue +4 -3
  310. package/src/components/Select/stories/States.vue +1 -1
  311. package/src/components/Select/stories/TriggerSlots.vue +2 -2
  312. package/src/components/Select/utils.ts +4 -3
  313. package/src/components/SettingsDialog/SettingsBody.vue +23 -0
  314. package/src/components/SettingsDialog/SettingsContent.vue +7 -0
  315. package/src/components/SettingsDialog/SettingsDialog.api.md +273 -0
  316. package/src/components/SettingsDialog/SettingsDialog.cy.ts +126 -0
  317. package/src/components/SettingsDialog/SettingsDialog.md +43 -0
  318. package/src/components/SettingsDialog/SettingsDialog.vue +76 -0
  319. package/src/components/SettingsDialog/SettingsHeader.vue +36 -0
  320. package/src/components/SettingsDialog/SettingsNavGroup.vue +20 -0
  321. package/src/components/SettingsDialog/SettingsNavItem.vue +33 -0
  322. package/src/components/SettingsDialog/SettingsPanel.cy.ts +77 -0
  323. package/src/components/SettingsDialog/SettingsPanel.vue +26 -0
  324. package/src/components/SettingsDialog/SettingsRow.vue +52 -0
  325. package/src/components/SettingsDialog/SettingsSidebar.vue +16 -0
  326. package/src/components/SettingsDialog/index.ts +10 -0
  327. package/src/components/SettingsDialog/stories/Default.vue +99 -0
  328. package/src/components/SettingsDialog/stories/PanelBasic.vue +52 -0
  329. package/src/components/SettingsDialog/stories/panels/NotificationsPanel.vue +57 -0
  330. package/src/components/SettingsDialog/stories/panels/PreferencesPanel.vue +57 -0
  331. package/src/components/SettingsDialog/stories/panels/ProfilePanel.vue +38 -0
  332. package/src/components/SettingsDialog/stories/panels/UsersPanel.vue +83 -0
  333. package/src/components/SettingsDialog/types.ts +21 -0
  334. package/src/components/Sidebar/Sidebar.api.md +124 -65
  335. package/src/components/Sidebar/Sidebar.cy.ts +127 -76
  336. package/src/components/Sidebar/Sidebar.md +47 -3
  337. package/src/components/Sidebar/Sidebar.vue +64 -56
  338. package/src/components/Sidebar/SidebarCollapseToggle.vue +23 -0
  339. package/src/components/Sidebar/SidebarHeader.vue +76 -52
  340. package/src/components/Sidebar/SidebarItem.vue +159 -95
  341. package/src/components/Sidebar/SidebarItemIcon.vue +25 -0
  342. package/src/components/Sidebar/SidebarLabel.vue +34 -0
  343. package/src/components/Sidebar/SidebarSection.vue +29 -14
  344. package/src/components/Sidebar/index.ts +13 -2
  345. package/src/components/Sidebar/stories/Collapsed.vue +40 -0
  346. package/src/components/Sidebar/stories/Default.vue +153 -0
  347. package/src/components/Sidebar/stories/Legacy.vue +36 -0
  348. package/src/components/Sidebar/types.ts +105 -20
  349. package/src/components/Skeleton/Skeleton.cy.ts +21 -0
  350. package/src/components/Skeleton/Skeleton.md +21 -0
  351. package/src/components/Skeleton/Skeleton.vue +6 -0
  352. package/src/components/Skeleton/index.ts +1 -0
  353. package/src/components/Skeleton/stories/Card.vue +12 -0
  354. package/src/components/Skeleton/stories/List.vue +15 -0
  355. package/src/components/Skeleton/stories/Text.vue +11 -0
  356. package/src/components/Slider/Slider.api.md +1 -1
  357. package/src/components/Slider/Slider.cy.ts +29 -0
  358. package/src/components/Slider/Slider.md +10 -0
  359. package/src/components/Slider/Slider.vue +37 -9
  360. package/src/components/Slider/stories/NegativeValues.vue +12 -0
  361. package/src/components/Slider/types.ts +1 -1
  362. package/src/components/Spinner/Spinner.api.md +32 -0
  363. package/src/components/Spinner/Spinner.cy.ts +87 -0
  364. package/src/components/Spinner/Spinner.md +21 -0
  365. package/src/components/Spinner/Spinner.vue +168 -0
  366. package/src/components/Spinner/index.ts +2 -0
  367. package/src/components/Spinner/stories/InContext.vue +49 -0
  368. package/src/components/Spinner/stories/Sizes.vue +12 -0
  369. package/src/components/Spinner/stories/Themes.vue +10 -0
  370. package/src/components/Spinner/stories/Track.vue +12 -0
  371. package/src/components/Spinner/types.ts +14 -0
  372. package/src/components/Switch/Switch.md +4 -0
  373. package/src/components/Switch/Switch.vue +6 -23
  374. package/src/components/TabButtons/Pill.vue +161 -0
  375. package/src/components/TabButtons/TabButtons.api.md +52 -2
  376. package/src/components/TabButtons/TabButtons.cy.ts +18 -5
  377. package/src/components/TabButtons/TabButtons.md +49 -0
  378. package/src/components/TabButtons/TabButtons.vue +207 -109
  379. package/src/components/TabButtons/index.ts +11 -1
  380. package/src/components/TabButtons/pillTypes.ts +43 -0
  381. package/src/components/TabButtons/stories/PrefixSuffix.vue +65 -0
  382. package/src/components/TabButtons/stories/Sizes.vue +26 -0
  383. package/src/components/TabButtons/stories/Variants.vue +40 -0
  384. package/src/components/TabButtons/stories/Vertical.vue +64 -0
  385. package/src/components/TabButtons/types.ts +49 -0
  386. package/src/components/Tabs/Tabs.md +4 -0
  387. package/src/components/Tabs/Tabs.vue +1 -1
  388. package/src/components/TextEditor/TextEditor.cy.ts +1 -1
  389. package/src/components/TextEditor/TextEditor.md +3 -0
  390. package/src/components/TextEditor/TextEditor.vue +7 -4
  391. package/src/components/TextEditor/components/CodeBlockComponent.vue +4 -194
  392. package/src/components/TextEditor/components/FontColor.vue +1 -1
  393. package/src/components/TextEditor/components/LinkPopup.vue +1 -1
  394. package/src/components/TextEditor/components/MediaNodeView.vue +17 -17
  395. package/src/components/TextEditor/components/MentionList.vue +1 -1
  396. package/src/components/TextEditor/components/TextEditorBubbleMenu.vue +14 -5
  397. package/src/components/TextEditor/components/TextEditorFixedMenu.vue +3 -3
  398. package/src/components/TextEditor/components/{Menu.vue → TextEditorMenu.vue} +3 -3
  399. package/src/components/TextEditor/extensions/iframe/InsertIframe.vue +2 -2
  400. package/src/components/TextEditor/extensions/iframe/iframe-extension.ts +62 -19
  401. package/src/components/TextEditor/extensions/image-group/ImageGroupNodeView.vue +1 -1
  402. package/src/components/TextEditor/extensions/image-group/ImageGroupUploadDialog.vue +4 -4
  403. package/src/components/TextEditor/extensions/suggestion/SuggestionList.vue +1 -1
  404. package/src/components/TextEditor/extensions/toc-node/TocNodeView.vue +1 -1
  405. package/src/components/TextEditor/hljs-github.css +197 -0
  406. package/src/components/TextEditor/index.ts +13 -0
  407. package/src/components/TextInput/TextInput.cy.ts +8 -3
  408. package/src/components/TextInput/TextInput.md +4 -0
  409. package/src/components/TextInput/TextInput.vue +11 -15
  410. package/src/components/TextInput/stories/Slots.vue +1 -1
  411. package/src/components/Textarea/Textarea.cy.ts +1 -1
  412. package/src/components/Textarea/Textarea.md +4 -0
  413. package/src/components/Textarea/Textarea.vue +11 -15
  414. package/src/components/ThemeSwitcher/ThemePreview.vue +74 -0
  415. package/src/components/ThemeSwitcher/ThemeSwitcher.api.md +82 -0
  416. package/src/components/ThemeSwitcher/ThemeSwitcher.cy.ts +101 -0
  417. package/src/components/ThemeSwitcher/ThemeSwitcher.md +70 -0
  418. package/src/components/ThemeSwitcher/ThemeSwitcher.vue +145 -0
  419. package/src/components/ThemeSwitcher/index.ts +2 -0
  420. package/src/components/ThemeSwitcher/previews/PreviewWindow.vue +74 -0
  421. package/src/components/ThemeSwitcher/stories/Branded.vue +12 -0
  422. package/src/components/ThemeSwitcher/stories/Default.vue +7 -0
  423. package/src/components/ThemeSwitcher/stories/Menu.vue +53 -0
  424. package/src/components/ThemeSwitcher/stories/Toggle.vue +25 -0
  425. package/src/components/ThemeSwitcher/types.ts +35 -0
  426. package/src/components/TimePicker/TimePicker.api.md +67 -15
  427. package/src/components/TimePicker/TimePicker.cy.ts +130 -14
  428. package/src/components/TimePicker/TimePicker.md +6 -0
  429. package/src/components/TimePicker/TimePicker.vue +74 -19
  430. package/src/components/TimePicker/stories/Labeling.vue +21 -0
  431. package/src/components/TimePicker/stories/Range.vue +1 -1
  432. package/src/components/TimePicker/stories/SizesAndVariants.vue +23 -0
  433. package/src/components/TimePicker/stories/TwentyFour.vue +1 -1
  434. package/src/components/TimePicker/types.ts +15 -3
  435. package/src/components/TimePicker/utils.ts +82 -21
  436. package/src/components/Toast/Toast.md +1 -1
  437. package/src/components/Toast/Toast.sanitize.test.ts +50 -0
  438. package/src/components/Toast/Toast.test.ts +5 -0
  439. package/src/components/Toast/Toast.vue +7 -7
  440. package/src/components/Toast/ToastProvider.vue +18 -9
  441. package/src/components/Toast/stories/CustomIcon.vue +1 -1
  442. package/src/components/Toast/toast.ts +32 -8
  443. package/src/components/Tooltip/Tooltip.api.md +43 -5
  444. package/src/components/Tooltip/Tooltip.cy.ts +48 -0
  445. package/src/components/Tooltip/Tooltip.md +10 -0
  446. package/src/components/Tooltip/Tooltip.vue +20 -5
  447. package/src/components/Tooltip/TooltipBubble.vue +2 -2
  448. package/src/components/Tooltip/TooltipProvider.vue +53 -0
  449. package/src/components/Tooltip/index.ts +1 -0
  450. package/src/components/Tooltip/stories/Group.vue +22 -0
  451. package/src/components/Tooltip/stories/Slots.vue +2 -2
  452. package/src/components/Tooltip/types.ts +1 -1
  453. package/src/components/Tree/Tree.api.md +76 -18
  454. package/src/components/Tree/Tree.md +144 -2
  455. package/src/components/Tree/Tree.vue +276 -97
  456. package/src/components/Tree/TreeItem.vue +263 -0
  457. package/src/components/Tree/index.ts +9 -1
  458. package/src/components/Tree/stories/DragDrop.vue +94 -0
  459. package/src/components/Tree/stories/Example.vue +11 -34
  460. package/src/components/Tree/stories/ExpandAll.vue +48 -0
  461. package/src/components/Tree/stories/Guides.vue +49 -0
  462. package/src/components/Tree/stories/ListView.vue +74 -0
  463. package/src/components/Tree/tree.cy.ts +218 -90
  464. package/src/components/Tree/types.ts +114 -24
  465. package/src/components/Tree/useTreeDragDrop.ts +203 -0
  466. package/src/components/Tree/useTreeKeyboard.ts +109 -0
  467. package/src/components/VueGridLayout/Layout.vue +1 -1
  468. package/src/components/shared/picker/PickerShell.vue +20 -15
  469. package/src/components/shared/popover/PopoverPanel.vue +67 -0
  470. package/src/components/shared/popover/popoverPanel.css +102 -0
  471. package/src/components/shared/selection/utils.ts +6 -6
  472. package/src/composables/useAutofocusOnOpen.ts +2 -0
  473. package/src/composables/useScreenSize.ts +46 -0
  474. package/src/composables/useScrollContainer.ts +108 -0
  475. package/src/composables/useShortcut.ts +3 -0
  476. package/src/data-fetching/docStore.test.ts +74 -0
  477. package/src/data-fetching/docStore.ts +18 -7
  478. package/src/data-fetching/useCall/types.ts +1 -0
  479. package/src/data-fetching/useCall/useCall.test.ts +43 -0
  480. package/src/data-fetching/useCall/useCall.ts +17 -4
  481. package/src/data-fetching/useDoc/useDoc.test.ts +61 -2
  482. package/src/data-fetching/useDoc/useDoc.ts +35 -15
  483. package/src/data-fetching/useDoctype/useDoctype.ts +6 -5
  484. package/src/data-fetching/useFrappeFetch.ts +17 -4
  485. package/src/data-fetching/useList/types.ts +1 -0
  486. package/src/data-fetching/useList/useList.test.ts +40 -0
  487. package/src/data-fetching/useList/useList.ts +20 -13
  488. package/src/data-fetching/useNewDoc/useNewDoc.ts +9 -5
  489. package/src/directives/focus.ts +3 -3
  490. package/src/index.ts +105 -74
  491. package/src/mocks/handlers.ts +8 -0
  492. package/src/molecules/editor/Editor.cy.ts +6 -1
  493. package/src/molecules/editor/EditorBubbleMenu.vue +1 -1
  494. package/src/molecules/editor/EditorContent.vue +50 -9
  495. package/src/molecules/editor/EditorFixedMenu.vue +2 -1
  496. package/src/molecules/editor/EditorFloatingMenu.vue +1 -1
  497. package/src/molecules/editor/EditorTableMenu.vue +303 -0
  498. package/src/molecules/editor/MenuItems.vue +81 -69
  499. package/src/molecules/editor/commands.ts +10 -2
  500. package/src/molecules/editor/components/AttachmentNodeView.vue +115 -0
  501. package/src/molecules/editor/components/EditorDropZone.vue +105 -0
  502. package/src/molecules/editor/components/EditorPopover.vue +89 -0
  503. package/src/molecules/editor/components/MediaNodeView.vue +156 -49
  504. package/src/molecules/editor/components/MediaResizeHandles.vue +38 -0
  505. package/src/molecules/editor/components/MediaToolbar.vue +108 -54
  506. package/src/molecules/editor/components/TableContextMenu.vue +144 -0
  507. package/src/molecules/editor/components/UploadProgressIndicator.vue +33 -0
  508. package/src/molecules/editor/components/VideoControls.vue +208 -0
  509. package/src/molecules/editor/components/font-color/ColorSwatchGrid.vue +3 -5
  510. package/src/molecules/editor/components/font-color/fontColorController.ts +28 -23
  511. package/src/molecules/editor/components/font-color/swatches.ts +1 -1
  512. package/src/molecules/editor/components/media-node-view-controller.ts +4 -4
  513. package/src/molecules/editor/components/media-node-view-utils.test.ts +0 -13
  514. package/src/molecules/editor/components/media-node-view-utils.ts +0 -10
  515. package/src/molecules/editor/components/table-color/tableCellColorController.ts +80 -0
  516. package/src/molecules/editor/components/table-size-picker/TableSizePicker.vue +92 -0
  517. package/src/molecules/editor/components/table-size-picker/tableSizePickerController.ts +40 -0
  518. package/src/molecules/editor/composables/useEditorFileDrop.ts +117 -0
  519. package/src/molecules/editor/composables/useFloatingPopup.ts +46 -2
  520. package/src/molecules/editor/composables/useNodeViewResize.test.ts +60 -11
  521. package/src/molecules/editor/composables/useNodeViewResize.ts +80 -21
  522. package/src/molecules/editor/composables/useTableCellColorState.ts +78 -0
  523. package/src/molecules/editor/composables/useWindowFileDragging.ts +68 -0
  524. package/src/molecules/editor/extensions/attachment/attachment-engine.ts +250 -0
  525. package/src/molecules/editor/extensions/attachment/attachment-extension.ts +248 -0
  526. package/src/molecules/editor/extensions/attachment/attachment.flow.test.ts +80 -0
  527. package/src/molecules/editor/extensions/attachment/attachment.roundtrip.test.ts +50 -0
  528. package/src/molecules/editor/extensions/attachment/index.ts +5 -0
  529. package/src/molecules/editor/extensions/code-block/CodeBlockComponent.css +16 -26
  530. package/src/molecules/editor/extensions/code-block/CodeBlockComponent.vue +107 -22
  531. package/src/molecules/editor/extensions/content-paste/content-paste-extension.ts +5 -3
  532. package/src/molecules/editor/extensions/iframe/IframeInsertDialog.vue +34 -7
  533. package/src/molecules/editor/extensions/iframe/IframeNodeView.vue +54 -48
  534. package/src/molecules/editor/extensions/iframe/iframe-allowlist.ts +33 -4
  535. package/src/molecules/editor/extensions/iframe/iframe-commands.ts +49 -5
  536. package/src/molecules/editor/extensions/iframe/iframe-embed-utils.ts +30 -0
  537. package/src/molecules/editor/extensions/iframe/iframe-extension.ts +4 -4
  538. package/src/molecules/editor/extensions/iframe/iframeInsertDialogController.ts +12 -1
  539. package/src/molecules/editor/extensions/iframe/index.ts +4 -4
  540. package/src/molecules/editor/extensions/iframe/useIframeDialog.ts +25 -13
  541. package/src/molecules/editor/extensions/image/image-extension.ts +29 -11
  542. package/src/molecules/editor/extensions/image-group/ImageGroupGrid.vue +27 -11
  543. package/src/molecules/editor/extensions/image-group/ImageGroupGridCell.vue +55 -11
  544. package/src/molecules/editor/extensions/image-group/ImageGroupNodeView.vue +64 -31
  545. package/src/molecules/editor/extensions/image-group/ImageGroupUploadDialog.vue +84 -40
  546. package/src/molecules/editor/extensions/image-group/image-group-extension.ts +4 -3
  547. package/src/molecules/editor/extensions/image-group/useImageGroupDialog.ts +116 -17
  548. package/src/molecules/editor/extensions/link/LinkEditorPopup.vue +214 -71
  549. package/src/molecules/editor/extensions/link/link-click-plugin.ts +10 -15
  550. package/src/molecules/editor/extensions/link/link-commands.ts +24 -3
  551. package/src/molecules/editor/extensions/link/link-extension.ts +11 -2
  552. package/src/molecules/editor/extensions/link/link-popup-controller.test.ts +49 -0
  553. package/src/molecules/editor/extensions/link/link-popup-controller.ts +67 -2
  554. package/src/molecules/editor/extensions/link/link-shortcut-plugin.ts +7 -2
  555. package/src/molecules/editor/extensions/media-drop/media-drop-extension.ts +177 -0
  556. package/src/molecules/editor/extensions/shared/media-dimensions.ts +36 -3
  557. package/src/molecules/editor/extensions/shared/media-node-ops.ts +17 -0
  558. package/src/molecules/editor/extensions/shared/media-plugin.test.ts +60 -0
  559. package/src/molecules/editor/extensions/shared/media-plugin.ts +7 -37
  560. package/src/molecules/editor/extensions/shared/media-upload-engine.test.ts +39 -2
  561. package/src/molecules/editor/extensions/shared/media-upload-engine.ts +104 -10
  562. package/src/molecules/editor/extensions/shared/media-upload-state.ts +55 -0
  563. package/src/molecules/editor/extensions/shared/media-upload-types.ts +52 -1
  564. package/src/molecules/editor/extensions/shared/suggestion-helpers.ts +20 -0
  565. package/src/molecules/editor/extensions/shared/suggestion-types.ts +7 -0
  566. package/src/molecules/editor/extensions/shared/upload-types.ts +1 -0
  567. package/src/molecules/editor/extensions/slash-commands/slash-commands-extension.test.ts +38 -0
  568. package/src/molecules/editor/extensions/slash-commands/slash-commands-extension.ts +119 -55
  569. package/src/molecules/editor/extensions/suggestion/SuggestionList.vue +62 -22
  570. package/src/molecules/editor/extensions/suggestion/SuggestionListItem.vue +1 -1
  571. package/src/molecules/editor/extensions/suggestion/createSuggestionExtension.ts +4 -0
  572. package/src/molecules/editor/extensions/table/drag-scroll.ts +154 -0
  573. package/src/molecules/editor/extensions/table/table-cell-color.ts +188 -0
  574. package/src/molecules/editor/extensions/table/table-navigation.ts +560 -0
  575. package/src/molecules/editor/extensions/table/table-selection-overlay.ts +137 -0
  576. package/src/molecules/editor/extensions/toc-node/TocNodeView.vue +1 -1
  577. package/src/molecules/editor/extensions/toc-node/toc-render.ts +7 -3
  578. package/src/molecules/editor/extensions/video/video-extension.ts +72 -5
  579. package/src/molecules/editor/extensions.ts +176 -8
  580. package/src/molecules/editor/index.ts +9 -4
  581. package/src/molecules/editor/kits.test.ts +21 -1
  582. package/src/molecules/editor/kits.ts +56 -33
  583. package/src/molecules/editor/menu.test.ts +3 -1
  584. package/src/molecules/editor/menu.ts +180 -18
  585. package/src/molecules/editor/stories/Comment.vue +4 -4
  586. package/src/molecules/editor/stories/Inline.vue +1 -1
  587. package/src/molecules/editor/stories/Primitives.vue +1 -1
  588. package/src/molecules/editor/stories/RichText.vue +1 -1
  589. package/src/molecules/editor/style.css +80 -3
  590. package/src/molecules/editor/useEditor.test.ts +4 -0
  591. package/src/molecules/editor/useEditor.ts +7 -3
  592. package/src/molecules/list/List.cy.ts +626 -0
  593. package/src/molecules/list/List.vue +93 -0
  594. package/src/molecules/list/ListCell.vue +15 -0
  595. package/src/molecules/list/ListGroup.vue +40 -0
  596. package/src/molecules/list/ListHeader.vue +62 -0
  597. package/src/molecules/list/ListHeaderCell.vue +26 -0
  598. package/src/molecules/list/ListHeaderCellSort.vue +106 -0
  599. package/src/molecules/list/ListRow.vue +55 -0
  600. package/src/molecules/list/ListRowBase.vue +103 -0
  601. package/src/molecules/list/ListRows.vue +120 -0
  602. package/src/molecules/list/index.ts +24 -0
  603. package/src/molecules/list/list-context.ts +44 -0
  604. package/src/molecules/list/list.api.md +276 -0
  605. package/src/molecules/list/list.md +119 -0
  606. package/src/molecules/list/stories/Columns.vue +103 -0
  607. package/src/molecules/list/stories/Feed.vue +105 -0
  608. package/src/molecules/list/stories/Virtual.vue +31 -0
  609. package/src/molecules/list/style.css +173 -0
  610. package/src/molecules/list/types.ts +82 -0
  611. package/src/molecules/list/useVirtualRows.ts +80 -0
  612. package/src/resources/documentResource.d.ts +28 -0
  613. package/src/resources/documentResource.js +32 -4
  614. package/src/resources/documentResource.test.ts +111 -0
  615. package/src/resources/listResource.d.ts +33 -0
  616. package/src/resources/plugin.d.ts +7 -0
  617. package/src/resources/resources.d.ts +35 -0
  618. package/src/utils/{call.js → call.ts} +50 -9
  619. package/src/utils/config.ts +16 -1
  620. package/src/utils/confirmDialog.d.ts +17 -0
  621. package/src/utils/debounce.ts +6 -7
  622. package/src/utils/dialog.cy.ts +9 -9
  623. package/src/utils/fileSize.ts +36 -0
  624. package/src/utils/fileUploadHandler.ts +46 -6
  625. package/src/utils/frappeRequest.test.ts +79 -0
  626. package/src/utils/{frappeRequest.js → frappeRequest.ts} +76 -13
  627. package/src/utils/pageMeta.ts +1 -1
  628. package/src/utils/plugin.ts +58 -0
  629. package/src/utils/{request.js → request.ts} +28 -3
  630. package/src/utils/{socketio.js → socketio.ts} +11 -1
  631. package/src/utils/theme.ts +48 -57
  632. package/src/utils/useFileUpload.ts +80 -23
  633. package/tailwind/audit-token-drift.cjs +176 -0
  634. package/tailwind/colorPalette.js +69 -8
  635. package/tailwind/colors.json +1115 -397
  636. package/tailwind/figma-tokens-to-theme.js +364 -0
  637. package/tailwind/generated/colors.json +1217 -0
  638. package/tailwind/generated/effects.json +41 -0
  639. package/tailwind/generated/radius.json +20 -0
  640. package/tailwind/generated/typography.json +479 -0
  641. package/tailwind/migrate-tokens-v2.js +453 -0
  642. package/tailwind/migrate-tokens-v2.test.js +55 -0
  643. package/tailwind/plugin.js +220 -15
  644. package/tailwind/preset.js +15 -0
  645. package/tailwind/tokens.js +35 -37
  646. package/tsconfig.base.json +2 -2
  647. package/vitepress/components/Docs/CommandPalette.vue +214 -0
  648. package/vitepress/components/Docs/Demo.vue +60 -0
  649. package/vitepress/components/Docs/EmitsTable.vue +99 -0
  650. package/vitepress/components/Docs/MobileNavSheet.vue +148 -0
  651. package/vitepress/components/Docs/OnThisPage.vue +74 -0
  652. package/vitepress/components/Docs/PrevNextBtns.vue +58 -0
  653. package/vitepress/components/Docs/PropsTable.vue +185 -0
  654. package/vitepress/components/Docs/Search.vue +34 -0
  655. package/vitepress/components/Docs/Sidebar.vue +91 -0
  656. package/vitepress/components/Docs/SlotsTable.vue +99 -0
  657. package/vitepress/components/Docs/index.ts +13 -0
  658. package/vitepress/components/Docs/sidebarList.ts +33 -0
  659. package/vitepress/components/Layout.vue +58 -0
  660. package/vitepress/components/Navbar.vue +126 -0
  661. package/vitepress/components/index.ts +3 -0
  662. package/vitepress/composables/index.ts +3 -0
  663. package/vitepress/composables/useTheme.ts +39 -0
  664. package/vitepress/css/fonts/Inter-Italic.var.woff2 +0 -0
  665. package/vitepress/css/fonts/Inter.var.woff2 +0 -0
  666. package/vitepress/css/style.css +198 -0
  667. package/vitepress/index.d.ts +23 -0
  668. package/vitepress/index.node.ts +175 -0
  669. package/vitepress/index.ts +17 -0
  670. package/vitepress/plugins/colocatedComponentDocs.ts +164 -0
  671. package/vitepress/plugins/componentTransformer.ts +286 -0
  672. package/vitepress/plugins/index.ts +12 -0
  673. package/vitepress/state.ts +10 -0
  674. package/vitepress/theme/index.ts +21 -0
  675. package/frappe/telemetry/utils.ts +0 -15
  676. package/src/components/Badge/stories/Themes.vue +0 -10
  677. package/src/components/Badge/stories/Variants.vue +0 -10
  678. package/src/components/Button/stories/Icons.vue +0 -27
  679. package/src/components/Button/stories/Sizes.vue +0 -11
  680. package/src/components/Button/stories/Themes.vue +0 -10
  681. package/src/components/Button/stories/Variants.vue +0 -10
  682. package/src/components/Popover/stories/Hover.vue +0 -14
  683. package/src/components/Resource.vue +0 -21
  684. package/src/components/Sidebar/stories/Example.vue +0 -76
  685. package/src/components/Spinner.vue +0 -3
  686. package/src/components/TabButtons/TabButtons.story.vue +0 -22
  687. package/src/utils/plugin.js +0 -24
  688. /package/src/components/Dropdown/stories/{02_Shortcuts.vue → Shortcuts.vue} +0 -0
  689. /package/src/components/Dropdown/stories/{01_Simple.vue → Simple.vue} +0 -0
  690. /package/src/components/Dropdown/stories/{03_Submenus.vue → Submenus.vue} +0 -0
  691. /package/src/components/Dropdown/stories/{04_Switches.vue → Switches.vue} +0 -0
  692. /package/src/components/{Dropdown/DropdownRenderContentAsChild.vue → Menu/MenuRenderContentAsChild.vue} +0 -0
@@ -1,26 +1,26 @@
1
1
  <template>
2
2
  <Dialog
3
3
  v-model="open"
4
- :options="{ title: 'Insert Embed', size: 'md' }"
4
+ :options="{ title: dialogTitle, size: 'md' }"
5
5
  @close="$emit('close')"
6
6
  >
7
7
  <template #body-content>
8
8
  <div>
9
- <label class="mb-2 block text-sm font-medium text-ink-gray-7">
9
+ <label class="mb-2 block text-base text-ink-gray-5">
10
10
  URL or Embed Code
11
11
  </label>
12
12
  <Textarea
13
13
  ref="input"
14
14
  v-model="dialog.embedInput.value"
15
- placeholder="https://youtube.com/watch?v=… or <iframe src=…>"
15
+ :placeholder="placeholder"
16
16
  @keydown.enter.prevent="submit"
17
17
  />
18
- <p v-if="dialog.urlError.value" class="mt-1 text-sm text-ink-red-3">
18
+ <p v-if="dialog.urlError.value" class="mt-1 text-sm text-ink-red-6">
19
19
  {{ dialog.urlError.value }}
20
20
  </p>
21
21
  <p
22
22
  v-else-if="dialog.embedInput.value && dialog.isValidUrl.value"
23
- class="mt-1 text-sm text-ink-green-3"
23
+ class="mt-1 text-sm text-ink-green-6"
24
24
  >
25
25
  ✓ Valid {{ dialog.platformName.value }} URL
26
26
  </p>
@@ -35,7 +35,7 @@
35
35
  :disabled="!dialog.isValidUrl.value"
36
36
  @click="submit"
37
37
  >
38
- Insert Embed
38
+ {{ isEditing ? 'Update Embed' : 'Insert Embed' }}
39
39
  </Button>
40
40
  </div>
41
41
  </template>
@@ -49,10 +49,17 @@ import Dialog from '#components/Dialog/Dialog.vue'
49
49
  import Button from '#components/Button/Button.vue'
50
50
  import Textarea from '#components/Textarea/Textarea.vue'
51
51
  import { useIframeDialog } from './useIframeDialog'
52
+ import { platformByName } from './iframe-embed-utils'
52
53
 
53
54
  const props = defineProps<{
54
55
  modelValue: boolean
55
56
  editor: Editor
57
+ /** Edit mode: position resolver for the iframe node being edited. */
58
+ getReplacePos?: () => number | undefined
59
+ /** Prefill for edit mode. */
60
+ initialUrl?: string
61
+ /** Platform name (e.g. "YouTube") tailoring the title/placeholder. */
62
+ platform?: string
56
63
  }>()
57
64
 
58
65
  const emit = defineEmits<{
@@ -65,7 +72,27 @@ const open = computed({
65
72
  set: (value) => emit('update:modelValue', value),
66
73
  })
67
74
 
68
- const dialog = useIframeDialog(props.editor)
75
+ const isEditing = computed(() => !!props.getReplacePos)
76
+
77
+ const platformConfig = computed(() =>
78
+ props.platform ? platformByName(props.platform) : null,
79
+ )
80
+ const dialogTitle = computed(() => {
81
+ if (isEditing.value) return 'Edit Embed'
82
+ return platformConfig.value
83
+ ? `Embed ${platformConfig.value.name}`
84
+ : 'Insert Embed'
85
+ })
86
+ const placeholder = computed(
87
+ () =>
88
+ platformConfig.value?.example ??
89
+ 'https://youtube.com/watch?v=… or <iframe src=…>',
90
+ )
91
+
92
+ const dialog = useIframeDialog(props.editor, {
93
+ getReplacePos: props.getReplacePos,
94
+ initialUrl: props.initialUrl,
95
+ })
69
96
  const input = useTemplateRef<{ el?: HTMLTextAreaElement }>('input')
70
97
 
71
98
  function submit() {
@@ -1,10 +1,13 @@
1
1
  <script setup lang="ts">
2
- import { computed, ref, toRaw } from 'vue'
2
+ import { computed, ref, toRaw, watch } from 'vue'
3
3
  import { NodeViewWrapper, nodeViewProps } from '@tiptap/vue-3'
4
+ import MediaToolbar from '#molecules/editor/components/MediaToolbar.vue'
5
+ import MediaResizeHandles from '#molecules/editor/components/MediaResizeHandles.vue'
4
6
  import { useNodeViewEditable } from '#molecules/editor/composables/useNodeViewEditable'
5
7
  import { useNodeViewResize } from '#molecules/editor/composables/useNodeViewResize'
6
8
  import { safeGetPos } from '#molecules/editor/extensions/shared/node-view'
7
9
  import { IFRAME_SANDBOX } from './iframe-allowlist'
10
+ import { openIframeInsertDialog } from './iframeInsertDialogController'
8
11
  import type { IframeAlign } from './iframe-commands'
9
12
 
10
13
  const props = defineProps(nodeViewProps)
@@ -32,7 +35,8 @@ const aspectRatio = computed<number>(
32
35
  const frameStyle = computed(() => {
33
36
  const width = (props.node.attrs.width as number | null) ?? 640
34
37
  const height =
35
- (props.node.attrs.height as number | null) ?? Math.round(width * aspectRatio.value)
38
+ (props.node.attrs.height as number | null) ??
39
+ Math.round(width * aspectRatio.value)
36
40
  return { width: `${width}px`, height: `${height}px` }
37
41
  })
38
42
 
@@ -51,23 +55,48 @@ const { startResize } = useNodeViewResize(editor, {
51
55
  },
52
56
  minWidth: MIN_WIDTH,
53
57
  maxWidthPadding: EDITOR_PADDING,
58
+ // The iframe's committed size renders via the frameStyle `:style` binding.
59
+ mediaSizing: 'style',
54
60
  })
55
61
 
62
+ const showCaption = ref(Boolean(props.node.attrs.title))
63
+
64
+ // Re-sync when the title attr changes elsewhere (collab, undo, …).
65
+ watch(
66
+ () => props.node.attrs.title,
67
+ (title) => {
68
+ if (title) showCaption.value = true
69
+ },
70
+ )
71
+
56
72
  function selectIframe(): void {
57
73
  const pos = safeGetPos(() => props.getPos())
58
74
  if (pos === null) return
59
75
  editor.commands.setNodeSelection(pos)
60
76
  }
61
77
 
62
- function onResizeStart(event: MouseEvent): void {
78
+ function onResizeStart(event: PointerEvent, edge: 'left' | 'right'): void {
63
79
  selectIframe()
64
- startResize(event)
80
+ startResize(event, edge)
65
81
  }
66
82
 
67
83
  function setAlignment(align: IframeAlign): void {
68
84
  props.updateAttributes({ align })
69
85
  }
70
86
 
87
+ function toggleCaption(): void {
88
+ showCaption.value = !showCaption.value
89
+ if (!showCaption.value) props.updateAttributes({ title: '' })
90
+ }
91
+
92
+ function changeEmbedLink(): void {
93
+ openIframeInsertDialog({
94
+ editor,
95
+ getReplacePos: () => safeGetPos(() => props.getPos()) ?? undefined,
96
+ initialUrl: (props.node.attrs.src as string | null) ?? undefined,
97
+ })
98
+ }
99
+
71
100
  function setCursorAt(pos: number): void {
72
101
  editor.commands.focus()
73
102
  editor.chain().setTextSelection(pos).scrollIntoView().run()
@@ -111,7 +140,7 @@ function commitCaption(event: Event): void {
111
140
  <NodeViewWrapper>
112
141
  <div
113
142
  ref="containerRef"
114
- class="relative my-6 block max-w-full overflow-hidden rounded-lg not-prose focus:outline-none"
143
+ class="relative isolate my-6 block max-w-full overflow-hidden rounded not-prose focus:outline-none"
115
144
  :class="[
116
145
  { 'ring-2 ring-outline-gray-3 ring-offset-2': selected },
117
146
  node.attrs.align === 'center' ? 'mx-auto' : '',
@@ -129,7 +158,7 @@ function commitCaption(event: Event): void {
129
158
  <iframe
130
159
  v-if="node.attrs.src"
131
160
  ref="iframeRef"
132
- class="block h-auto max-w-full rounded-lg border-0"
161
+ class="block h-auto max-w-full rounded border-0"
133
162
  :class="{ 'pointer-events-none': overlayActive }"
134
163
  :src="node.attrs.src"
135
164
  :style="frameStyle"
@@ -149,52 +178,29 @@ function commitCaption(event: Event): void {
149
178
  @click.stop="selectIframe"
150
179
  ></div>
151
180
 
152
- <!-- Controls overlay -->
153
- <div class="absolute bottom-2 right-2 z-20 flex items-center gap-2">
154
- <div
155
- v-if="selected && isEditable"
156
- class="flex divide-x divide-ink-gray-6 rounded-md bg-black/65"
157
- >
158
- <button
159
- class="px-1.5 py-1 text-ink-gray-4 transition-colors duration-150 hover:text-white"
160
- :class="{ 'text-white': node.attrs.align === 'left' }"
161
- title="Align Left"
162
- @click.stop="setAlignment('left')"
163
- >
164
- <span class="lucide-align-left size-4" />
165
- </button>
166
- <button
167
- class="px-1.5 py-1 text-ink-gray-4 transition-colors duration-150 hover:text-white"
168
- :class="{ 'text-white': node.attrs.align === 'center' }"
169
- title="Align Center"
170
- @click.stop="setAlignment('center')"
171
- >
172
- <span class="lucide-align-center size-4" />
173
- </button>
174
- <button
175
- class="px-1.5 py-1 text-ink-gray-4 transition-colors duration-150 hover:text-white"
176
- :class="{ 'text-white': node.attrs.align === 'right' }"
177
- title="Align Right"
178
- @click.stop="setAlignment('right')"
179
- >
180
- <span class="lucide-align-right size-4" />
181
- </button>
182
- </div>
181
+ <!-- Shared media toolbar (caption / change link / align) -->
182
+ <MediaToolbar
183
+ v-if="node.attrs.src"
184
+ :node="node"
185
+ media-type="embed"
186
+ :is-editable="isEditable"
187
+ :selected="selected"
188
+ :show-caption="showCaption"
189
+ @toggle-caption="toggleCaption"
190
+ @set-align="setAlignment"
191
+ @replace="changeEmbedLink"
192
+ />
183
193
 
184
- <button
185
- v-if="selected && isEditable"
186
- class="cursor-nw-resize rounded-md bg-black/65 p-1"
187
- title="Resize"
188
- @mousedown.prevent="onResizeStart"
189
- >
190
- <span class="lucide-move-diagonal-2 size-4 text-white" />
191
- </button>
192
- </div>
194
+ <MediaResizeHandles
195
+ v-if="selected && isEditable"
196
+ label="Resize embed"
197
+ @resize-start="onResizeStart"
198
+ />
193
199
 
194
200
  <!-- Placeholder while no src is set -->
195
201
  <div
196
202
  v-if="!node.attrs.src"
197
- class="flex h-[360px] w-[640px] items-center justify-center rounded-lg bg-surface-gray-1"
203
+ class="flex h-[360px] w-[640px] items-center justify-center rounded bg-surface-gray-1"
198
204
  >
199
205
  <div class="text-center text-ink-gray-5">
200
206
  <div class="mb-1 text-lg">🔗</div>
@@ -205,7 +211,7 @@ function commitCaption(event: Event): void {
205
211
 
206
212
  <!-- Caption input (commits on blur / Enter) -->
207
213
  <input
208
- v-if="(isEditable || node.attrs.title) && node.attrs.src"
214
+ v-if="(node.attrs.title || showCaption) && node.attrs.src"
209
215
  :value="node.attrs.title"
210
216
  class="mt-2 h-7 w-full border-0 bg-transparent text-center text-sm text-ink-gray-6 placeholder-ink-gray-4 focus:outline-none focus:ring-0 disabled:opacity-60"
211
217
  placeholder="Add caption"
@@ -8,6 +8,7 @@
8
8
  * (absolute + http/https only) and {@link matchesHostname} (exact host OR
9
9
  * dot-boundary suffix — never substring).
10
10
  */
11
+ import type { Editor } from '@tiptap/core'
11
12
  import {
12
13
  isSafeUrl,
13
14
  matchesHostname,
@@ -38,12 +39,40 @@ export const IFRAME_ALLOWLIST: readonly string[] = [
38
39
  /**
39
40
  * The sandbox attribute applied to every embedded iframe.
40
41
  *
41
- * Deliberately WITHOUT `allow-same-origin`: the embed must not be able to read
42
- * cookies / localStorage for our origin. Scripts, popups, forms, and
43
- * presentation (fullscreen) are permitted so the common embeds still work.
42
+ * Includes `allow-same-origin`: this lets the embed keep ITS OWN origin
43
+ * (youtube.com etc.) so its scripts can use their own cookies/storage —
44
+ * without it, YouTube's player throws SecurityError and renders a black box.
45
+ * It does NOT grant the iframe our origin; cross-origin isolation from the
46
+ * host still applies. The `allow-scripts + allow-same-origin` sandbox-escape
47
+ * concern only exists for content served from OUR origin, which the
48
+ * allowlist precludes (absolute http(s) URLs on external hosts only).
44
49
  */
45
50
  export const IFRAME_SANDBOX =
46
- 'allow-scripts allow-popups allow-forms allow-presentation' as const
51
+ 'allow-scripts allow-same-origin allow-popups allow-forms allow-presentation' as const
52
+
53
+ /**
54
+ * The allowlist override configured on the editor's iframe extension, or
55
+ * `undefined` when the extension is absent / uses the default list.
56
+ */
57
+ export function getIframeAllowlist(
58
+ editor: Editor,
59
+ ): readonly string[] | undefined {
60
+ return editor.extensionManager?.extensions.find(
61
+ (extension) => extension.name === 'iframe',
62
+ )?.options.allowlist as readonly string[] | undefined
63
+ }
64
+
65
+ /**
66
+ * True iff any of `hosts` would pass the editor's iframe allowlist — used to
67
+ * prune per-platform embed shortcuts when a custom allowlist excludes them.
68
+ */
69
+ export function allowlistPermitsHosts(
70
+ editor: Editor,
71
+ hosts: readonly string[],
72
+ ): boolean {
73
+ const allowlist = getIframeAllowlist(editor) ?? IFRAME_ALLOWLIST
74
+ return hosts.some((host) => matchesHostname(host, allowlist))
75
+ }
47
76
 
48
77
  /** Options for {@link validateIframeUrl}. */
49
78
  export interface ValidateIframeUrlOptions {
@@ -31,8 +31,18 @@ declare module '@tiptap/core' {
31
31
  insertIframeURL: (url: string) => ReturnType
32
32
  /** Update the selected iframe's alignment. */
33
33
  setIframeAlign: (align: IframeAlign) => ReturnType
34
- /** Open the insert-embed dialog (mounts it via the controller). */
35
- openIframeDialog: () => ReturnType
34
+ /**
35
+ * Open the insert-embed dialog (mounts it via the controller).
36
+ * `platform` (a `PLATFORM_CONFIGS` name, e.g. "YouTube") tailors the
37
+ * dialog title/placeholder; the dialog still accepts any supported URL.
38
+ */
39
+ openIframeDialog: (platform?: string) => ReturnType
40
+ }
41
+ // Separate group: the legacy TextEditor also augments `Commands.iframe`,
42
+ // and a divergent second declaration of a shared group is a TS2717 error.
43
+ iframeEdit: {
44
+ /** Swap the src of the iframe node at `pos` (keeps caption/align). */
45
+ updateIframeAt: (pos: number, url: string) => ReturnType
36
46
  }
37
47
  }
38
48
  }
@@ -78,11 +88,45 @@ export function buildIframeCommands(
78
88
  commands.setIframe({ src: url })
79
89
 
80
90
  const openIframeDialog: RawCommands['openIframeDialog'] =
81
- () =>
91
+ (platform?: string) =>
82
92
  ({ editor }) => {
83
- openIframeInsertDialog({ editor })
93
+ openIframeInsertDialog({ editor, platform })
94
+ return true
95
+ }
96
+
97
+ const updateIframeAt: RawCommands['updateIframeAt'] =
98
+ (pos: number, url: string) =>
99
+ ({ editor, tr, dispatch }) => {
100
+ const node = editor.state.doc.nodeAt(pos)
101
+ if (!node || node.type.name !== nodeName) return false
102
+ const processedSrc = processEmbedUrl(url)
103
+ if (!validateIframeUrl(processedSrc, { allowlist })) return false
104
+ // Same src → keep the user's sizing; new src → re-derive platform dims.
105
+ let { width, height } = node.attrs as {
106
+ width: number | null
107
+ height: number | null
108
+ }
109
+ if (processedSrc !== node.attrs.src || !width || !height) {
110
+ const editorWidth = editor.view.dom.clientWidth || 800
111
+ ;({ width, height } = getOptimalDimensions(processedSrc, editorWidth))
112
+ }
113
+ if (dispatch) {
114
+ tr.setNodeMarkup(pos, undefined, {
115
+ ...node.attrs,
116
+ src: processedSrc,
117
+ width,
118
+ height,
119
+ aspectRatio: height / width,
120
+ })
121
+ }
84
122
  return true
85
123
  }
86
124
 
87
- return { setIframeAlign, setIframe, insertIframeURL, openIframeDialog }
125
+ return {
126
+ setIframeAlign,
127
+ setIframe,
128
+ insertIframeURL,
129
+ openIframeDialog,
130
+ updateIframeAt,
131
+ }
88
132
  }
@@ -32,6 +32,13 @@ export interface PlatformConfig {
32
32
  defaultWidth: number
33
33
  /** Hostnames (exact or dot-boundary suffix) this platform owns. */
34
34
  hosts: readonly string[]
35
+ /**
36
+ * Slash-menu glyph. Lucide ships no brand icons, so these are semantic
37
+ * stand-ins (play for video platforms, frame for Figma, …).
38
+ */
39
+ icon: string
40
+ /** Example URL shown as the dialog placeholder for this platform. */
41
+ example: string
35
42
  }
36
43
 
37
44
  /** Per-platform sizing + host ownership. */
@@ -41,45 +48,68 @@ export const PLATFORM_CONFIGS: readonly PlatformConfig[] = [
41
48
  ratio: 9 / 16,
42
49
  defaultWidth: 640,
43
50
  hosts: ['youtube.com', 'youtu.be', 'youtube-nocookie.com'],
51
+ icon: 'lucide-square-play',
52
+ example: 'https://youtube.com/watch?v=…',
44
53
  },
45
54
  {
46
55
  name: 'Vimeo',
47
56
  ratio: 9 / 16,
48
57
  defaultWidth: 640,
49
58
  hosts: ['vimeo.com', 'player.vimeo.com'],
59
+ icon: 'lucide-circle-play',
60
+ example: 'https://vimeo.com/…',
50
61
  },
51
62
  {
52
63
  name: 'CodePen',
53
64
  ratio: 3 / 2,
54
65
  defaultWidth: 500,
55
66
  hosts: ['codepen.io'],
67
+ icon: 'lucide-code-xml',
68
+ example: 'https://codepen.io/user/pen/…',
56
69
  },
57
70
  {
58
71
  name: 'CodeSandbox',
59
72
  ratio: 3 / 2,
60
73
  defaultWidth: 500,
61
74
  hosts: ['codesandbox.io'],
75
+ icon: 'lucide-box',
76
+ example: 'https://codesandbox.io/s/…',
62
77
  },
63
78
  {
64
79
  name: 'Figma',
65
80
  ratio: 9 / 16,
66
81
  defaultWidth: 800,
67
82
  hosts: ['figma.com', 'embed.figma.com'],
83
+ icon: 'lucide-frame',
84
+ example: 'https://figma.com/design/…',
68
85
  },
69
86
  {
70
87
  name: 'Google Docs',
71
88
  ratio: 4 / 3,
72
89
  defaultWidth: 600,
73
90
  hosts: ['docs.google.com', 'drive.google.com'],
91
+ icon: 'lucide-file-text',
92
+ example: 'https://docs.google.com/document/d/…',
74
93
  },
75
94
  {
76
95
  name: 'Notion',
77
96
  ratio: 4 / 3,
78
97
  defaultWidth: 600,
79
98
  hosts: ['notion.so'],
99
+ icon: 'lucide-notebook-text',
100
+ example: 'https://notion.so/…',
80
101
  },
81
102
  ]
82
103
 
104
+ /** Resolve a platform config by its display name (case-insensitive). */
105
+ export function platformByName(name: string): PlatformConfig | null {
106
+ const lower = name.toLowerCase()
107
+ return (
108
+ PLATFORM_CONFIGS.find((config) => config.name.toLowerCase() === lower) ??
109
+ null
110
+ )
111
+ }
112
+
83
113
  /** Resolve the platform config for a URL, or null when unknown/unparseable. */
84
114
  export function detectPlatform(url: string): PlatformConfig | null {
85
115
  let host: string
@@ -8,8 +8,8 @@
8
8
  * - `src` is validated against the allowlist on LOAD (`parseHTML` `getAttrs`)
9
9
  * and on insert/paste — substring matching and the `startsWith('/')` escape
10
10
  * hatch are gone (see `iframe-allowlist.ts`).
11
- * - the default `sandbox` no longer includes `allow-same-origin`
12
- * ({@link IFRAME_SANDBOX}); rendering always forces that value.
11
+ * - rendering always forces the {@link IFRAME_SANDBOX} value, so incoming
12
+ * markup can never widen the sandbox beyond it.
13
13
  */
14
14
  import { Node, mergeAttributes } from '@tiptap/core'
15
15
  import { VueNodeViewRenderer } from '@tiptap/vue-3'
@@ -106,8 +106,8 @@ export const IframeExtension = Node.create<IframeOptions>({
106
106
  },
107
107
  sandbox: {
108
108
  default: IFRAME_SANDBOX,
109
- // Never let incoming HTML widen the sandbox: the strict default always
110
- // wins, so attacker markup cannot re-add `allow-same-origin` on load.
109
+ // Never let incoming HTML widen the sandbox: the fixed default always
110
+ // wins, so attacker markup cannot add extra capabilities on load.
111
111
  parseHTML: () => IFRAME_SANDBOX,
112
112
  renderHTML: () => ({ sandbox: IFRAME_SANDBOX }),
113
113
  },
@@ -18,7 +18,15 @@ function teardown() {
18
18
  container = null
19
19
  }
20
20
 
21
- export function openIframeInsertDialog(args: { editor: Editor }): void {
21
+ export function openIframeInsertDialog(args: {
22
+ editor: Editor
23
+ /** Edit mode: position resolver for the iframe node being edited. */
24
+ getReplacePos?: () => number | undefined
25
+ /** Prefill for edit mode (the node's current src). */
26
+ initialUrl?: string
27
+ /** Platform name (e.g. "YouTube") tailoring the dialog title/placeholder. */
28
+ platform?: string
29
+ }): void {
22
30
  teardown()
23
31
 
24
32
  const open = ref(true)
@@ -35,6 +43,9 @@ export function openIframeInsertDialog(args: { editor: Editor }): void {
35
43
  if (!value) setTimeout(teardown, 0)
36
44
  },
37
45
  editor: args.editor,
46
+ getReplacePos: args.getReplacePos,
47
+ initialUrl: args.initialUrl,
48
+ platform: args.platform,
38
49
  onClose: () => setTimeout(teardown, 0),
39
50
  })
40
51
  },
@@ -6,17 +6,17 @@ export type {
6
6
  } from './iframe-extension'
7
7
  export {
8
8
  detectPlatform,
9
+ platformByName,
9
10
  calculateAspectRatio,
10
11
  getOptimalDimensions,
11
12
  processEmbedUrl,
12
13
  PLATFORM_CONFIGS,
13
14
  } from './iframe-embed-utils'
14
- export type {
15
- AspectRatioInfo,
16
- PlatformConfig,
17
- } from './iframe-embed-utils'
15
+ export type { AspectRatioInfo, PlatformConfig } from './iframe-embed-utils'
18
16
  export {
19
17
  IFRAME_ALLOWLIST,
20
18
  IFRAME_SANDBOX,
21
19
  validateIframeUrl,
20
+ getIframeAllowlist,
21
+ allowlistPermitsHosts,
22
22
  } from './iframe-allowlist'
@@ -11,7 +11,7 @@
11
11
  */
12
12
  import { computed, ref, watch, type Ref } from 'vue'
13
13
  import type { Editor } from '@tiptap/core'
14
- import { validateIframeUrl } from './iframe-allowlist'
14
+ import { getIframeAllowlist, validateIframeUrl } from './iframe-allowlist'
15
15
  import {
16
16
  calculateAspectRatio,
17
17
  detectPlatform,
@@ -28,10 +28,15 @@ function extractSrc(input: string): string {
28
28
  return trimmed
29
29
  }
30
30
 
31
- function getAllowlist(editor: Editor): readonly string[] | undefined {
32
- return editor.extensionManager?.extensions.find(
33
- (extension) => extension.name === 'iframe',
34
- )?.options.allowlist as readonly string[] | undefined
31
+ export interface UseIframeDialogArgs {
32
+ /**
33
+ * Edit mode: resolve the position of the iframe node being edited. The
34
+ * dialog then swaps that node's src in place (via `updateIframeAt`) instead
35
+ * of inserting. Resolved at submit time so doc changes can't stale the pos.
36
+ */
37
+ getReplacePos?: () => number | undefined
38
+ /** Prefill for edit mode (the node's current src). */
39
+ initialUrl?: string
35
40
  }
36
41
 
37
42
  export interface UseIframeDialog {
@@ -47,9 +52,12 @@ export interface UseIframeDialog {
47
52
  insert: () => boolean
48
53
  }
49
54
 
50
- export function useIframeDialog(editor: Editor): UseIframeDialog {
51
- const allowlist = getAllowlist(editor)
52
- const embedInput = ref('')
55
+ export function useIframeDialog(
56
+ editor: Editor,
57
+ args: UseIframeDialogArgs = {},
58
+ ): UseIframeDialog {
59
+ const allowlist = getIframeAllowlist(editor)
60
+ const embedInput = ref(args.initialUrl ?? '')
53
61
  const urlError = ref('')
54
62
  const width = ref(640)
55
63
  const height = ref(360)
@@ -99,11 +107,15 @@ export function useIframeDialog(editor: Editor): UseIframeDialog {
99
107
 
100
108
  function insert(): boolean {
101
109
  if (!isValidUrl.value) return false
102
- const success = editor.commands.setIframe({
103
- src: processedUrl.value,
104
- width: width.value,
105
- height: height.value,
106
- })
110
+ const replacePos = args.getReplacePos?.()
111
+ const success =
112
+ replacePos != null
113
+ ? editor.commands.updateIframeAt(replacePos, processedUrl.value)
114
+ : editor.commands.setIframe({
115
+ src: processedUrl.value,
116
+ width: width.value,
117
+ height: height.value,
118
+ })
107
119
  if (success) {
108
120
  editor.commands.focus()
109
121
  } else {