@widgetic/editor 3.11.4 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (497) hide show
  1. package/README.md +245 -0
  2. package/dist/assets/fonts.json +331 -0
  3. package/dist/assets/icons/ic-browser/audio.svg +7 -0
  4. package/dist/assets/icons/ic-browser/document.svg +11 -0
  5. package/dist/assets/icons/ic-browser/error.svg +5 -0
  6. package/dist/assets/icons/ic-browser/folder.svg +28 -0
  7. package/dist/assets/icons/ic-browser/player-pause.svg +1 -0
  8. package/dist/assets/icons/ic-browser/player-play.svg +1 -0
  9. package/dist/assets/icons/ic-browser/trash.svg +1 -0
  10. package/dist/assets/icons/ic-fixed-position/background-deselected.svg +3 -0
  11. package/dist/assets/icons/ic-fixed-position/background-selected.svg +3 -0
  12. package/dist/assets/icons/ic-fixed-position/bottom-left-background-deselected.svg +3 -0
  13. package/dist/assets/icons/ic-fixed-position/bottom-left-background-selected.svg +3 -0
  14. package/dist/assets/icons/ic-fixed-position/bottom-left-icon.svg +4 -0
  15. package/dist/assets/icons/ic-fixed-position/bottom-right-background-deselected.svg +3 -0
  16. package/dist/assets/icons/ic-fixed-position/bottom-right-background-selected.svg +3 -0
  17. package/dist/assets/icons/ic-fixed-position/bottom-right-icon.svg +4 -0
  18. package/dist/assets/icons/ic-fixed-position/center-bottom-icon.svg +5 -0
  19. package/dist/assets/icons/ic-fixed-position/center-center-icon.svg +3 -0
  20. package/dist/assets/icons/ic-fixed-position/center-left-icon.svg +5 -0
  21. package/dist/assets/icons/ic-fixed-position/center-right-icon.svg +5 -0
  22. package/dist/assets/icons/ic-fixed-position/center-top-icon.svg +5 -0
  23. package/dist/assets/icons/ic-fixed-position/top-left-background-deselected.svg +3 -0
  24. package/dist/assets/icons/ic-fixed-position/top-left-background-selected.svg +3 -0
  25. package/dist/assets/icons/ic-fixed-position/top-left-icon.svg +4 -0
  26. package/dist/assets/icons/ic-fixed-position/top-right-background-deselected.svg +3 -0
  27. package/dist/assets/icons/ic-fixed-position/top-right-background-selected.svg +3 -0
  28. package/dist/assets/icons/ic-fixed-position/top-right-icon.svg +4 -0
  29. package/dist/assets/icons/trash.svg +1 -0
  30. package/dist/components/MockWidget.svelte +108 -0
  31. package/dist/components/MockWidget.svelte.d.ts +22 -0
  32. package/dist/components/WidgetPreview.svelte +852 -0
  33. package/dist/components/WidgetPreview.svelte.d.ts +100 -0
  34. package/dist/components/color-select/ColorSelect.svelte +258 -0
  35. package/dist/components/color-select/ColorSelect.svelte.d.ts +42 -0
  36. package/dist/components/color-select/color.d.ts +25 -0
  37. package/dist/components/color-select/color.js +21 -0
  38. package/dist/components/color-select/constants.d.ts +5 -0
  39. package/dist/components/color-select/constants.js +5 -0
  40. package/dist/components/color-select/render.d.ts +5 -0
  41. package/dist/components/color-select/render.js +77 -0
  42. package/dist/components/color-select/shaders.d.ts +3 -0
  43. package/dist/components/color-select/shaders.js +8 -0
  44. package/dist/components/editors/EmbedEditor.svelte +700 -0
  45. package/dist/components/editors/EmbedEditor.svelte.d.ts +50 -0
  46. package/dist/components/editors/PropsEditor.svelte +4009 -0
  47. package/dist/components/editors/PropsEditor.svelte.d.ts +62 -0
  48. package/dist/components/editors/props-editor.css +73 -0
  49. package/dist/components/ic/AngleInputController.svelte +276 -0
  50. package/dist/components/ic/AngleInputController.svelte.d.ts +26 -0
  51. package/dist/components/ic/BrowserInputController.svelte +731 -0
  52. package/dist/components/ic/BrowserInputController.svelte.d.ts +39 -0
  53. package/dist/components/ic/CheckboxInputController.svelte +203 -0
  54. package/dist/components/ic/CheckboxInputController.svelte.d.ts +41 -0
  55. package/dist/components/ic/ColorPickerInputController.svelte +1584 -0
  56. package/dist/components/ic/ColorPickerInputController.svelte.d.ts +50 -0
  57. package/dist/components/ic/DateFieldInputController.svelte +453 -0
  58. package/dist/components/ic/DateFieldInputController.svelte.d.ts +35 -0
  59. package/dist/components/ic/DatePickerInputController.svelte +364 -0
  60. package/dist/components/ic/DatePickerInputController.svelte.d.ts +28 -0
  61. package/dist/components/ic/DropdownInputController.svelte +226 -0
  62. package/dist/components/ic/DropdownInputController.svelte.d.ts +43 -0
  63. package/dist/components/ic/FixedPositionInputController.svelte +353 -0
  64. package/dist/components/ic/FixedPositionInputController.svelte.d.ts +23 -0
  65. package/dist/components/ic/FontInputController.svelte +2098 -0
  66. package/dist/components/ic/FontInputController.svelte.d.ts +47 -0
  67. package/dist/components/ic/InputControllerHeader.svelte +147 -0
  68. package/dist/components/ic/InputControllerHeader.svelte.d.ts +23 -0
  69. package/dist/components/ic/NumberControls.svelte +299 -0
  70. package/dist/components/ic/NumberControls.svelte.d.ts +30 -0
  71. package/dist/components/ic/NumberInputController.svelte +397 -0
  72. package/dist/components/ic/NumberInputController.svelte.d.ts +30 -0
  73. package/dist/components/ic/PositionInputController.svelte +252 -0
  74. package/dist/components/ic/PositionInputController.svelte.d.ts +37 -0
  75. package/dist/components/ic/RadioGroupInputController.svelte +162 -0
  76. package/dist/components/ic/RadioGroupInputController.svelte.d.ts +38 -0
  77. package/dist/components/ic/RangeCalendarInputController.svelte +262 -0
  78. package/dist/components/ic/RangeCalendarInputController.svelte.d.ts +32 -0
  79. package/dist/components/ic/RoundedCornersInputController.svelte +469 -0
  80. package/dist/components/ic/RoundedCornersInputController.svelte.d.ts +37 -0
  81. package/dist/components/ic/SliderInputController.svelte +411 -0
  82. package/dist/components/ic/SliderInputController.svelte.d.ts +46 -0
  83. package/dist/components/ic/SliderRangeInputController.svelte +155 -0
  84. package/dist/components/ic/SliderRangeInputController.svelte.d.ts +33 -0
  85. package/dist/components/ic/SwitchInputController.svelte +107 -0
  86. package/dist/components/ic/SwitchInputController.svelte.d.ts +24 -0
  87. package/dist/components/ic/TagsInputController.svelte +125 -0
  88. package/dist/components/ic/TagsInputController.svelte.d.ts +27 -0
  89. package/dist/components/ic/TextAreaInputController.svelte +1932 -0
  90. package/dist/components/ic/TextAreaInputController.svelte.d.ts +14 -0
  91. package/dist/components/ic/TextAreaPreviewInputController.svelte +478 -0
  92. package/dist/components/ic/TextAreaPreviewInputController.svelte.d.ts +28 -0
  93. package/dist/components/ic/TextInputController.svelte +70 -0
  94. package/dist/components/ic/TextInputController.svelte.d.ts +30 -0
  95. package/dist/components/ic/TimeInputController.svelte +434 -0
  96. package/dist/components/ic/TimeInputController.svelte.d.ts +25 -0
  97. package/dist/components/ic/VectorInputController.svelte +1826 -0
  98. package/dist/components/ic/VectorInputController.svelte.d.ts +47 -0
  99. package/dist/components/ic/index.d.ts +39 -0
  100. package/dist/components/ic/index.js +24 -0
  101. package/dist/components/ic/shared/CategoryTabs.svelte +40 -0
  102. package/dist/components/ic/shared/CategoryTabs.svelte.d.ts +25 -0
  103. package/dist/components/ic/shared/ItemGrid.svelte +181 -0
  104. package/dist/components/ic/shared/ItemGrid.svelte.d.ts +35 -0
  105. package/dist/components/ic/shared/RecentItemsSection.svelte +260 -0
  106. package/dist/components/ic/shared/RecentItemsSection.svelte.d.ts +35 -0
  107. package/dist/components/ic/shared/index.d.ts +8 -0
  108. package/dist/components/ic/shared/index.js +9 -0
  109. package/dist/components/icons/addnew.svelte +21 -0
  110. package/dist/components/icons/addnew.svelte.d.ts +20 -0
  111. package/dist/components/icons/drag.svelte +29 -0
  112. package/dist/components/icons/drag.svelte.d.ts +20 -0
  113. package/dist/components/icons/drag.svg +1 -0
  114. package/dist/components/icons/locked.svelte +4 -0
  115. package/dist/components/icons/locked.svelte.d.ts +26 -0
  116. package/dist/components/icons/locked.svg +4 -0
  117. package/dist/components/icons/ok.svelte +20 -0
  118. package/dist/components/icons/ok.svelte.d.ts +20 -0
  119. package/dist/components/icons/ok.svg +11 -0
  120. package/dist/components/icons/unlocked.svelte +4 -0
  121. package/dist/components/icons/unlocked.svelte.d.ts +26 -0
  122. package/dist/components/icons/unlocked.svg +4 -0
  123. package/dist/components/icons/x.svelte +14 -0
  124. package/dist/components/icons/x.svelte.d.ts +20 -0
  125. package/dist/components/icons/x.svg +5 -0
  126. package/dist/components/ui/arrows/OpacitySliderArrows.svelte +43 -0
  127. package/dist/components/ui/arrows/OpacitySliderArrows.svelte.d.ts +23 -0
  128. package/dist/components/ui/checkerboard-background/CheckerboardBackground.svelte +15 -0
  129. package/dist/components/ui/checkerboard-background/CheckerboardBackground.svelte.d.ts +20 -0
  130. package/dist/config.d.ts +11 -0
  131. package/dist/config.js +31 -0
  132. package/dist/index.d.ts +5 -0
  133. package/dist/index.js +9 -0
  134. package/dist/stores/userSession.d.ts +7 -0
  135. package/dist/stores/userSession.js +28 -0
  136. package/dist/utils/FontManager.d.ts +278 -0
  137. package/dist/utils/FontManager.js +1302 -0
  138. package/dist/utils/MediaManager.d.ts +35 -0
  139. package/dist/utils/MediaManager.js +59 -0
  140. package/dist/utils/VectorManager.d.ts +190 -0
  141. package/dist/utils/VectorManager.js +754 -0
  142. package/dist/utils/colorAutocomplete.d.ts +10 -0
  143. package/dist/utils/colorAutocomplete.js +28 -0
  144. package/dist/utils/colorNames.d.ts +3 -0
  145. package/dist/utils/colorNames.js +152 -0
  146. package/dist/utils/colorTransparency.d.ts +52 -0
  147. package/dist/utils/colorTransparency.js +114 -0
  148. package/dist/utils/fontUtils.d.ts +34 -0
  149. package/dist/utils/fontUtils.js +132 -0
  150. package/dist/utils/passiveEvents.js +20 -0
  151. package/dist/utils/recentColors.d.ts +10 -0
  152. package/dist/utils/recentColors.js +63 -0
  153. package/dist/utils/skipFonts.d.ts +3 -0
  154. package/dist/utils/skipFonts.js +49 -0
  155. package/dist/utils/vectorUtils.d.ts +34 -0
  156. package/dist/utils/vectorUtils.js +71 -0
  157. package/dist/utils.d.ts +11 -0
  158. package/dist/utils.js +38 -0
  159. package/package.json +134 -74
  160. package/.babelrc +0 -10
  161. package/.bowerrc +0 -4
  162. package/.nvmrc +0 -1
  163. package/Gruntfile.coffee +0 -69
  164. package/bower.json +0 -28
  165. package/css/browser/entries/activateable.styl +0 -29
  166. package/css/browser/entries/audio.styl +0 -39
  167. package/css/browser/entries/folder.styl +0 -79
  168. package/css/browser/entries/image.styl +0 -26
  169. package/css/browser/entries/index.styl +0 -66
  170. package/css/browser/entries/rss.styl +0 -11
  171. package/css/browser/entries/video.styl +0 -11
  172. package/css/browser/index.styl +0 -86
  173. package/css/browser/intent/index.styl +0 -61
  174. package/css/browser/nav/index.styl +0 -73
  175. package/css/browser/social-browser.styl +0 -23
  176. package/css/controls/clock.styl +0 -110
  177. package/css/controls/config.styl +0 -6
  178. package/css/controls/control-audio-details.styl +0 -63
  179. package/css/controls/control-audio.styl +0 -28
  180. package/css/controls/control-browser.styl +0 -48
  181. package/css/controls/control-color.styl +0 -423
  182. package/css/controls/control-date_time.styl +0 -191
  183. package/css/controls/control-dropdown.styl +0 -79
  184. package/css/controls/control-font.styl +0 -147
  185. package/css/controls/control-image-details.styl +0 -35
  186. package/css/controls/control-image.styl +0 -43
  187. package/css/controls/control-order-picker.styl +0 -29
  188. package/css/controls/control-position.styl +0 -390
  189. package/css/controls/control-range.styl +0 -68
  190. package/css/controls/control-scale.styl +0 -30
  191. package/css/controls/control-slider.styl +0 -38
  192. package/css/controls/control-spinner.styl +0 -39
  193. package/css/controls/control-textarea.styl +0 -52
  194. package/css/controls/control-toggle.styl +0 -34
  195. package/css/controls/control-url.styl +0 -32
  196. package/css/controls/control-video-details.styl +0 -85
  197. package/css/controls/control-video.styl +0 -43
  198. package/css/controls/general.styl +0 -98
  199. package/css/controls/index.styl +0 -25
  200. package/css/editor/colors.styl +0 -7
  201. package/css/editor/content/directinput.styl +0 -47
  202. package/css/editor/content/index.styl +0 -111
  203. package/css/editor/content/message.styl +0 -53
  204. package/css/editor/content/tabs.styl +0 -75
  205. package/css/editor/copied-from-old-style.styl +0 -197
  206. package/css/editor/details/bulk-editor.styl +0 -104
  207. package/css/editor/details/index.styl +0 -21
  208. package/css/editor/details/item.styl +0 -153
  209. package/css/editor/embed.styl +0 -106
  210. package/css/editor/footer.styl +0 -180
  211. package/css/editor/full-editor.styl +0 -16
  212. package/css/editor/index.styl +0 -124
  213. package/css/editor/login-popup.styl +0 -13
  214. package/css/editor/preview.styl +0 -257
  215. package/css/editor/skin/editor/index.styl +0 -54
  216. package/css/editor/skin/editor/tabs.styl +0 -111
  217. package/css/editor/skin/index.styl +0 -138
  218. package/css/editor/skin/popup.styl +0 -152
  219. package/css/editor/skin/presets/dropdown.styl +0 -61
  220. package/css/editor/skin/presets/index.styl +0 -86
  221. package/css/editor/skin/presets/list.styl +0 -244
  222. package/css/editor/tooltips.styl +0 -85
  223. package/css/fonts.styl +0 -47
  224. package/css/icons/BlogvioEditor.dev.svg +0 -112
  225. package/css/icons.styl +0 -16
  226. package/css/img/arrow-right-rounded-v-small.png +0 -0
  227. package/css/img/browser-folder-separator.png +0 -0
  228. package/css/img/calendar-input-icons.png +0 -0
  229. package/css/img/check.png +0 -0
  230. package/css/img/close.png +0 -0
  231. package/css/img/composition/edit-icon.png +0 -0
  232. package/css/img/content/audio-icon.png +0 -0
  233. package/css/img/content/folder-icon.png +0 -0
  234. package/css/img/content/messages/error-background.png +0 -0
  235. package/css/img/content/messages/error-icon.png +0 -0
  236. package/css/img/content/messages/notification-background.png +0 -0
  237. package/css/img/content/messages/notification-icon.png +0 -0
  238. package/css/img/content/no-files-audio.png +0 -0
  239. package/css/img/content/no-files-image.png +0 -0
  240. package/css/img/content/no-files-video.png +0 -0
  241. package/css/img/content/no-files.png +0 -0
  242. package/css/img/content/rss-icon.png +0 -0
  243. package/css/img/content/selected.png +0 -0
  244. package/css/img/content/video-icon.png +0 -0
  245. package/css/img/content/with-control-text.png +0 -0
  246. package/css/img/control-dropdown-arrow.png +0 -0
  247. package/css/img/control-image-valid.png +0 -0
  248. package/css/img/control-position-icon.png +0 -0
  249. package/css/img/control-scale-lock-icon.png +0 -0
  250. package/css/img/control-slider-arrows-hover.png +0 -0
  251. package/css/img/control-slider-arrows.png +0 -0
  252. package/css/img/control-spinner-buttons-hover.png +0 -0
  253. package/css/img/control-spinner-buttons.png +0 -0
  254. package/css/img/control-textarea-bold-active.png +0 -0
  255. package/css/img/control-textarea-bold.png +0 -0
  256. package/css/img/control-textarea-italic-active.png +0 -0
  257. package/css/img/control-textarea-italic.png +0 -0
  258. package/css/img/control-textarea-underline-active.png +0 -0
  259. package/css/img/control-textarea-underline.png +0 -0
  260. package/css/img/custom-checkbox-sprite.png +0 -0
  261. package/css/img/details/item-collapse-icon.png +0 -0
  262. package/css/img/details/item-expand-icon.png +0 -0
  263. package/css/img/details/item-sort-icon.png +0 -0
  264. package/css/img/done-arrow.png +0 -0
  265. package/css/img/fb-logo-icon-w.png +0 -0
  266. package/css/img/grid-icons.png +0 -0
  267. package/css/img/icon-reference.png +0 -0
  268. package/css/img/indicator.png +0 -0
  269. package/css/img/loader.gif +0 -0
  270. package/css/img/loaders/audio-white.png +0 -0
  271. package/css/img/loaders/audio.png +0 -0
  272. package/css/img/loaders/photo-white.png +0 -0
  273. package/css/img/loaders/photo.png +0 -0
  274. package/css/img/loaders/util-white.png +0 -0
  275. package/css/img/loaders/util.png +0 -0
  276. package/css/img/loaders/video-white.png +0 -0
  277. package/css/img/loaders/video.png +0 -0
  278. package/css/img/maximize.png +0 -0
  279. package/css/img/minimize.png +0 -0
  280. package/css/img/next-btn.png +0 -0
  281. package/css/img/search-icon-sprite.png +0 -0
  282. package/css/img/services/blogvio.png +0 -0
  283. package/css/img/services/drive.png +0 -0
  284. package/css/img/services/dropbox-login.png +0 -0
  285. package/css/img/services/dropbox.png +0 -0
  286. package/css/img/services/facebook-login.png +0 -0
  287. package/css/img/services/facebook.png +0 -0
  288. package/css/img/services/flickr-login.png +0 -0
  289. package/css/img/services/flickr.png +0 -0
  290. package/css/img/services/instagram-login.png +0 -0
  291. package/css/img/services/instagram.png +0 -0
  292. package/css/img/services/text.png +0 -0
  293. package/css/img/services/uploads.png +0 -0
  294. package/css/img/services/uploads.svg +0 -689
  295. package/css/img/services/webaddress-bw.png +0 -0
  296. package/css/img/services/webaddress.png +0 -0
  297. package/css/img/services/webadress.png +0 -0
  298. package/css/img/services/websearch.png +0 -0
  299. package/css/img/services/wix.png +0 -0
  300. package/css/img/skin/item-active-indicator.png +0 -0
  301. package/css/img/skin/item-hover-icon.png +0 -0
  302. package/css/img/skin/overwrite.png +0 -0
  303. package/css/img/skin/popup-name-icon.png +0 -0
  304. package/css/img/skin/popup-save.png +0 -0
  305. package/css/img/skin/preset.png +0 -0
  306. package/css/img/skin/presets-arrow-purple.png +0 -0
  307. package/css/img/tw-icon-w.png +0 -0
  308. package/css/img/warning.png +0 -0
  309. package/css/img/white-rounded-check.png +0 -0
  310. package/css/index.styl +0 -9
  311. package/css/mixins.styl +0 -62
  312. package/css/scrollable.styl +0 -101
  313. package/karma.conf.js +0 -73
  314. package/lib/control.js +0 -1
  315. package/lib/controls.js +0 -1
  316. package/lib/core.editor.js +0 -4
  317. package/lib/core.js +0 -1
  318. package/lib/core.vendor.js +0 -3
  319. package/lib/dev/control.js +0 -845
  320. package/lib/dev/controls.js +0 -7774
  321. package/lib/dev/core.editor.js +0 -6439
  322. package/lib/dev/core.js +0 -1486
  323. package/lib/dev/core.vendor.js +0 -10708
  324. package/lib/dev/editor.css +0 -166
  325. package/lib/dev/editor.js +0 -29710
  326. package/lib/dev/editor.vendor.js +0 -15119
  327. package/lib/dev/jquery.nanoscroller.js.map +0 -10
  328. package/lib/editor.css +0 -167
  329. package/lib/editor.js +0 -49
  330. package/lib/editor.vendor.js +0 -11
  331. package/src/api/index.coffee +0 -23
  332. package/src/api/mixin.coffee +0 -89
  333. package/src/browser/controller/entry/audio.coffee +0 -38
  334. package/src/browser/controller/entry/button.coffee +0 -16
  335. package/src/browser/controller/entry/file.coffee +0 -35
  336. package/src/browser/controller/entry/folder.coffee +0 -14
  337. package/src/browser/controller/entry/image.coffee +0 -24
  338. package/src/browser/controller/entry/index.coffee +0 -18
  339. package/src/browser/controller/entry/rss.coffee +0 -5
  340. package/src/browser/controller/entry/video.coffee +0 -4
  341. package/src/browser/controller/index.coffee +0 -289
  342. package/src/browser/controller/instagram.coffee +0 -129
  343. package/src/browser/controller/navbar.coffee +0 -48
  344. package/src/browser/controller/social.coffee +0 -213
  345. package/src/browser/model/entry.coffee +0 -18
  346. package/src/browser/model/index.coffee +0 -34
  347. package/src/core/controller/font-loader.coffee +0 -65
  348. package/src/core/controller/lazyload.coffee +0 -14
  349. package/src/core/controller/overlay.coffee +0 -146
  350. package/src/core/controller/popup.coffee +0 -36
  351. package/src/core/controller/scrollable.coffee +0 -68
  352. package/src/core/controls/audio.coffee +0 -56
  353. package/src/core/controls/browser.coffee +0 -271
  354. package/src/core/controls/color.coffee +0 -121
  355. package/src/core/controls/date-time/calendar/index.coffee +0 -99
  356. package/src/core/controls/date-time/calendar/months.coffee +0 -52
  357. package/src/core/controls/date-time/calendar/years.coffee +0 -58
  358. package/src/core/controls/date-time/clock.coffee +0 -62
  359. package/src/core/controls/date-time/helper.coffee +0 -31
  360. package/src/core/controls/date-time/index.coffee +0 -62
  361. package/src/core/controls/date-time/popup.coffee +0 -56
  362. package/src/core/controls/details/audio.coffee +0 -33
  363. package/src/core/controls/details/image.coffee +0 -19
  364. package/src/core/controls/details/video.coffee +0 -14
  365. package/src/core/controls/dropdown.coffee +0 -100
  366. package/src/core/controls/font/font-item.coffee +0 -22
  367. package/src/core/controls/font/font-list.coffee +0 -108
  368. package/src/core/controls/font/fonts-popup.coffee +0 -91
  369. package/src/core/controls/font/index.coffee +0 -66
  370. package/src/core/controls/image.coffee +0 -37
  371. package/src/core/controls/index.coffee +0 -88
  372. package/src/core/controls/orderPicker.coffee +0 -93
  373. package/src/core/controls/position/grid-panel.coffee +0 -75
  374. package/src/core/controls/position/index.coffee +0 -98
  375. package/src/core/controls/position/panel.coffee +0 -68
  376. package/src/core/controls/position/pixel-panel.coffee +0 -54
  377. package/src/core/controls/position/popup.coffee +0 -63
  378. package/src/core/controls/position/reference-panel.coffee +0 -26
  379. package/src/core/controls/range.coffee +0 -191
  380. package/src/core/controls/rss.coffee +0 -16
  381. package/src/core/controls/scale.coffee +0 -78
  382. package/src/core/controls/slider.coffee +0 -122
  383. package/src/core/controls/stepper.coffee +0 -97
  384. package/src/core/controls/text.coffee +0 -54
  385. package/src/core/controls/textarea.coffee +0 -78
  386. package/src/core/controls/toggle.coffee +0 -29
  387. package/src/core/controls/url.coffee +0 -88
  388. package/src/core/controls/video.coffee +0 -9
  389. package/src/core/embed/api.js +0 -178
  390. package/src/core/embed/controller.js +0 -331
  391. package/src/core/models/composition.coffee +0 -178
  392. package/src/core/models/content.coffee +0 -79
  393. package/src/core/models/skin.coffee +0 -91
  394. package/src/core/models/user.coffee +0 -6
  395. package/src/core/models/widget.coffee +0 -88
  396. package/src/core/router/index.coffee +0 -44
  397. package/src/core/services/container/index.js +0 -18
  398. package/src/core/services/container/instance.js +0 -3
  399. package/src/core/services/content.js +0 -80
  400. package/src/core/services/instagram.coffee +0 -47
  401. package/src/core/services/mixin/events.js +0 -17
  402. package/src/core/services/service.js +0 -1
  403. package/src/core/services/sounds.js +0 -81
  404. package/src/core/services/uploads.js +0 -299
  405. package/src/core/utils/popup.coffee +0 -67
  406. package/src/core/utils/should-show.coffee +0 -12
  407. package/src/core/views/audio-details.pug +0 -9
  408. package/src/core/views/audio.pug +0 -7
  409. package/src/core/views/browser.pug +0 -4
  410. package/src/core/views/color.pug +0 -8
  411. package/src/core/views/date-time/calendar.pug +0 -3
  412. package/src/core/views/date-time/clock.pug +0 -12
  413. package/src/core/views/date-time/grid.pug +0 -6
  414. package/src/core/views/date-time/input.pug +0 -4
  415. package/src/core/views/date-time/table.pug +0 -6
  416. package/src/core/views/dropdown.pug +0 -9
  417. package/src/core/views/font/font-item.pug +0 -3
  418. package/src/core/views/font/fonts-list.pug +0 -7
  419. package/src/core/views/font/fonts-popup.pug +0 -5
  420. package/src/core/views/font/index.pug +0 -7
  421. package/src/core/views/image-details.pug +0 -6
  422. package/src/core/views/image.pug +0 -5
  423. package/src/core/views/input-layout.pug +0 -15
  424. package/src/core/views/order-items.pug +0 -7
  425. package/src/core/views/order-picker.pug +0 -7
  426. package/src/core/views/pixelpanel.pug +0 -69
  427. package/src/core/views/position.pug +0 -10
  428. package/src/core/views/range.pug +0 -9
  429. package/src/core/views/scale.pug +0 -7
  430. package/src/core/views/slider.pug +0 -6
  431. package/src/core/views/stepper.pug +0 -6
  432. package/src/core/views/text.pug +0 -4
  433. package/src/core/views/textarea.pug +0 -8
  434. package/src/core/views/toggle.pug +0 -5
  435. package/src/core/views/video-details.pug +0 -7
  436. package/src/core/views/video.pug +0 -4
  437. package/src/core/widget/index.coffee +0 -66
  438. package/src/editor/content/browser/dropzone.html +0 -268
  439. package/src/editor/content/browser/file.html +0 -124
  440. package/src/editor/content/browser/scroll-pane.html +0 -32
  441. package/src/editor/content/browser/service.html +0 -53
  442. package/src/editor/content/browser/upload.coffee +0 -44
  443. package/src/editor/content/browser/upload.html +0 -439
  444. package/src/editor/content/index.coffee +0 -294
  445. package/src/editor/content/input/field.coffee +0 -45
  446. package/src/editor/content/input/index.coffee +0 -115
  447. package/src/editor/content/service/base/photoitem.coffee +0 -49
  448. package/src/editor/content/service/cache.coffee +0 -20
  449. package/src/editor/content/service/default.coffee +0 -59
  450. package/src/editor/content/service/dropbox.coffee +0 -71
  451. package/src/editor/content/service/facebook.coffee +0 -143
  452. package/src/editor/content/service/flickr.coffee +0 -85
  453. package/src/editor/content/service/google-drive-sdk.coffee +0 -18
  454. package/src/editor/content/service/google-drive.coffee +0 -144
  455. package/src/editor/content/service/instagram-social.coffee +0 -84
  456. package/src/editor/content/service/instagram.coffee +0 -168
  457. package/src/editor/content/service/share-popup.coffee +0 -15
  458. package/src/editor/content/service/soundcloud.coffee +0 -163
  459. package/src/editor/content/tab.coffee +0 -27
  460. package/src/editor/details/bulk-editor/attributes-panel.coffee +0 -11
  461. package/src/editor/details/bulk-editor/index.coffee +0 -86
  462. package/src/editor/details/index.coffee +0 -156
  463. package/src/editor/details/item/attributes.coffee +0 -45
  464. package/src/editor/details/item/index.coffee +0 -82
  465. package/src/editor/editor.coffee +0 -274
  466. package/src/editor/footer/breadcrumb.coffee +0 -17
  467. package/src/editor/footer/index.coffee +0 -36
  468. package/src/editor/index.coffee +0 -79
  469. package/src/editor/listeners/embed-listener.coffee +0 -143
  470. package/src/editor/login-popup.coffee +0 -29
  471. package/src/editor/notification-controller.coffee +0 -35
  472. package/src/editor/popup.coffee +0 -68
  473. package/src/editor/remote-preview.coffee +0 -68
  474. package/src/editor/save/index.coffee +0 -62
  475. package/src/editor/skin/delete.coffee +0 -33
  476. package/src/editor/skin/editor/index.coffee +0 -188
  477. package/src/editor/skin/editor/panel.coffee +0 -37
  478. package/src/editor/skin/index.coffee +0 -126
  479. package/src/editor/skin/save.coffee +0 -58
  480. package/src/editor/skin/selector/index.coffee +0 -59
  481. package/src/editor/skin/selector/item.coffee +0 -123
  482. package/src/editor/skin/selector/list.coffee +0 -97
  483. package/src/editor/views/embed.pug +0 -10
  484. package/src/editor/views/loginbutton.pug +0 -8
  485. package/src/editor/views/popup.pug +0 -17
  486. package/src/editor/views/presets.pug +0 -8
  487. package/src/editor/views/presetslist-header.pug +0 -8
  488. package/src/editor/views/skinitem.pug +0 -13
  489. package/src/modules/control.js +0 -3
  490. package/src/modules/controls.js +0 -29
  491. package/src/modules/core.js +0 -19
  492. package/src/modules/editor.js +0 -80
  493. package/test/config.js +0 -3
  494. package/test/core/models/skin-spec.js +0 -209
  495. package/webpack.config.js +0 -46
  496. package/webpack.config.prod.js +0 -9
  497. /package/{src/core/views/date-time/popup.pug → dist/utils/passiveEvents.d.ts} +0 -0
@@ -0,0 +1,1826 @@
1
+ <!--
2
+ * VectorInputController Component
3
+ * A single-input controller for SVG vector selection with category filtering.
4
+ *
5
+ * Features:
6
+ * - Vector selection with preview
7
+ * - Real-time search with highlighting
8
+ * - Category filtering (Icons, Emojis, Clipart, Mine)
9
+ * - Recently used vectors tracking (up to 3 vectors)
10
+ * - Performance-optimized vector loading
11
+ * - Custom vector support via file browser
12
+ *
13
+ * Performance optimizations:
14
+ * - Lazy loads vectors only when visible in viewport
15
+ * - Delays vector loading until scrolling stops (1 second delay)
16
+ * - Preloads default vectors on mount
17
+
18
+ -->
19
+ <script lang="ts">
20
+ // Svelte imports
21
+ import { onMount, onDestroy, tick } from 'svelte';
22
+ const browser = typeof window !== 'undefined';
23
+
24
+ /** COMPONENTS IMPORTS */
25
+ // Shadcn UI components imports from design-system
26
+ import { Popover, EmptyState } from '@widgetic/design-system/components';
27
+
28
+ // Import Tabler Icons from design-system
29
+ import { Check, IconCloudUpload, IconCaretDownFilled, IconAlertTriangle } from '@widgetic/design-system/icons';
30
+
31
+ // Import InputControllerHeader
32
+ import { InputControllerHeader } from './index';
33
+
34
+ // Import shared components
35
+ // import { SearchInput } from './shared';
36
+ import { SearchInput } from '@widgetic/design-system/components';
37
+ import CategoryTabs from './shared/CategoryTabs.svelte';
38
+ import { RecentItemsSection, ItemGrid } from './shared';
39
+
40
+ // Utils imports
41
+ import {
42
+ vectorUtils,
43
+ vectorCategories,
44
+ type VectorItem,
45
+ type RecentVector,
46
+ recentVectorsStore
47
+ } from '../../utils/vectorUtils';
48
+ import type { VectorData } from '../../utils/VectorManager';
49
+ import VectorManager from '../../utils/VectorManager';
50
+
51
+ // Constants
52
+ const VECTOR_FILTER_KEY = (id: string) => `vector-specific-filter-state-${id}`;
53
+ const MAX_RECENT_VECTORS = 3;
54
+
55
+ type FilterType = typeof vectorCategories.types[number];
56
+ let currentFilter: FilterType = 'All'; // Default to 'All'
57
+ let lastActiveFilter: FilterType = 'All'; // Add this to track the last active filter
58
+ let filterOptions: FilterType[] = [...vectorCategories.types];
59
+
60
+ /**
61
+ * Component Props
62
+ * @prop {string} label - Display label for the vector selector
63
+ * @prop {string} value - Currently selected vector
64
+ * @prop {Function} onChange - Callback when vector selection changes
65
+ * @prop {string} description - Optional help text
66
+ * @prop {string} controllerId - Unique identifier for this controller
67
+ * @prop {any} openDropdowns - Store tracking open dropdown states
68
+ * @prop {Function} toggleDropdown - Function to toggle dropdown state
69
+ * @prop {Function} onFocus - Optional focus handler
70
+ */
71
+ export let label: string;
72
+ export let value: { name: string; category: string; url: string } | null = null;
73
+ export let onChange: ((newValue: { name: string; category: string; url: string } | null) => void) | undefined = undefined;
74
+ export let description: string | undefined = undefined;
75
+ export let controllerId: string;
76
+ export let openDropdowns: any;
77
+ export let toggleDropdown: (id: string) => void;
78
+ export let onFocus: () => void = () => {};
79
+
80
+ // Vector modal state
81
+ let showVectorModal = false;
82
+
83
+ // Vector search and filter state
84
+ let allVectors: VectorItem[] = [];
85
+ let recentVectors: VectorItem[] = [];
86
+ let filteredVectors: VectorData[] = [];
87
+ let searchTerm = '';
88
+ let searchBatchIndex = VectorManager.initialVectorNumber;
89
+ const SEARCH_BATCH_SIZE = VectorManager.BATCH_SIZE;
90
+
91
+ // Vector loading optimization
92
+ let vectorListContainer: HTMLElement;
93
+ let vectorObserver = new IntersectionObserver(() => {});
94
+
95
+ // Initialize VectorManager instance
96
+ let vectorManagerInstance: VectorManager;
97
+ let buttonWidth = 0;
98
+
99
+ let scrollTimeout: ReturnType<typeof setTimeout>;
100
+
101
+ // Add these variables for search
102
+ let activeSearchIndex = -1;
103
+
104
+ // Add loading state
105
+ let isLoadingBatch = false;
106
+
107
+ // Add new prop for controlling vector list behavior
108
+ export let hideRecentFromList: boolean = false;
109
+ export let hideTrashIcon: boolean = true;
110
+
111
+ // Add scroll state tracking
112
+ let isScrolled = false;
113
+
114
+ // Add loading state variables for ItemGrid
115
+ let loadingMoreVectors = false;
116
+ let hasMoreVectors = false;
117
+
118
+ // Add state to track SVG loading errors
119
+ let svgLoadErrors = new Map<string, boolean>();
120
+
121
+ // Function to handle SVG loading errors
122
+ function handleSvgError(vectorName: string, event: Event): void {
123
+ // Extract more information about the error source
124
+ let errorUrl = 'unknown';
125
+ if (event && event.target instanceof HTMLImageElement) {
126
+ errorUrl = event.target.src || 'unknown';
127
+ event.target.style.display = 'none';
128
+ }
129
+
130
+ console.error(`❌ [VectorInput] Failed to load SVG: "${vectorName}" from URL: ${errorUrl}`);
131
+ svgLoadErrors.set(vectorName, true);
132
+ // Force component update by reassigning the Map
133
+ svgLoadErrors = new Map(svgLoadErrors);
134
+ }
135
+
136
+ // Function to check if a vector has a loading error
137
+ function hasSvgLoadError(vectorName: string): boolean {
138
+ return svgLoadErrors.has(vectorName);
139
+ }
140
+
141
+ // Function to render fallback for failed SVG loads
142
+ function renderSvgFallback(vectorName: string): string {
143
+ return `
144
+ <div class="flex items-center justify-center w-full h-full bg-grey1 rounded-large widgetic-radius">
145
+ <div class="flex flex-col items-center justify-center gap-1 p-2 text-center">
146
+ <span class="text-error">
147
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
148
+ <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"></path>
149
+ <path d="M12 9v4"></path>
150
+ <path d="M12 17h.01"></path>
151
+ </svg>
152
+ </span>
153
+ <span class="caption text-grey5">Failed to load</span>
154
+ </div>
155
+ </div>
156
+ `;
157
+ }
158
+
159
+ // Function to update scroll state
160
+ function updateScrollState() {
161
+ if (vectorListContainer) {
162
+ const wasScrolled = isScrolled;
163
+ isScrolled = vectorListContainer.scrollTop > 10;
164
+
165
+ // If we're transitioning from scrolled to not scrolled, remove transition
166
+ if (wasScrolled && !isScrolled) {
167
+ const stickyHeader = document.querySelector(`#vector-header-${controllerId}`);
168
+ if (stickyHeader) {
169
+ stickyHeader.classList.remove('transition-border');
170
+ // Force a reflow to ensure the transition class removal takes effect
171
+ void (stickyHeader as HTMLElement).offsetWidth;
172
+ }
173
+ } else if (!wasScrolled && isScrolled) {
174
+ // Add transition class when starting to scroll
175
+ const stickyHeader = document.querySelector(`#vector-header-${controllerId}`);
176
+ if (stickyHeader) {
177
+ stickyHeader.classList.add('transition-border');
178
+ }
179
+ }
180
+ }
181
+ }
182
+
183
+ // Subscribe to store changes
184
+ $: {
185
+ recentVectors = $recentVectorsStore
186
+ .map(vector => ({
187
+ name: vector.name,
188
+ category: vectorCategories.normalize(vector.category) as VectorItem['category'],
189
+ url: vector.url,
190
+ isCustom: vector.isCustom
191
+ }));
192
+
193
+ // Update filtered vectors when recent vectors change
194
+ if (hideRecentFromList) {
195
+ updateFilteredVectors();
196
+ }
197
+ }
198
+
199
+ // Add debounce function at the top of the script section
200
+ function debounce<T extends (...args: any[]) => Promise<void>>(
201
+ func: T,
202
+ wait: number
203
+ ): (...args: Parameters<T>) => void {
204
+ let timeout: ReturnType<typeof setTimeout> | null = null;
205
+ return function(this: any, ...args: Parameters<T>): void {
206
+ const context = this;
207
+
208
+ // Ensure we have a valid event with a target
209
+ const event = args[0] as Event;
210
+ if (!event || !event.target) {
211
+ console.warn('❌ [VectorSearch] Debounced function called with invalid event:', event);
212
+ return;
213
+ }
214
+
215
+ if (timeout) clearTimeout(timeout);
216
+ timeout = setTimeout(() => {
217
+ timeout = null;
218
+ func.apply(context, args);
219
+ }, wait);
220
+ };
221
+ };
222
+
223
+ // Add a variable to store the debounced search function
224
+ let debouncedSearch: (event: Event) => void;
225
+
226
+ /**
227
+ * Updates the filtered vectors list based on search term and category
228
+ * Also handles setting up the observer after updating
229
+ */
230
+ function updateFilteredVectors() {
231
+ let vectors = [...allVectors];
232
+
233
+ // console.log('🔄 [VectorInput] Updating filtered vectors:', {
234
+ // allVectors: allVectors.length,
235
+ // currentFilter,
236
+ // searchTerm,
237
+ // hideRecentFromList,
238
+ // recentVectors: recentVectors.length
239
+ // });
240
+
241
+ // Apply search filter
242
+ if (searchTerm) {
243
+ const term = searchTerm.toLowerCase();
244
+ vectors = vectors.filter(vector =>
245
+ vector.name.toLowerCase().includes(term)
246
+ );
247
+ }
248
+
249
+ // Apply category filter
250
+ if (currentFilter !== 'All') {
251
+ vectors = vectors.filter(vector => {
252
+ // For Mine category, check exact match
253
+ if (currentFilter === 'Mine') {
254
+ return vector.category === 'Mine';
255
+ }
256
+ // For other categories, use normalization
257
+ const normalizedCategory = vectorCategories.normalize(vector.category);
258
+ return normalizedCategory === currentFilter;
259
+ });
260
+ }
261
+
262
+ // Optionally filter out recent vectors
263
+ if (hideRecentFromList) {
264
+ vectors = vectors.filter(vector =>
265
+ !recentVectors.some(recent => recent.name === vector.name)
266
+ );
267
+ }
268
+
269
+ // Update filtered vectors
270
+ filteredVectors = vectors;
271
+
272
+ // console.log('✅ [VectorInput] Filtered vectors result:', {
273
+ // total: vectors.length,
274
+ // filter: currentFilter,
275
+ // searchTerm,
276
+ // hideRecentFromList,
277
+ // recentVectorsHidden: hideRecentFromList ? recentVectors.length : 0
278
+ // });
279
+
280
+ // Setup observer after updating filtered vectors
281
+ // Use a small timeout to ensure the DOM has updated
282
+ if (scrollTimeout) {
283
+ clearTimeout(scrollTimeout);
284
+ }
285
+
286
+ scrollTimeout = setTimeout(() => {
287
+ setupVectorObserver();
288
+ }, 50);
289
+ }
290
+
291
+ // Initialize the debounced search function in onMount
292
+ onMount(async () => {
293
+ // Initialize the debounced search function
294
+ debouncedSearch = debounce(async (event: Event) => {
295
+ await handleSearch(event);
296
+ }, 300); // 300ms debounce time
297
+
298
+ // Setup global error handler for SVG loading errors
299
+ const vectorErrorHandler = (event: Event) => {
300
+ if (event instanceof CustomEvent && event.detail) {
301
+ const vectorName = event.detail;
302
+ // Check if we can get the vector URL from allVectors
303
+ const vectorData = allVectors.find(v => v.name === vectorName);
304
+ const vectorUrl = vectorData?.url || 'unknown';
305
+
306
+ console.error(`❌ [VectorInput] Failed to load SVG: "${vectorName}" from URL: ${vectorUrl}`);
307
+ svgLoadErrors.set(vectorName, true);
308
+ // Force component update
309
+ svgLoadErrors = new Map(svgLoadErrors);
310
+ }
311
+ };
312
+
313
+ // Add the event listener
314
+ document.addEventListener('vector-load-error', vectorErrorHandler);
315
+
316
+ // Store the handler reference for cleanup in onDestroy
317
+ (window as any)['vectorErrorHandler'] = vectorErrorHandler;
318
+
319
+ try {
320
+ if (browser) {
321
+ // Load saved filter
322
+ const savedFilter = localStorage.getItem(VECTOR_FILTER_KEY(controllerId));
323
+ if (savedFilter) {
324
+ currentFilter = savedFilter as FilterType;
325
+ }
326
+
327
+ // Check if the current selected vector exists/loads
328
+ if (value?.name && value.url) {
329
+ const testImg = new Image();
330
+ testImg.onload = () => {
331
+ // Vector loads successfully
332
+ console.log(`✅ [VectorInput] Current vector loads successfully: ${value.name}`);
333
+ };
334
+ testImg.onerror = () => {
335
+ // Mark as error if it fails to load
336
+ console.error(`❌ [VectorInput] Current vector failed to load: ${value.name}, URL: ${value.url}`);
337
+ svgLoadErrors.set(value.name, true);
338
+ svgLoadErrors = new Map(svgLoadErrors);
339
+ };
340
+ testImg.src = value.url;
341
+ }
342
+ }
343
+
344
+ // console.log('🚀 [VectorInput] Initializing controller:', {
345
+ // controllerId,
346
+ // initialFilter: currentFilter
347
+ // });
348
+
349
+ vectorManagerInstance = await VectorManager.getInstance();
350
+
351
+ // Load and verify recent vectors
352
+ const loadedRecentVectors = vectorUtils.getRecentVectors();
353
+ // console.log('📝 [VectorInput] Loading recent vectors:', loadedRecentVectors);
354
+
355
+ // Filter and validate recent vectors
356
+ const validRecentVectors = loadedRecentVectors
357
+ .slice(0, MAX_RECENT_VECTORS);
358
+
359
+ // Update store with validated vectors
360
+ recentVectorsStore.set(validRecentVectors);
361
+
362
+ // Update local state
363
+ recentVectors = validRecentVectors.map(vector => ({
364
+ name: vector.name,
365
+ category: vectorCategories.normalize(vector.category) as VectorItem['category'],
366
+ url: vector.url,
367
+ isCustom: vector.isCustom
368
+ }));
369
+
370
+ // console.log('✅ [VectorInput] Recent vectors initialized:', {
371
+ // loaded: loadedRecentVectors.length,
372
+ // valid: validRecentVectors.length,
373
+ // vectors: validRecentVectors.map(v => v.name)
374
+ // });
375
+
376
+ // Initialize vectors list with saved filter
377
+ const initializeVectors = async () => {
378
+ if (currentFilter === 'Mine') {
379
+ const customVectors = await vectorManagerInstance.loadCustomVectors();
380
+ return customVectors;
381
+ }
382
+
383
+ // For All category, load standard vectors first, then append custom vectors
384
+ const standardVectors = await vectorManagerInstance.getInitialVectorList();
385
+ const customVectors = await vectorManagerInstance.loadCustomVectors();
386
+
387
+ if (currentFilter === 'All') {
388
+ return [
389
+ ...standardVectors.filter(vector => !vector.isCustom),
390
+ ...customVectors
391
+ ];
392
+ }
393
+
394
+ // For other categories, filter out custom vectors
395
+ return standardVectors.filter(vector => !vector.isCustom);
396
+ };
397
+
398
+ const initialVectors = await initializeVectors();
399
+
400
+ // Force a fresh batch for this controller
401
+ vectorManagerInstance.resetBatchIndex(controllerId);
402
+
403
+ // For Mine category, use all custom vectors
404
+ if (currentFilter === 'Mine') {
405
+ allVectors = initialVectors;
406
+ } else {
407
+ // For other categories, use normal filtering
408
+ if (currentFilter !== 'All') {
409
+ const categoryVectors = initialVectors.filter(vector =>
410
+ vectorCategories.normalize(vector.category) === currentFilter
411
+ );
412
+ allVectors = categoryVectors.slice(0, searchBatchIndex);
413
+ } else {
414
+ allVectors = initialVectors.slice(0, searchBatchIndex);
415
+ }
416
+ }
417
+
418
+ // Update filtered vectors immediately
419
+ updateFilteredVectors();
420
+
421
+ // Preload the vectors to detect missing files early
422
+ if (filteredVectors.length > 0) {
423
+ console.log(`🔍 [VectorInput] Preloading ${filteredVectors.length} vectors to detect missing files...`);
424
+ preloadVectors(filteredVectors);
425
+ }
426
+
427
+ // Also preload recent vectors
428
+ if (recentVectors.length > 0) {
429
+ console.log(`🔍 [VectorInput] Preloading ${recentVectors.length} recent vectors...`);
430
+ preloadVectors(recentVectors);
431
+ }
432
+
433
+ // Setup observer after vectors are loaded
434
+ scrollTimeout = setTimeout(() => {
435
+ setupVectorObserver();
436
+ }, 0);
437
+
438
+ // console.log('✅ [VectorInput] Controller initialized:', {
439
+ // controllerId,
440
+ // initialVectors: allVectors.length,
441
+ // filteredVectors: filteredVectors.length,
442
+ // recentVectors: recentVectors.length,
443
+ // currentFilter,
444
+ // customVectors: allVectors.filter(v => v.category === 'Mine').length
445
+ // });
446
+ } catch (error) {
447
+ console.error('❌ [VectorInput] Error during initialization:', error);
448
+ }
449
+ });
450
+
451
+ /**
452
+ * Manages real-time search functionality
453
+ * Prioritizes recent vectors in search results
454
+ * Enhanced to better handle partial matches
455
+ */
456
+ async function handleSearch(event: Event) {
457
+ try {
458
+ const input = event.target as HTMLInputElement;
459
+ // Add null check for input element
460
+ if (!input) {
461
+ console.warn('❌ [VectorSearch] Search input element is null');
462
+ return;
463
+ }
464
+
465
+ const newSearchTerm = input.value;
466
+
467
+ // Skip processing if the search term hasn't changed
468
+ if (newSearchTerm === searchTerm) {
469
+ return;
470
+ }
471
+
472
+ // If this is a new search (was empty before), save the current filter
473
+ if (!searchTerm && newSearchTerm) {
474
+ lastActiveFilter = currentFilter;
475
+ currentFilter = 'All'; // Reset to All when starting a search
476
+ if (browser) {
477
+ localStorage.setItem(VECTOR_FILTER_KEY(controllerId), 'All');
478
+ }
479
+ }
480
+
481
+ searchTerm = newSearchTerm;
482
+
483
+ // console.log('🔍 [VectorSearch] Search input:', { searchTerm });
484
+
485
+ if (!searchTerm.trim()) {
486
+ // Reset to normal view if search is empty
487
+ searchTerm = '';
488
+
489
+ // Reset batch index to start fresh
490
+ if (vectorManagerInstance) {
491
+ vectorManagerInstance.resetBatchIndex(controllerId);
492
+
493
+ // Reload vectors for the current category
494
+ await tick(); // Ensure the UI updates first
495
+
496
+ // The reactive statement for empty search term will handle reloading
497
+ }
498
+
499
+ if (vectorListContainer) {
500
+ vectorListContainer.scrollTop = 0;
501
+ }
502
+ return; // Let reactivity handle the reset
503
+ }
504
+
505
+ // Just load the complete vector list if we're searching
506
+ const completeVectorList = await vectorManagerInstance?.getCompleteVectorList(true) || [];
507
+ allVectors = completeVectorList;
508
+
509
+ // Reset scroll position
510
+ if (vectorListContainer) {
511
+ vectorListContainer.scrollTop = 0;
512
+ }
513
+
514
+ // Ensure the UI updates to show "Search Results" instead of "More"
515
+ await tick();
516
+ } catch (error) {
517
+ console.error('❌ [VectorSearch] Error:', error);
518
+ }
519
+ }
520
+
521
+ // Add a derived store for search results
522
+ let currentSearchResults: VectorData[] = [];
523
+
524
+ // Update the reactive statement for search
525
+ $: if (searchTerm) {
526
+ const searchValue = searchTerm.toLowerCase().trim();
527
+ // Only process if the search value is not empty
528
+ if (searchValue) {
529
+ try {
530
+ // Get both standard and custom vectors from cache - add null checks
531
+ const standardVectors = vectorManagerInstance?.getCachedVectorList()?.filter(vector => !vector.isCustom) || [];
532
+ const customVectors = vectorManagerInstance?.getCachedVectorList()?.filter(vector => vector.isCustom) || [];
533
+
534
+ // Merge both lists
535
+ const allCachedVectors = [...standardVectors, ...customVectors];
536
+
537
+ // Enhanced search algorithm
538
+ const newSearchResults = allCachedVectors
539
+ .filter(vector => {
540
+ const nameLower = vector.name.toLowerCase().trim();
541
+
542
+ // Use exact matching for search term instead of splitting by spaces
543
+ return nameLower.includes(searchValue);
544
+ })
545
+ .sort((a, b) => {
546
+ const aName = a.name.toLowerCase();
547
+ const bName = b.name.toLowerCase();
548
+
549
+ // Prioritize vectors that start with the search term
550
+ const aStartsWith = aName.startsWith(searchValue);
551
+ const bStartsWith = bName.startsWith(searchValue);
552
+
553
+ if (aStartsWith && !bStartsWith) return -1;
554
+ if (!aStartsWith && bStartsWith) return 1;
555
+
556
+ // Then prioritize vectors where search term appears earlier
557
+ const aIndex = aName.indexOf(searchValue);
558
+ const bIndex = bName.indexOf(searchValue);
559
+
560
+ if (aIndex !== -1 && bIndex !== -1) {
561
+ return aIndex - bIndex;
562
+ }
563
+
564
+ // Fall back to alphabetical sorting
565
+ return aName.localeCompare(bName);
566
+ });
567
+
568
+ // Only update results if they've changed
569
+ if (JSON.stringify(newSearchResults) !== JSON.stringify(currentSearchResults)) {
570
+ // Store all search results for batching
571
+ currentSearchResults = newSearchResults;
572
+
573
+ // Select first item automatically if we have results
574
+ activeSearchIndex = currentSearchResults.length > 0 ? 0 : -1;
575
+
576
+ // Show more initial results (double the default)
577
+ const initialResultsCount = Math.min(VectorManager.initialVectorNumber * 2, currentSearchResults.length);
578
+ filteredVectors = currentSearchResults.slice(0, initialResultsCount);
579
+
580
+ // Reset scroll position
581
+ if (vectorListContainer) {
582
+ vectorListContainer.scrollTop = 0;
583
+ }
584
+
585
+ // console.log('📝 [VectorSearch] Updated results:', {
586
+ // searchValue,
587
+ // totalMatches: currentSearchResults.length,
588
+ // displayedResults: filteredVectors.length,
589
+ // activeIndex: activeSearchIndex
590
+ // });
591
+ }
592
+ } catch (error) {
593
+ console.error('❌ [VectorSearch] Error processing search:', error);
594
+ }
595
+ }
596
+ }
597
+
598
+ // Handle empty search term separately to avoid reactivity loop
599
+ $: if (searchTerm === '') {
600
+ // Only reset if we actually had search results before or if we have no filtered vectors
601
+ if (currentSearchResults.length > 0 || filteredVectors.length === 0) {
602
+ // console.log('🔄 [VectorSearch] Handling empty search term');
603
+
604
+ // Reset search state
605
+ currentSearchResults = [];
606
+ activeSearchIndex = -1; // Reset active index
607
+
608
+ // Reset batch index to start fresh
609
+ if (vectorManagerInstance) {
610
+ vectorManagerInstance.resetBatchIndex(controllerId);
611
+
612
+ // Get vectors for current category
613
+ let vectors: VectorData[] = [];
614
+
615
+ if (currentFilter === 'All') {
616
+ try {
617
+ // Get standard vectors first - add null check
618
+ const standardVectors = vectorManagerInstance.getCachedVectorList()
619
+ ?.filter(vector => !vector.isCustom)
620
+ ?.slice(0, VectorManager.initialVectorNumber) || [];
621
+
622
+ // Get custom vectors - add null check
623
+ const customVectors = vectorManagerInstance.getCachedVectorList()
624
+ ?.filter(vector => vector.isCustom) || [];
625
+
626
+ // Combine them with custom vectors at the end
627
+ vectors = [...standardVectors, ...customVectors];
628
+ } catch (error) {
629
+ console.error('❌ [VectorSearch] Error getting cached vectors:', error);
630
+ vectors = [];
631
+ }
632
+ } else if (currentFilter === 'Mine') {
633
+ vectors = vectorManagerInstance.getPreloadedVectors(currentFilter as VectorItem['category']);
634
+ } else {
635
+ // For other categories, filter out custom vectors
636
+ vectors = vectorManagerInstance.getPreloadedVectors(currentFilter as VectorItem['category'])
637
+ .filter(vector => !vector.isCustom);
638
+ }
639
+
640
+ // Update all vectors
641
+ allVectors = vectors;
642
+
643
+ // Update filtered vectors directly to ensure UI updates
644
+ updateFilteredVectors();
645
+ }
646
+
647
+ // Reset scroll position
648
+ if (vectorListContainer) {
649
+ vectorListContainer.scrollTop = 0;
650
+ }
651
+
652
+ // Ensure the observer is set up after the DOM updates
653
+ setTimeout(() => {
654
+ setupVectorObserver();
655
+ // console.log('🔄 [VectorSearch] Observer setup after search clear');
656
+ }, 50);
657
+ }
658
+ }
659
+
660
+ /**
661
+ * Manages intersection observer for lazy loading vectors
662
+ * Only loads vectors when they become visible in viewport
663
+ */
664
+ function setupVectorObserver() {
665
+ if (!vectorListContainer) {
666
+ // console.log('⚠️ [VectorInput] Vector list container not found, retrying...');
667
+ return scrollTimeout = setTimeout(() => {
668
+ if (vectorListContainer) {
669
+ setupVectorObserver();
670
+ }
671
+ }, 50);
672
+ }
673
+
674
+ // If we have no vectors to observe, try to update filtered vectors first
675
+ if (!filteredVectors.length) {
676
+ // console.log('⚠️ [VectorInput] No vectors available to observe, updating filtered vectors');
677
+ updateFilteredVectors();
678
+
679
+ // If we still have no vectors, log a warning and return
680
+ if (!filteredVectors.length) {
681
+ // console.warn('❌ [VectorInput] No vectors available to observe after update');
682
+
683
+ // If we're in a category that should have vectors, try to reload them
684
+ if (vectorManagerInstance) {
685
+ // console.log('🔄 [VectorInput] Attempting to reload vectors for category:', currentFilter);
686
+
687
+ // Reset batch index
688
+ vectorManagerInstance.resetBatchIndex(controllerId);
689
+
690
+ // Get vectors for current category
691
+ let vectors: VectorData[] = [];
692
+
693
+ try {
694
+ if (currentFilter === 'All') {
695
+ // Get standard vectors first - add null check
696
+ const standardVectors = vectorManagerInstance.getCachedVectorList()
697
+ ?.filter(vector => !vector.isCustom)
698
+ ?.slice(0, VectorManager.initialVectorNumber) || [];
699
+
700
+ // Get custom vectors - add null check
701
+ const customVectors = vectorManagerInstance.getCachedVectorList()
702
+ ?.filter(vector => vector.isCustom) || [];
703
+
704
+ // Combine them with custom vectors at the end
705
+ vectors = [...standardVectors, ...customVectors];
706
+ } else if (currentFilter === 'Mine') {
707
+ vectors = vectorManagerInstance.getPreloadedVectors(currentFilter as VectorItem['category']);
708
+ } else {
709
+ // For other categories, filter out custom vectors
710
+ vectors = vectorManagerInstance.getPreloadedVectors(currentFilter as VectorItem['category'])
711
+ .filter(vector => !vector.isCustom);
712
+ }
713
+ } catch (error) {
714
+ console.error('❌ [VectorInput] Error getting cached vectors:', error);
715
+ vectors = [];
716
+ }
717
+
718
+ // Update all vectors
719
+ allVectors = vectors;
720
+ updateFilteredVectors();
721
+ }
722
+
723
+ return;
724
+ }
725
+ }
726
+
727
+ // console.log('🔍 [VectorInput] Setting up vector observer for', filteredVectors.length, 'vectors');
728
+
729
+ // Get the observer from VectorManager
730
+ const observer = vectorManagerInstance?.getObserver(vectorListContainer);
731
+ if (!observer) {
732
+ // console.warn('❌ [VectorInput] Failed to create observer');
733
+ return;
734
+ }
735
+
736
+ // Disconnect existing observer if it exists
737
+ if (vectorObserver) {
738
+ vectorObserver.disconnect();
739
+ }
740
+
741
+ vectorObserver = observer;
742
+
743
+ // Observe all existing vector elements - add null check
744
+ if (vectorListContainer) {
745
+ const vectorElements = vectorListContainer.querySelectorAll('[data-vector]');
746
+ // console.log('📝 [VectorInput] Observing', vectorElements.length, 'vector elements');
747
+
748
+ // Use a more efficient approach to observe elements
749
+ // Batch the observation process to reduce performance impact
750
+ const batchSize = 20; // Process 20 elements at a time
751
+ let currentIndex = 0;
752
+
753
+ const observeBatch = () => {
754
+ const endIndex = Math.min(currentIndex + batchSize, vectorElements.length);
755
+
756
+ for (let i = currentIndex; i < endIndex; i++) {
757
+ observer.observe(vectorElements[i]);
758
+ }
759
+
760
+ currentIndex = endIndex;
761
+
762
+ if (currentIndex < vectorElements.length) {
763
+ // Process next batch in the next animation frame
764
+ requestAnimationFrame(observeBatch);
765
+ }
766
+ };
767
+
768
+ // Start the batched observation process
769
+ observeBatch();
770
+ }
771
+ }
772
+
773
+ /**
774
+ * Handles keyboard navigation in search results
775
+ */
776
+ function handleSearchKeydown(event: KeyboardEvent | CustomEvent<KeyboardEvent>) {
777
+ // Ensure we have a KeyboardEvent
778
+ const keyEvent = event instanceof KeyboardEvent ? event : event.detail;
779
+
780
+ // Prevent handling backspace when search is already empty
781
+ if (keyEvent.key === 'Backspace' && !searchTerm) {
782
+ keyEvent.preventDefault();
783
+ return;
784
+ }
785
+
786
+ if (!filteredVectors.length) return;
787
+
788
+ if (keyEvent.key === 'ArrowDown') {
789
+ keyEvent.preventDefault();
790
+ handleArrowClick('down');
791
+ } else if (keyEvent.key === 'ArrowUp') {
792
+ keyEvent.preventDefault();
793
+ handleArrowClick('up');
794
+ } else if (keyEvent.key === 'Enter' && activeSearchIndex !== -1) {
795
+ keyEvent.preventDefault();
796
+ handleVectorSelect(filteredVectors[activeSearchIndex]);
797
+ }
798
+ }
799
+
800
+ /**
801
+ * Updates arrow navigation in search results
802
+ */
803
+ function handleArrowClick(direction: 'up' | 'down') {
804
+ if (!filteredVectors.length) return;
805
+
806
+ if (direction === 'down') {
807
+ activeSearchIndex = (activeSearchIndex + 1) % filteredVectors.length;
808
+ } else {
809
+ activeSearchIndex = activeSearchIndex <= 0 ? filteredVectors.length - 1 : activeSearchIndex - 1;
810
+ }
811
+
812
+ // Ensure the selected item is visible and add null check for vectorListContainer
813
+ if (vectorListContainer && activeSearchIndex >= 0 && activeSearchIndex < filteredVectors.length) {
814
+ const selectedElement = vectorListContainer.querySelector(`[data-vector="${filteredVectors[activeSearchIndex].name}"]`);
815
+ selectedElement?.scrollIntoView({ block: 'nearest' });
816
+ }
817
+ }
818
+
819
+ /**
820
+ * Handles vector selection and updates recent vectors
821
+ */
822
+ function handleVectorSelect(vector: VectorItem | RecentVector) {
823
+ // First check if the vector has a loading error
824
+ if (hasSvgLoadError(vector.name)) {
825
+ console.warn(`⚠️ [VectorInput] Prevented selection of vector with loading error: ${vector.name}`);
826
+ return; // Don't select vectors that failed to load
827
+ }
828
+
829
+ if (onChange) {
830
+ const vectorValue = {
831
+ name: vector.name,
832
+ category: vector.category,
833
+ url: vector.url
834
+ };
835
+
836
+ // Pass the vector value object to onChange
837
+ onChange(vectorValue);
838
+
839
+ // console.log('✅ [VectorInput] Vector selected:', {
840
+ // vector: vectorValue
841
+ // });
842
+ }
843
+ }
844
+
845
+ /**
846
+ * Handles vector list scrolling
847
+ */
848
+ function handleVectorListScroll(event: Event) {
849
+ // Ensure target is an HTMLElement
850
+ if (!(event.target instanceof HTMLElement)) {
851
+ return;
852
+ }
853
+
854
+ // Prevent event propagation to stop global scroll handler from closing the modal
855
+ event.stopPropagation();
856
+
857
+ // Handle vector loading for visible vectors
858
+ vectorManagerInstance?.handleScroll();
859
+
860
+ const container = event.target;
861
+ const { scrollTop, scrollHeight, clientHeight } = container;
862
+ // Increase the threshold to detect when we're closer to the bottom
863
+ const scrolledToBottom = (scrollHeight - (scrollTop + clientHeight)) <= 100;
864
+
865
+ // Add debug log to help diagnose scrolling issues
866
+ // console.log('📜 [VectorScroll] Scroll position:', {
867
+ // scrollTop,
868
+ // scrollHeight,
869
+ // clientHeight,
870
+ // remainingScroll: scrollHeight - (scrollTop + clientHeight),
871
+ // scrolledToBottom,
872
+ // hasSearchTerm: Boolean(searchTerm),
873
+ // searchResultsCount: currentSearchResults.length,
874
+ // displayedResults: filteredVectors.length
875
+ // });
876
+
877
+ // Use requestAnimationFrame to optimize scroll handling
878
+ if (scrolledToBottom) {
879
+ // Use requestAnimationFrame to avoid blocking the UI thread
880
+ requestAnimationFrame(() => {
881
+ if (searchTerm && currentSearchResults.length > 0) {
882
+ // Calculate next batch using SEARCH_BATCH_SIZE
883
+ const nextBatchStart = filteredVectors.length;
884
+ const nextBatch = currentSearchResults.slice(
885
+ nextBatchStart,
886
+ nextBatchStart + SEARCH_BATCH_SIZE
887
+ );
888
+
889
+ if (nextBatch.length > 0) {
890
+ // Add new batch to filtered vectors
891
+ filteredVectors = [...filteredVectors, ...nextBatch];
892
+
893
+ // Setup observer for new vectors
894
+ scrollTimeout = setTimeout(() => {
895
+ if (vectorListContainer) {
896
+ setupVectorObserver();
897
+ }
898
+ }, 0);
899
+
900
+ console.log('📝 [VectorSearch] Loaded next batch:', {
901
+ batchSize: nextBatch.length,
902
+ totalDisplayed: filteredVectors.length,
903
+ remainingVectors: currentSearchResults.length - filteredVectors.length,
904
+ nextBatchSize: SEARCH_BATCH_SIZE
905
+ });
906
+ }
907
+ } else {
908
+ // Normal vector list scrolling
909
+ if (!isLoadingBatch && vectorManagerInstance?.hasMoreVectors(controllerId, currentFilter)) {
910
+ loadNextBatch();
911
+ }
912
+ }
913
+ });
914
+ }
915
+ }
916
+
917
+ /**
918
+ * Loads the next batch of vectors
919
+ */
920
+ async function loadNextBatch() {
921
+ if (isLoadingBatch) return;
922
+
923
+ try {
924
+ isLoadingBatch = true;
925
+
926
+ // Get next batch for this controller
927
+ const newVectors = vectorManagerInstance.getNextBatch(controllerId, currentFilter);
928
+
929
+ // Add to existing vectors, ensuring uniqueness
930
+ const combinedVectors = [...allVectors, ...newVectors];
931
+ allVectors = Array.from(
932
+ new Map(combinedVectors.map(vector => [vector.name, vector])).values()
933
+ );
934
+
935
+ // Update filtered vectors while preserving current filter
936
+ updateFilteredVectors();
937
+
938
+ // console.log('🔄 [VectorInput] Loaded new batch:', {
939
+ // controllerId,
940
+ // filter: currentFilter,
941
+ // newVectors: newVectors.length,
942
+ // totalVectors: allVectors.length,
943
+ // filteredVectors: filteredVectors.length
944
+ // });
945
+
946
+ } catch (error) {
947
+ console.error('❌ [VectorInput] Error loading vector batch:', error);
948
+ } finally {
949
+ isLoadingBatch = false;
950
+ }
951
+ }
952
+
953
+ onDestroy(() => {
954
+ window.removeEventListener('scroll', handleGlobalScroll, true);
955
+ window.removeEventListener('touchstart', () => {});
956
+ window.removeEventListener('touchmove', () => {});
957
+
958
+ // Remove vector load error listener using the stored reference
959
+ const vectorErrorHandler = (window as any)['vectorErrorHandler'];
960
+ if (vectorErrorHandler) {
961
+ document.removeEventListener('vector-load-error', vectorErrorHandler);
962
+ delete (window as any)['vectorErrorHandler'];
963
+ }
964
+
965
+ if (vectorObserver) {
966
+ vectorObserver.disconnect();
967
+ }
968
+ // Clear any timeouts
969
+ if (scrollTimeout) {
970
+ clearTimeout(scrollTimeout);
971
+ }
972
+ showVectorModal = false;
973
+
974
+ console.log('🧹 [VectorInput] Cleanup complete:', {
975
+ controllerId,
976
+ lastFilter: currentFilter
977
+ });
978
+ });
979
+
980
+ /**
981
+ * Opens the file browser for vector selection
982
+ */
983
+ async function openFileBrowser() {
984
+ console.log('🔍 [VectorInput] Opening file browser for vectors');
985
+
986
+ try {
987
+ // Get the current vector's ID if it exists in our custom vectors
988
+ const selectedId = value?.name ?
989
+ vectorManagerInstance?.getCachedVectorList()
990
+ .find(v => v.name === value.name && v.category === 'Mine')?.name
991
+ : undefined;
992
+
993
+ console.log('🔍 [VectorInput] Opening file browser:', {
994
+ fileTypes: ['svg'],
995
+ selectedId
996
+ });
997
+
998
+ // TODO: Implement file browser functionality
999
+ // This will be similar to FontInputController but for SVG files
1000
+ } catch (error) {
1001
+ console.error('❌ [VectorInput] Error opening file browser:', error);
1002
+ }
1003
+ }
1004
+
1005
+ /**
1006
+ * Handles category filter changes
1007
+ */
1008
+ async function handleFilterChange(filter: FilterType) {
1009
+ if (currentFilter !== filter) {
1010
+ currentFilter = filter;
1011
+ lastActiveFilter = filter; // Remember the last active filter
1012
+
1013
+ if (browser) {
1014
+ localStorage.setItem(VECTOR_FILTER_KEY(controllerId), filter);
1015
+ }
1016
+
1017
+ if (vectorManagerInstance) {
1018
+ vectorManagerInstance.resetBatchIndex(controllerId);
1019
+
1020
+ try {
1021
+ let vectors: VectorData[] = [];
1022
+
1023
+ if (filter === 'All') {
1024
+ // Get standard vectors first - add null check
1025
+ const standardVectors = vectorManagerInstance.getCachedVectorList()
1026
+ ?.filter(vector => !vector.isCustom)
1027
+ ?.slice(0, searchBatchIndex) || [];
1028
+
1029
+ // Get custom vectors - add null check
1030
+ const customVectors = vectorManagerInstance.getCachedVectorList()
1031
+ ?.filter(vector => vector.isCustom) || [];
1032
+
1033
+ // Combine them with custom vectors at the end
1034
+ vectors = [...standardVectors, ...customVectors];
1035
+ } else if (filter === 'Mine') {
1036
+ vectors = vectorManagerInstance.getPreloadedVectors(filter as VectorItem['category']);
1037
+ } else {
1038
+ // For other categories, filter out custom vectors
1039
+ vectors = vectorManagerInstance.getPreloadedVectors(filter as VectorItem['category'])
1040
+ .filter(vector => !vector.isCustom);
1041
+ }
1042
+
1043
+ allVectors = vectors;
1044
+ updateFilteredVectors();
1045
+
1046
+ // Setup observer in next tick
1047
+ scrollTimeout = setTimeout(() => {
1048
+ if (vectorListContainer) {
1049
+ setupVectorObserver();
1050
+ }
1051
+ }, 0);
1052
+
1053
+ if (vectorListContainer) {
1054
+ vectorListContainer.scrollTop = 0;
1055
+ }
1056
+
1057
+ // console.log('🔄 [VectorInput] Category changed:', {
1058
+ // filter,
1059
+ // vectors: allVectors.length,
1060
+ // filteredVectors: filteredVectors.length,
1061
+ // customVectors: allVectors.filter(v => v.isCustom).length
1062
+ // });
1063
+ } catch (error) {
1064
+ console.error('❌ [VectorInput] Error changing category:', error);
1065
+ }
1066
+ }
1067
+ }
1068
+ }
1069
+
1070
+ /**
1071
+ * Creates a custom action for vector observation
1072
+ */
1073
+ function observeVector(node: HTMLElement) {
1074
+ if (vectorObserver) {
1075
+ vectorObserver.observe(node);
1076
+ }
1077
+
1078
+ return {
1079
+ destroy() {
1080
+ if (vectorObserver) {
1081
+ vectorObserver.unobserve(node);
1082
+ }
1083
+ }
1084
+ };
1085
+ }
1086
+
1087
+ /**
1088
+ * Highlights search term matches in vector names
1089
+ */
1090
+ function highlightMatch(name: string, term: string) {
1091
+ if (!term) return name;
1092
+ const nameLower = name.toLowerCase();
1093
+ const termLower = term.toLowerCase().trim();
1094
+
1095
+ // If the name contains the search term, highlight that part in blue
1096
+ if (termLower && nameLower.includes(termLower)) {
1097
+ const index = nameLower.indexOf(termLower);
1098
+ const beforeMatch = name.slice(0, index);
1099
+ const match = name.slice(index, index + termLower.length);
1100
+ const afterMatch = name.slice(index + termLower.length);
1101
+
1102
+ return `<span style="color: hsl(var(--grey7))">${beforeMatch}<span style="color: #45BCFE">${match}</span>${afterMatch}</span>`;
1103
+ }
1104
+
1105
+ // Default case - just return the name in grey7 color
1106
+ return `<span style="color: hsl(var(--grey7))">${name}</span>`;
1107
+ }
1108
+
1109
+
1110
+ /**
1111
+ * Removes a custom vector
1112
+ */
1113
+ async function removeVector(name: string) {
1114
+ try {
1115
+ // Remove from manager
1116
+ await vectorManagerInstance?.removeCustomVector(name);
1117
+
1118
+ // If the removed vector was selected, clear the selection
1119
+ if (value?.name === name && onChange) {
1120
+ onChange(null);
1121
+ }
1122
+
1123
+ // Remove from recent vectors if present
1124
+ const updatedRecentVectors = recentVectors.filter(v => v.name !== name);
1125
+ recentVectors = updatedRecentVectors;
1126
+
1127
+ // Add lastUsed property before updating store
1128
+ const recentVectorsWithLastUsed = updatedRecentVectors.map(v => ({
1129
+ ...v,
1130
+ lastUsed: Date.now()
1131
+ }));
1132
+ recentVectorsStore.set(recentVectorsWithLastUsed);
1133
+
1134
+ // Remove from all vectors
1135
+ allVectors = allVectors.filter(v => v.name !== name);
1136
+
1137
+ // Update filtered vectors
1138
+ updateFilteredVectors();
1139
+
1140
+ // Wait for DOM update
1141
+ await tick();
1142
+
1143
+ // Re-setup observer for remaining vectors
1144
+ setupVectorObserver();
1145
+
1146
+ // console.log('✅ [VectorInput] Vector removed:', {
1147
+ // name,
1148
+ // remainingVectors: allVectors.length,
1149
+ // filteredVectors: filteredVectors.length,
1150
+ // recentVectors: recentVectors.length
1151
+ // });
1152
+ } catch (error) {
1153
+ console.error('❌ [VectorInput] Error removing vector:', error);
1154
+ }
1155
+ }
1156
+
1157
+ // Add scroll listener with passive option
1158
+ window.addEventListener('scroll', handleGlobalScroll, {
1159
+ capture: true,
1160
+ passive: true
1161
+ });
1162
+
1163
+ // Add touch listeners with passive option
1164
+ window.addEventListener('touchstart', () => {}, { passive: true });
1165
+ window.addEventListener('touchmove', () => {}, { passive: true });
1166
+
1167
+ // Add to VectorInputController.svelte
1168
+ function handleToggle() {
1169
+ const beforeState = $openDropdowns?.has(controllerId);
1170
+ console.log('🟡 [VectorInput] Toggle start:', {
1171
+ controllerId,
1172
+ beforeState,
1173
+ openDropdowns: Array.from($openDropdowns || new Set())
1174
+ });
1175
+
1176
+ toggleDropdown(controllerId);
1177
+
1178
+ const afterState = $openDropdowns?.has(controllerId);
1179
+ console.log('🟢 [VectorInput] Toggle complete:', {
1180
+ controllerId,
1181
+ beforeState,
1182
+ afterState,
1183
+ openDropdowns: Array.from($openDropdowns || new Set())
1184
+ });
1185
+ }
1186
+
1187
+ function closeDropdown() {
1188
+ if (open) {
1189
+ handleToggle();
1190
+ }
1191
+ }
1192
+
1193
+ function handleGlobalScroll(event: Event) {
1194
+ // Don't close if scrolling happens within our containers
1195
+ if ((event.target instanceof HTMLElement)) {
1196
+ const target = event.target;
1197
+
1198
+ // Check if scroll happened inside our containers
1199
+ const isInsideVectorList = target === vectorListContainer || target.closest(`#vector-list-${controllerId}`);
1200
+ const isInsidePopover = target.closest('.vector-popover-content') || target.closest('.vector-input-popover');
1201
+ const isInsideCommand = target.closest('[cmdk-root]');
1202
+
1203
+ // Log scroll event for debugging
1204
+ // console.log('🔍 [VectorInput] Scroll event:', {
1205
+ // target: target.id || target.className,
1206
+ // isInsideVectorList,
1207
+ // isInsidePopover,
1208
+ // isInsideCommand,
1209
+ // shouldStayOpen: isInsideVectorList || isInsidePopover || isInsideCommand
1210
+ // });
1211
+
1212
+ // If scrolling is inside any of our containers, don't close
1213
+ if (isInsideVectorList || isInsidePopover || isInsideCommand) {
1214
+ return;
1215
+ }
1216
+
1217
+ // If scrolling outside our containers, close dropdowns
1218
+ if ($openDropdowns?.has(controllerId)) {
1219
+ console.log('🔴 [VectorInput] Closing dropdown due to external scroll');
1220
+ toggleDropdown(controllerId);
1221
+ }
1222
+ }
1223
+ }
1224
+
1225
+ // Add to VectorInputController.svelte
1226
+ $: open = $openDropdowns.has(controllerId) || false;
1227
+
1228
+ // Add after filteredVectors.length > 0 check
1229
+ $: if (filteredVectors.length > 0 && vectorListContainer) {
1230
+ // console.log('📝 [VectorInput] Vectors loaded, setting up observer...');
1231
+ setupVectorObserver();
1232
+ }
1233
+
1234
+ // Add these event handlers
1235
+ function handleButtonClick() {
1236
+ const currentState = $openDropdowns?.has(controllerId);
1237
+ // console.log('🔵 [VectorInput] Button click start:', {
1238
+ // controllerId,
1239
+ // currentState,
1240
+ // openDropdowns: Array.from($openDropdowns || new Set()),
1241
+ // source: 'button'
1242
+ // });
1243
+
1244
+ if (!currentState) {
1245
+ // console.log('🎯 [VectorInput] Triggering focus');
1246
+ onFocus();
1247
+ }
1248
+ // toggleDropdown(controllerId);
1249
+ }
1250
+
1251
+ function handleKeyDown(event: KeyboardEvent) {
1252
+ if (event.key === 'Enter' || event.key === ' ') {
1253
+ event.preventDefault();
1254
+ handleButtonClick();
1255
+ }
1256
+ }
1257
+
1258
+ // Add this function
1259
+ function measureWidth(node: HTMLElement) {
1260
+ const resizeObserver = new ResizeObserver(() => {
1261
+ buttonWidth = node.offsetWidth;
1262
+ });
1263
+
1264
+ resizeObserver.observe(node);
1265
+
1266
+ return {
1267
+ destroy() {
1268
+ resizeObserver.disconnect();
1269
+ }
1270
+ };
1271
+ }
1272
+
1273
+ // Add this reactive statement
1274
+ $: popoverStyle = `width: ${buttonWidth}px`;
1275
+
1276
+ // Update the trash click handler
1277
+ function handleTrashClick(vector: VectorItem, event: MouseEvent) {
1278
+ event.preventDefault();
1279
+ event.stopPropagation();
1280
+ removeVector(vector.name);
1281
+ }
1282
+
1283
+ // Create a Svelte action to handle image error events
1284
+ function handleImageError(node: HTMLElement, vectorName: string) {
1285
+ const errorHandler = (event: Event) => {
1286
+ console.log(`❌ [VectorInput] Image failed to load: ${vectorName}`);
1287
+ svgLoadErrors.set(vectorName, true);
1288
+ svgLoadErrors = new Map(svgLoadErrors);
1289
+
1290
+ if (node instanceof HTMLImageElement) {
1291
+ node.style.display = 'none';
1292
+ }
1293
+ };
1294
+
1295
+ node.addEventListener('error', errorHandler);
1296
+
1297
+ return {
1298
+ destroy() {
1299
+ node.removeEventListener('error', errorHandler);
1300
+ },
1301
+ update(newVectorName: string) {
1302
+ vectorName = newVectorName;
1303
+ }
1304
+ };
1305
+ }
1306
+
1307
+ // Functions for RecentItemsSection and ItemGrid
1308
+ function renderVectorItem(vector: VectorItem) {
1309
+ // If we already know this SVG has a load error, show fallback
1310
+ if (hasSvgLoadError(vector.name)) {
1311
+ return {
1312
+ html: `
1313
+ <div class="flex items-center justify-center w-full h-full bg-grey1 rounded-large widgetic-radius">
1314
+ <div class="flex flex-col items-center justify-center gap-1 p-2 text-center">
1315
+ <span class="text-error">
1316
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1317
+ <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"></path>
1318
+ <path d="M12 9v4"></path>
1319
+ <path d="M12 17h.01"></path>
1320
+ </svg>
1321
+ </span>
1322
+ <span class="caption text-grey5">Failed to load</span>
1323
+ </div>
1324
+ </div>
1325
+ `
1326
+ };
1327
+ }
1328
+
1329
+ return {
1330
+ html: `
1331
+ <div class="flex items-center justify-center w-full h-full vector-img-container">
1332
+ <img
1333
+ src="${vector.url}"
1334
+ alt="${vector.name}"
1335
+ class="w-[72px] h-[72px] object-contain"
1336
+ draggable="false"
1337
+ onerror="this.style.display='none'; document.dispatchEvent(new CustomEvent('vector-load-error', {detail: this.alt, bubbles: true})); console.error('❌ [VectorInput] Vector failed to load:', this.alt, 'URL:', this.src);"
1338
+ />
1339
+ </div>
1340
+ `
1341
+ };
1342
+ }
1343
+
1344
+ function isVectorSelected(vector: VectorItem) {
1345
+ return Boolean(value?.name === vector.name);
1346
+ }
1347
+
1348
+ function isCustomVector(vector: VectorItem) {
1349
+ return vector.category === 'Mine' && Boolean(vector.isCustom);
1350
+ }
1351
+
1352
+ function getVectorKey(vector: VectorItem) {
1353
+ // Create a unique key by combining name with URL or category
1354
+ // This prevents duplicate keys when two vectors have the same name
1355
+ return vector.url ? `${vector.name}-${vector.url}` : `${vector.name}-${vector.category}`;
1356
+ }
1357
+
1358
+ function handleVectorDelete(vector: VectorItem, event: Event) {
1359
+ // Convert Event to MouseEvent or create a synthetic one if needed
1360
+ const mouseEvent = event instanceof MouseEvent
1361
+ ? event
1362
+ : new MouseEvent('click', {
1363
+ bubbles: true,
1364
+ cancelable: true,
1365
+ view: window
1366
+ });
1367
+
1368
+ handleTrashClick(vector, mouseEvent);
1369
+ }
1370
+
1371
+ // Update when vector manager changes
1372
+ $: if (vectorManagerInstance) {
1373
+ hasMoreVectors = vectorManagerInstance.hasMoreVectors(controllerId, currentFilter);
1374
+ }
1375
+
1376
+ // Add a handleSearchImmediate function to process search terms immediately without debounce
1377
+ async function handleSearchImmediate(value: string) {
1378
+ if (!searchTerm && value) {
1379
+ // If this is a new search, save current filter
1380
+ lastActiveFilter = currentFilter;
1381
+ currentFilter = 'All'; // Reset to All when starting a search
1382
+ if (browser) {
1383
+ localStorage.setItem(VECTOR_FILTER_KEY(controllerId), 'All');
1384
+ }
1385
+ }
1386
+
1387
+ // Just load the complete vector list if we're searching
1388
+ const completeVectorList = await vectorManagerInstance?.getCompleteVectorList(true) || [];
1389
+ allVectors = completeVectorList;
1390
+
1391
+ // Apply filtering with the new search term
1392
+ updateFilteredVectors();
1393
+
1394
+ // Reset scroll position
1395
+ if (vectorListContainer) {
1396
+ vectorListContainer.scrollTop = 0;
1397
+ }
1398
+ }
1399
+
1400
+ // Update the paste handler to use handleSearchImmediate for immediate processing
1401
+ function handlePaste(event: ClipboardEvent) {
1402
+ const pastedText = event.clipboardData?.getData('text/plain') || '';
1403
+
1404
+ // Don't prevent default - let the paste happen naturally
1405
+ // Then trigger an immediate search update with the pasted content
1406
+ // searchTerm will be updated through binding
1407
+
1408
+ // Schedule the handleSearchImmediate for the next tick to ensure
1409
+ // the input value is updated with the pasted content first
1410
+ setTimeout(() => {
1411
+ handleSearchImmediate(pastedText);
1412
+ }, 0);
1413
+ }
1414
+
1415
+ /**
1416
+ * Preloads a batch of vectors to detect loading errors early
1417
+ */
1418
+ async function preloadVectors(vectors: VectorItem[]) {
1419
+ const batchSize = 10;
1420
+ const preloadChunks = (chunks: VectorItem[][]) => {
1421
+ if (chunks.length === 0) return;
1422
+
1423
+ const chunk = chunks.shift()!;
1424
+
1425
+ // Process this chunk
1426
+ chunk.forEach(vector => {
1427
+ const img = new Image();
1428
+ img.onload = () => {
1429
+ // Successfully loaded
1430
+ };
1431
+ img.onerror = () => {
1432
+ console.error(`❌ [VectorInput] Preload failed for vector: "${vector.name}" URL: ${vector.url}`);
1433
+ svgLoadErrors.set(vector.name, true);
1434
+ svgLoadErrors = new Map(svgLoadErrors);
1435
+ };
1436
+ img.src = vector.url;
1437
+ });
1438
+
1439
+ // Process next chunk after a small delay
1440
+ if (chunks.length > 0) {
1441
+ setTimeout(() => preloadChunks(chunks), 100);
1442
+ }
1443
+ };
1444
+
1445
+ // Split vectors into batches to avoid overwhelming the browser
1446
+ const chunks: VectorItem[][] = [];
1447
+ for (let i = 0; i < vectors.length; i += batchSize) {
1448
+ chunks.push(vectors.slice(i, i + batchSize));
1449
+ }
1450
+
1451
+ // Start preloading
1452
+ preloadChunks(chunks);
1453
+ }
1454
+ </script>
1455
+
1456
+ <div class="flex flex-col gap-2 widgetic-colors">
1457
+ <div class="flex items-center w-full relative">
1458
+ <div class="w-[185px] flex-shrink-0 ml-auto">
1459
+ <InputControllerHeader
1460
+ label={label}
1461
+ helpText={description}
1462
+ />
1463
+ </div>
1464
+
1465
+ <div class="absolute right-0">
1466
+ <div
1467
+ class="h-[48px] w-[185px] rounded-large border border-grey2 flex items-center bg-grey1 transition-colors duration-200 hover:border-forest-green"
1468
+ onfocusin={onFocus}
1469
+ >
1470
+ <Popover.Root
1471
+ open={$openDropdowns?.has(controllerId)}
1472
+ onOpenChange={(isOpen) => {
1473
+ if (isOpen !== $openDropdowns?.has(controllerId)) {
1474
+ if (isOpen) {
1475
+ // Check if the selected vector image is valid
1476
+ if (value?.name && value.url) {
1477
+ // Create a temporary image to test if it loads
1478
+ const testImg = new Image();
1479
+ testImg.onload = () => {
1480
+ // Remove from error list if it was there
1481
+ if (hasSvgLoadError(value.name)) {
1482
+ svgLoadErrors.delete(value.name);
1483
+ svgLoadErrors = new Map(svgLoadErrors);
1484
+ console.log(`✅ [VectorInput] Vector now loads successfully: ${value.name}`);
1485
+ }
1486
+ };
1487
+ testImg.onerror = () => {
1488
+ // Mark as error if it fails to load
1489
+ if (!hasSvgLoadError(value.name)) {
1490
+ svgLoadErrors.set(value.name, true);
1491
+ svgLoadErrors = new Map(svgLoadErrors);
1492
+ console.error(`❌ [VectorInput] Current vector failed to load: ${value.name}, URL: ${value.url}`);
1493
+ }
1494
+ };
1495
+ testImg.src = value.url;
1496
+ }
1497
+ } else if (!isOpen && value?.name) {
1498
+ vectorUtils.addRecentVector({
1499
+ name: value.name,
1500
+ category: vectorCategories.normalize(value.category) as VectorItem['category'],
1501
+ url: value.url,
1502
+ isCustom: vectorManagerInstance?.isCustomVector(value.name) || false
1503
+ });
1504
+ }
1505
+
1506
+ toggleDropdown(controllerId);
1507
+ }
1508
+ }}
1509
+ >
1510
+ <Popover.Trigger
1511
+ class="w-full h-full flex items-center border-0 bg-transparent hover:bg-transparent focus:bg-transparent pl-3 pr-[16px] group"
1512
+ onclick={handleButtonClick}
1513
+ onkeydown={handleKeyDown}
1514
+ onmouseenter={() => {
1515
+ // Preload vectors when hovering over the button
1516
+ // This reduces the delay when actually clicking to open
1517
+ if (!$openDropdowns?.has(controllerId) && filteredVectors.length === 0 && allVectors.length > 0) {
1518
+ updateFilteredVectors();
1519
+ // console.log('🔄 [VectorInput] Preloading vectors on hover');
1520
+ }
1521
+ }}
1522
+ aria-expanded={open}
1523
+ aria-haspopup="listbox"
1524
+ aria-controls={`vector-list-${controllerId}`}
1525
+ >
1526
+ <div class="flex items-center gap-2 truncate flex-1">
1527
+ {#if value?.url}
1528
+ {#if hasSvgLoadError(value.name)}
1529
+ <div class="w-5 h-5 flex items-center justify-center">
1530
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-error">
1531
+ <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"></path>
1532
+ <path d="M12 9v4"></path>
1533
+ <path d="M12 17h.01"></path>
1534
+ </svg>
1535
+ </div>
1536
+ <span class="truncate text-left capitalize subtext text-grey7">
1537
+ {value?.name || "Select vector..."}
1538
+ </span>
1539
+ {:else}
1540
+ <!-- Use a simple img tag with Svelte action for error handling -->
1541
+ <img
1542
+ src={value.url}
1543
+ alt={value.name}
1544
+ class="w-5 h-5 object-contain"
1545
+ use:handleImageError={value.name}
1546
+ />
1547
+ <span class="truncate text-left capitalize subtext text-grey7">
1548
+ {value?.name || "Select vector..."}
1549
+ </span>
1550
+ {/if}
1551
+ {:else}
1552
+ <span class="truncate text-left subtext text-grey7">
1553
+ Select vector...
1554
+ </span>
1555
+ {/if}
1556
+ </div>
1557
+ <IconCaretDownFilled class="w-5 h-5 shrink-0 text-grey5 group-hover:text-grey7" />
1558
+ </Popover.Trigger>
1559
+
1560
+ <Popover.Content class="vector-input-popover w-[374px] h-[565px] !p-0 widgetic-colors widgetic-radius rounded-medium modal-shadow border border-grey2">
1561
+ <div class="flex flex-col h-full vector-popover-content">
1562
+ <!-- Title and upload icon -->
1563
+ <div class="relative pt-6 pb-[8px] px-4">
1564
+ <div class="h5 text-grey7 widgetic-colors">Vectors</div>
1565
+ <button
1566
+ type="button"
1567
+ class="absolute right-6 top-[26px] bg-transparent border-0 cursor-pointer p-0 rounded-full hover:bg-grey3 transition-all duration-300"
1568
+ onclick={() => {
1569
+ try {
1570
+ openFileBrowser();
1571
+ } catch (error) {
1572
+ console.error('Error opening file browser:', error);
1573
+ }
1574
+ }}
1575
+ >
1576
+ <IconCloudUpload class="w-6 h-6 text-grey7 hover:text-grey7 transition-colors duration-300 widgetic-colors" />
1577
+ </button>
1578
+ </div>
1579
+
1580
+ {#if description}
1581
+ <p class="px-4 -mt-2 mb-2 subtext text-grey5">{description}</p>
1582
+ {/if}
1583
+
1584
+ <!-- Search input -->
1585
+ <div class="relative px-4">
1586
+ <SearchInput
1587
+ placeholder="Search vectors..."
1588
+ bind:value={searchTerm}
1589
+ onInput={(e: Event) => {
1590
+ // Use debounced search for better performance
1591
+ debouncedSearch(e);
1592
+ }}
1593
+ onKeydown={handleSearchKeydown}
1594
+ onPaste={handlePaste}
1595
+ onClear={() => {
1596
+ searchTerm = ''; // Reset search term
1597
+
1598
+ // Restore the previous filter when clearing search
1599
+ currentFilter = lastActiveFilter;
1600
+ if (browser) {
1601
+ localStorage.setItem(VECTOR_FILTER_KEY(controllerId), lastActiveFilter);
1602
+ }
1603
+
1604
+ // Reset batch index to start fresh
1605
+ if (vectorManagerInstance) {
1606
+ vectorManagerInstance.resetBatchIndex(controllerId);
1607
+
1608
+ // Get vectors for current category
1609
+ let vectors: VectorData[] = [];
1610
+
1611
+ try {
1612
+ if (currentFilter === 'All') {
1613
+ // Get standard vectors first - add null check
1614
+ const standardVectors = vectorManagerInstance.getCachedVectorList()
1615
+ ?.filter(vector => !vector.isCustom)
1616
+ ?.slice(0, VectorManager.initialVectorNumber) || [];
1617
+
1618
+ // Get custom vectors - add null check
1619
+ const customVectors = vectorManagerInstance.getCachedVectorList()
1620
+ ?.filter(vector => vector.isCustom) || [];
1621
+
1622
+ // Combine them with custom vectors at the end
1623
+ vectors = [...standardVectors, ...customVectors];
1624
+ } else if (currentFilter === 'Mine') {
1625
+ vectors = vectorManagerInstance.getPreloadedVectors(currentFilter as VectorItem['category']);
1626
+ } else {
1627
+ // For other categories, filter out custom vectors
1628
+ vectors = vectorManagerInstance.getPreloadedVectors(currentFilter as VectorItem['category'])
1629
+ .filter(vector => !vector.isCustom);
1630
+ }
1631
+ } catch (error) {
1632
+ console.error('❌ [VectorSearch] Error getting cached vectors:', error);
1633
+ vectors = [];
1634
+ }
1635
+
1636
+ // Update all vectors
1637
+ allVectors = vectors;
1638
+
1639
+ // Update filtered vectors directly to ensure UI updates
1640
+ updateFilteredVectors();
1641
+ }
1642
+
1643
+ // Reset scroll position
1644
+ if (vectorListContainer) {
1645
+ vectorListContainer.scrollTop = 0;
1646
+ }
1647
+
1648
+ // Ensure the observer is set up after the DOM updates
1649
+ setTimeout(() => {
1650
+ setupVectorObserver();
1651
+ console.log('🔄 [VectorSearch] Observer setup after search clear');
1652
+ }, 50);
1653
+
1654
+ console.log('🔄 [VectorSearch] Search cleared');
1655
+ }}
1656
+ />
1657
+ </div>
1658
+
1659
+ <!-- Filter toggle group - sticky header -->
1660
+ {#if !searchTerm}
1661
+ <div class="sticky top-0 bg-white z-10">
1662
+ <CategoryTabs
1663
+ categories={filterOptions}
1664
+ activeCategory={currentFilter}
1665
+ onCategorySelect={handleFilterChange}
1666
+ filterVisible={!searchTerm}
1667
+ />
1668
+ </div>
1669
+ {/if}
1670
+
1671
+ <!-- Scrollable content -->
1672
+ <div
1673
+ class="flex-1 {filteredVectors.length > 0 ? 'overflow-y-auto' : 'overflow-hidden'} min-h-0 vector-list-container"
1674
+ bind:this={vectorListContainer}
1675
+ onscroll={(e: Event) => {
1676
+ e.stopPropagation();
1677
+ handleVectorListScroll(e);
1678
+ updateScrollState();
1679
+ }}
1680
+ id={`vector-list-${controllerId}`}
1681
+ role="listbox"
1682
+ aria-label="Vector list"
1683
+ >
1684
+ <div class="space-y-0">
1685
+ <!-- Recently used section - only show when not searching -->
1686
+ {#if recentVectors.length > 0 && !searchTerm}
1687
+ <RecentItemsSection
1688
+ recentItems={recentVectors}
1689
+ title="Recently used"
1690
+ onItemSelect={handleVectorSelect}
1691
+ onItemDelete={handleVectorDelete}
1692
+ isItemSelected={isVectorSelected}
1693
+ renderItem={renderVectorItem}
1694
+ isCustomItem={isCustomVector}
1695
+ getItemKey={getVectorKey}
1696
+ observeItem={observeVector}
1697
+ displayMode="grid"
1698
+ hideTrashIcon={hideTrashIcon}
1699
+ itemType="vector"
1700
+ />
1701
+ {/if}
1702
+
1703
+ <!-- All vectors section -->
1704
+ <div class="mt-6 mb-0">
1705
+ {#if filteredVectors.length > 0}
1706
+ <div id="vector-header-{controllerId}" class="sticky top-0 -mt-2 bg-white z-10 pt-2 {isScrolled ? 'border-b border-grey2' : 'border-b border-transparent'} transition-border">
1707
+ <h3 class="subtext text-grey7 px-4 pb-2">
1708
+ {searchTerm ? 'Search results' : 'More'}
1709
+ </h3>
1710
+ </div>
1711
+ {/if}
1712
+
1713
+ {#if filteredVectors.length === 0}
1714
+ <div class="empty-state-wrapper">
1715
+ <EmptyState
1716
+ message={searchTerm ? 'No vectors found' : 'No vectors available'}
1717
+ height="auto"
1718
+ />
1719
+ </div>
1720
+ {:else}
1721
+ <ItemGrid
1722
+ items={filteredVectors}
1723
+ containerRef={(node) => { vectorListContainer = node; }}
1724
+ onItemSelect={handleVectorSelect}
1725
+ isItemSelected={isVectorSelected}
1726
+ renderItem={renderVectorItem}
1727
+ getItemKey={getVectorKey}
1728
+ observeItem={observeVector}
1729
+ loadingMore={loadingMoreVectors}
1730
+ hasMoreItems={hasMoreVectors}
1731
+ loadNextBatch={() => {
1732
+ if (!isLoadingBatch && vectorManagerInstance?.hasMoreVectors(controllerId, currentFilter)) {
1733
+ loadNextBatch();
1734
+ }
1735
+ }}
1736
+ layoutClass="grid-cols-3"
1737
+ itemType="vector"
1738
+ />
1739
+ {/if}
1740
+ </div>
1741
+ </div>
1742
+ </div>
1743
+ </div>
1744
+ </Popover.Content>
1745
+ </Popover.Root>
1746
+ </div>
1747
+ </div>
1748
+ </div>
1749
+ </div>
1750
+
1751
+ <style>
1752
+ /* Update button styles */
1753
+ :global(button.bg-transparent) {
1754
+ background: transparent !important;
1755
+ border: none !important;
1756
+ }
1757
+
1758
+ :global(button.bg-transparent:hover),
1759
+ :global(button.bg-transparent:focus) {
1760
+ background-color: transparent !important;
1761
+ }
1762
+
1763
+ /* Add transition only for border appearance */
1764
+ :global(.transition-border) {
1765
+ transition: border-color 0.2s ease-in-out;
1766
+ }
1767
+
1768
+ /* Keep scrollbar styles */
1769
+ :global(.overflow-y-auto) {
1770
+ -webkit-overflow-scrolling: touch;
1771
+ scrollbar-width: thin;
1772
+ scrollbar-color: hsl(var(--grey3)) transparent;
1773
+ }
1774
+
1775
+ /* Target specific elements */
1776
+ :global(.vector-input-wrapper:hover ~ .vector-input-wrapper .group-hover\\:underline) {
1777
+ text-decoration: none;
1778
+ }
1779
+
1780
+ :global(.vector-input-wrapper:hover ~ .vector-input-wrapper) {
1781
+ pointer-events: none;
1782
+ }
1783
+
1784
+ /* Custom styling for vector items in grid - moved to RecentItemsSection.svelte */
1785
+
1786
+ /* Ensure vector list container properly handles scroll events */
1787
+ :global(.vector-list-container) {
1788
+ -webkit-overflow-scrolling: touch;
1789
+ scrollbar-width: thin;
1790
+ scrollbar-color: hsl(var(--grey3)) transparent;
1791
+ will-change: transform;
1792
+ transform: translateZ(0);
1793
+ backface-visibility: hidden;
1794
+ }
1795
+
1796
+ /* Ensure popover content stays open during scrolling */
1797
+ :global(.vector-input-popover) {
1798
+ z-index: 50;
1799
+ will-change: transform;
1800
+ transform: translateZ(0);
1801
+ backface-visibility: hidden;
1802
+ }
1803
+
1804
+ /* Optimize vector popover content rendering */
1805
+ :global(.vector-popover-content) {
1806
+ will-change: transform;
1807
+ transform: translateZ(0);
1808
+ backface-visibility: hidden;
1809
+ }
1810
+
1811
+ /* Prevent scrolling when showing empty state */
1812
+ .empty-state-wrapper {
1813
+ position: absolute;
1814
+ top: 0;
1815
+ left: 0;
1816
+ right: 0;
1817
+ bottom: 0;
1818
+ display: flex;
1819
+ align-items: center;
1820
+ justify-content: center;
1821
+ overflow: hidden;
1822
+ padding-top: 60px; /* Add padding to account for the header */
1823
+ padding-bottom: 60px; /* Balance the padding */
1824
+ }
1825
+ </style>
1826
+