@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.
- package/README.md +245 -0
- package/dist/assets/fonts.json +331 -0
- package/dist/assets/icons/ic-browser/audio.svg +7 -0
- package/dist/assets/icons/ic-browser/document.svg +11 -0
- package/dist/assets/icons/ic-browser/error.svg +5 -0
- package/dist/assets/icons/ic-browser/folder.svg +28 -0
- package/dist/assets/icons/ic-browser/player-pause.svg +1 -0
- package/dist/assets/icons/ic-browser/player-play.svg +1 -0
- package/dist/assets/icons/ic-browser/trash.svg +1 -0
- package/dist/assets/icons/ic-fixed-position/background-deselected.svg +3 -0
- package/dist/assets/icons/ic-fixed-position/background-selected.svg +3 -0
- package/dist/assets/icons/ic-fixed-position/bottom-left-background-deselected.svg +3 -0
- package/dist/assets/icons/ic-fixed-position/bottom-left-background-selected.svg +3 -0
- package/dist/assets/icons/ic-fixed-position/bottom-left-icon.svg +4 -0
- package/dist/assets/icons/ic-fixed-position/bottom-right-background-deselected.svg +3 -0
- package/dist/assets/icons/ic-fixed-position/bottom-right-background-selected.svg +3 -0
- package/dist/assets/icons/ic-fixed-position/bottom-right-icon.svg +4 -0
- package/dist/assets/icons/ic-fixed-position/center-bottom-icon.svg +5 -0
- package/dist/assets/icons/ic-fixed-position/center-center-icon.svg +3 -0
- package/dist/assets/icons/ic-fixed-position/center-left-icon.svg +5 -0
- package/dist/assets/icons/ic-fixed-position/center-right-icon.svg +5 -0
- package/dist/assets/icons/ic-fixed-position/center-top-icon.svg +5 -0
- package/dist/assets/icons/ic-fixed-position/top-left-background-deselected.svg +3 -0
- package/dist/assets/icons/ic-fixed-position/top-left-background-selected.svg +3 -0
- package/dist/assets/icons/ic-fixed-position/top-left-icon.svg +4 -0
- package/dist/assets/icons/ic-fixed-position/top-right-background-deselected.svg +3 -0
- package/dist/assets/icons/ic-fixed-position/top-right-background-selected.svg +3 -0
- package/dist/assets/icons/ic-fixed-position/top-right-icon.svg +4 -0
- package/dist/assets/icons/trash.svg +1 -0
- package/dist/components/MockWidget.svelte +108 -0
- package/dist/components/MockWidget.svelte.d.ts +22 -0
- package/dist/components/WidgetPreview.svelte +852 -0
- package/dist/components/WidgetPreview.svelte.d.ts +100 -0
- package/dist/components/color-select/ColorSelect.svelte +258 -0
- package/dist/components/color-select/ColorSelect.svelte.d.ts +42 -0
- package/dist/components/color-select/color.d.ts +25 -0
- package/dist/components/color-select/color.js +21 -0
- package/dist/components/color-select/constants.d.ts +5 -0
- package/dist/components/color-select/constants.js +5 -0
- package/dist/components/color-select/render.d.ts +5 -0
- package/dist/components/color-select/render.js +77 -0
- package/dist/components/color-select/shaders.d.ts +3 -0
- package/dist/components/color-select/shaders.js +8 -0
- package/dist/components/editors/EmbedEditor.svelte +700 -0
- package/dist/components/editors/EmbedEditor.svelte.d.ts +50 -0
- package/dist/components/editors/PropsEditor.svelte +4009 -0
- package/dist/components/editors/PropsEditor.svelte.d.ts +62 -0
- package/dist/components/editors/props-editor.css +73 -0
- package/dist/components/ic/AngleInputController.svelte +276 -0
- package/dist/components/ic/AngleInputController.svelte.d.ts +26 -0
- package/dist/components/ic/BrowserInputController.svelte +731 -0
- package/dist/components/ic/BrowserInputController.svelte.d.ts +39 -0
- package/dist/components/ic/CheckboxInputController.svelte +203 -0
- package/dist/components/ic/CheckboxInputController.svelte.d.ts +41 -0
- package/dist/components/ic/ColorPickerInputController.svelte +1584 -0
- package/dist/components/ic/ColorPickerInputController.svelte.d.ts +50 -0
- package/dist/components/ic/DateFieldInputController.svelte +453 -0
- package/dist/components/ic/DateFieldInputController.svelte.d.ts +35 -0
- package/dist/components/ic/DatePickerInputController.svelte +364 -0
- package/dist/components/ic/DatePickerInputController.svelte.d.ts +28 -0
- package/dist/components/ic/DropdownInputController.svelte +226 -0
- package/dist/components/ic/DropdownInputController.svelte.d.ts +43 -0
- package/dist/components/ic/FixedPositionInputController.svelte +353 -0
- package/dist/components/ic/FixedPositionInputController.svelte.d.ts +23 -0
- package/dist/components/ic/FontInputController.svelte +2098 -0
- package/dist/components/ic/FontInputController.svelte.d.ts +47 -0
- package/dist/components/ic/InputControllerHeader.svelte +147 -0
- package/dist/components/ic/InputControllerHeader.svelte.d.ts +23 -0
- package/dist/components/ic/NumberControls.svelte +299 -0
- package/dist/components/ic/NumberControls.svelte.d.ts +30 -0
- package/dist/components/ic/NumberInputController.svelte +397 -0
- package/dist/components/ic/NumberInputController.svelte.d.ts +30 -0
- package/dist/components/ic/PositionInputController.svelte +252 -0
- package/dist/components/ic/PositionInputController.svelte.d.ts +37 -0
- package/dist/components/ic/RadioGroupInputController.svelte +162 -0
- package/dist/components/ic/RadioGroupInputController.svelte.d.ts +38 -0
- package/dist/components/ic/RangeCalendarInputController.svelte +262 -0
- package/dist/components/ic/RangeCalendarInputController.svelte.d.ts +32 -0
- package/dist/components/ic/RoundedCornersInputController.svelte +469 -0
- package/dist/components/ic/RoundedCornersInputController.svelte.d.ts +37 -0
- package/dist/components/ic/SliderInputController.svelte +411 -0
- package/dist/components/ic/SliderInputController.svelte.d.ts +46 -0
- package/dist/components/ic/SliderRangeInputController.svelte +155 -0
- package/dist/components/ic/SliderRangeInputController.svelte.d.ts +33 -0
- package/dist/components/ic/SwitchInputController.svelte +107 -0
- package/dist/components/ic/SwitchInputController.svelte.d.ts +24 -0
- package/dist/components/ic/TagsInputController.svelte +125 -0
- package/dist/components/ic/TagsInputController.svelte.d.ts +27 -0
- package/dist/components/ic/TextAreaInputController.svelte +1932 -0
- package/dist/components/ic/TextAreaInputController.svelte.d.ts +14 -0
- package/dist/components/ic/TextAreaPreviewInputController.svelte +478 -0
- package/dist/components/ic/TextAreaPreviewInputController.svelte.d.ts +28 -0
- package/dist/components/ic/TextInputController.svelte +70 -0
- package/dist/components/ic/TextInputController.svelte.d.ts +30 -0
- package/dist/components/ic/TimeInputController.svelte +434 -0
- package/dist/components/ic/TimeInputController.svelte.d.ts +25 -0
- package/dist/components/ic/VectorInputController.svelte +1826 -0
- package/dist/components/ic/VectorInputController.svelte.d.ts +47 -0
- package/dist/components/ic/index.d.ts +39 -0
- package/dist/components/ic/index.js +24 -0
- package/dist/components/ic/shared/CategoryTabs.svelte +40 -0
- package/dist/components/ic/shared/CategoryTabs.svelte.d.ts +25 -0
- package/dist/components/ic/shared/ItemGrid.svelte +181 -0
- package/dist/components/ic/shared/ItemGrid.svelte.d.ts +35 -0
- package/dist/components/ic/shared/RecentItemsSection.svelte +260 -0
- package/dist/components/ic/shared/RecentItemsSection.svelte.d.ts +35 -0
- package/dist/components/ic/shared/index.d.ts +8 -0
- package/dist/components/ic/shared/index.js +9 -0
- package/dist/components/icons/addnew.svelte +21 -0
- package/dist/components/icons/addnew.svelte.d.ts +20 -0
- package/dist/components/icons/drag.svelte +29 -0
- package/dist/components/icons/drag.svelte.d.ts +20 -0
- package/dist/components/icons/drag.svg +1 -0
- package/dist/components/icons/locked.svelte +4 -0
- package/dist/components/icons/locked.svelte.d.ts +26 -0
- package/dist/components/icons/locked.svg +4 -0
- package/dist/components/icons/ok.svelte +20 -0
- package/dist/components/icons/ok.svelte.d.ts +20 -0
- package/dist/components/icons/ok.svg +11 -0
- package/dist/components/icons/unlocked.svelte +4 -0
- package/dist/components/icons/unlocked.svelte.d.ts +26 -0
- package/dist/components/icons/unlocked.svg +4 -0
- package/dist/components/icons/x.svelte +14 -0
- package/dist/components/icons/x.svelte.d.ts +20 -0
- package/dist/components/icons/x.svg +5 -0
- package/dist/components/ui/arrows/OpacitySliderArrows.svelte +43 -0
- package/dist/components/ui/arrows/OpacitySliderArrows.svelte.d.ts +23 -0
- package/dist/components/ui/checkerboard-background/CheckerboardBackground.svelte +15 -0
- package/dist/components/ui/checkerboard-background/CheckerboardBackground.svelte.d.ts +20 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +31 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +9 -0
- package/dist/stores/userSession.d.ts +7 -0
- package/dist/stores/userSession.js +28 -0
- package/dist/utils/FontManager.d.ts +278 -0
- package/dist/utils/FontManager.js +1302 -0
- package/dist/utils/MediaManager.d.ts +35 -0
- package/dist/utils/MediaManager.js +59 -0
- package/dist/utils/VectorManager.d.ts +190 -0
- package/dist/utils/VectorManager.js +754 -0
- package/dist/utils/colorAutocomplete.d.ts +10 -0
- package/dist/utils/colorAutocomplete.js +28 -0
- package/dist/utils/colorNames.d.ts +3 -0
- package/dist/utils/colorNames.js +152 -0
- package/dist/utils/colorTransparency.d.ts +52 -0
- package/dist/utils/colorTransparency.js +114 -0
- package/dist/utils/fontUtils.d.ts +34 -0
- package/dist/utils/fontUtils.js +132 -0
- package/dist/utils/passiveEvents.js +20 -0
- package/dist/utils/recentColors.d.ts +10 -0
- package/dist/utils/recentColors.js +63 -0
- package/dist/utils/skipFonts.d.ts +3 -0
- package/dist/utils/skipFonts.js +49 -0
- package/dist/utils/vectorUtils.d.ts +34 -0
- package/dist/utils/vectorUtils.js +71 -0
- package/dist/utils.d.ts +11 -0
- package/dist/utils.js +38 -0
- package/package.json +134 -74
- package/.babelrc +0 -10
- package/.bowerrc +0 -4
- package/.nvmrc +0 -1
- package/Gruntfile.coffee +0 -69
- package/bower.json +0 -28
- package/css/browser/entries/activateable.styl +0 -29
- package/css/browser/entries/audio.styl +0 -39
- package/css/browser/entries/folder.styl +0 -79
- package/css/browser/entries/image.styl +0 -26
- package/css/browser/entries/index.styl +0 -66
- package/css/browser/entries/rss.styl +0 -11
- package/css/browser/entries/video.styl +0 -11
- package/css/browser/index.styl +0 -86
- package/css/browser/intent/index.styl +0 -61
- package/css/browser/nav/index.styl +0 -73
- package/css/browser/social-browser.styl +0 -23
- package/css/controls/clock.styl +0 -110
- package/css/controls/config.styl +0 -6
- package/css/controls/control-audio-details.styl +0 -63
- package/css/controls/control-audio.styl +0 -28
- package/css/controls/control-browser.styl +0 -48
- package/css/controls/control-color.styl +0 -423
- package/css/controls/control-date_time.styl +0 -191
- package/css/controls/control-dropdown.styl +0 -79
- package/css/controls/control-font.styl +0 -147
- package/css/controls/control-image-details.styl +0 -35
- package/css/controls/control-image.styl +0 -43
- package/css/controls/control-order-picker.styl +0 -29
- package/css/controls/control-position.styl +0 -390
- package/css/controls/control-range.styl +0 -68
- package/css/controls/control-scale.styl +0 -30
- package/css/controls/control-slider.styl +0 -38
- package/css/controls/control-spinner.styl +0 -39
- package/css/controls/control-textarea.styl +0 -52
- package/css/controls/control-toggle.styl +0 -34
- package/css/controls/control-url.styl +0 -32
- package/css/controls/control-video-details.styl +0 -85
- package/css/controls/control-video.styl +0 -43
- package/css/controls/general.styl +0 -98
- package/css/controls/index.styl +0 -25
- package/css/editor/colors.styl +0 -7
- package/css/editor/content/directinput.styl +0 -47
- package/css/editor/content/index.styl +0 -111
- package/css/editor/content/message.styl +0 -53
- package/css/editor/content/tabs.styl +0 -75
- package/css/editor/copied-from-old-style.styl +0 -197
- package/css/editor/details/bulk-editor.styl +0 -104
- package/css/editor/details/index.styl +0 -21
- package/css/editor/details/item.styl +0 -153
- package/css/editor/embed.styl +0 -106
- package/css/editor/footer.styl +0 -180
- package/css/editor/full-editor.styl +0 -16
- package/css/editor/index.styl +0 -124
- package/css/editor/login-popup.styl +0 -13
- package/css/editor/preview.styl +0 -257
- package/css/editor/skin/editor/index.styl +0 -54
- package/css/editor/skin/editor/tabs.styl +0 -111
- package/css/editor/skin/index.styl +0 -138
- package/css/editor/skin/popup.styl +0 -152
- package/css/editor/skin/presets/dropdown.styl +0 -61
- package/css/editor/skin/presets/index.styl +0 -86
- package/css/editor/skin/presets/list.styl +0 -244
- package/css/editor/tooltips.styl +0 -85
- package/css/fonts.styl +0 -47
- package/css/icons/BlogvioEditor.dev.svg +0 -112
- package/css/icons.styl +0 -16
- package/css/img/arrow-right-rounded-v-small.png +0 -0
- package/css/img/browser-folder-separator.png +0 -0
- package/css/img/calendar-input-icons.png +0 -0
- package/css/img/check.png +0 -0
- package/css/img/close.png +0 -0
- package/css/img/composition/edit-icon.png +0 -0
- package/css/img/content/audio-icon.png +0 -0
- package/css/img/content/folder-icon.png +0 -0
- package/css/img/content/messages/error-background.png +0 -0
- package/css/img/content/messages/error-icon.png +0 -0
- package/css/img/content/messages/notification-background.png +0 -0
- package/css/img/content/messages/notification-icon.png +0 -0
- package/css/img/content/no-files-audio.png +0 -0
- package/css/img/content/no-files-image.png +0 -0
- package/css/img/content/no-files-video.png +0 -0
- package/css/img/content/no-files.png +0 -0
- package/css/img/content/rss-icon.png +0 -0
- package/css/img/content/selected.png +0 -0
- package/css/img/content/video-icon.png +0 -0
- package/css/img/content/with-control-text.png +0 -0
- package/css/img/control-dropdown-arrow.png +0 -0
- package/css/img/control-image-valid.png +0 -0
- package/css/img/control-position-icon.png +0 -0
- package/css/img/control-scale-lock-icon.png +0 -0
- package/css/img/control-slider-arrows-hover.png +0 -0
- package/css/img/control-slider-arrows.png +0 -0
- package/css/img/control-spinner-buttons-hover.png +0 -0
- package/css/img/control-spinner-buttons.png +0 -0
- package/css/img/control-textarea-bold-active.png +0 -0
- package/css/img/control-textarea-bold.png +0 -0
- package/css/img/control-textarea-italic-active.png +0 -0
- package/css/img/control-textarea-italic.png +0 -0
- package/css/img/control-textarea-underline-active.png +0 -0
- package/css/img/control-textarea-underline.png +0 -0
- package/css/img/custom-checkbox-sprite.png +0 -0
- package/css/img/details/item-collapse-icon.png +0 -0
- package/css/img/details/item-expand-icon.png +0 -0
- package/css/img/details/item-sort-icon.png +0 -0
- package/css/img/done-arrow.png +0 -0
- package/css/img/fb-logo-icon-w.png +0 -0
- package/css/img/grid-icons.png +0 -0
- package/css/img/icon-reference.png +0 -0
- package/css/img/indicator.png +0 -0
- package/css/img/loader.gif +0 -0
- package/css/img/loaders/audio-white.png +0 -0
- package/css/img/loaders/audio.png +0 -0
- package/css/img/loaders/photo-white.png +0 -0
- package/css/img/loaders/photo.png +0 -0
- package/css/img/loaders/util-white.png +0 -0
- package/css/img/loaders/util.png +0 -0
- package/css/img/loaders/video-white.png +0 -0
- package/css/img/loaders/video.png +0 -0
- package/css/img/maximize.png +0 -0
- package/css/img/minimize.png +0 -0
- package/css/img/next-btn.png +0 -0
- package/css/img/search-icon-sprite.png +0 -0
- package/css/img/services/blogvio.png +0 -0
- package/css/img/services/drive.png +0 -0
- package/css/img/services/dropbox-login.png +0 -0
- package/css/img/services/dropbox.png +0 -0
- package/css/img/services/facebook-login.png +0 -0
- package/css/img/services/facebook.png +0 -0
- package/css/img/services/flickr-login.png +0 -0
- package/css/img/services/flickr.png +0 -0
- package/css/img/services/instagram-login.png +0 -0
- package/css/img/services/instagram.png +0 -0
- package/css/img/services/text.png +0 -0
- package/css/img/services/uploads.png +0 -0
- package/css/img/services/uploads.svg +0 -689
- package/css/img/services/webaddress-bw.png +0 -0
- package/css/img/services/webaddress.png +0 -0
- package/css/img/services/webadress.png +0 -0
- package/css/img/services/websearch.png +0 -0
- package/css/img/services/wix.png +0 -0
- package/css/img/skin/item-active-indicator.png +0 -0
- package/css/img/skin/item-hover-icon.png +0 -0
- package/css/img/skin/overwrite.png +0 -0
- package/css/img/skin/popup-name-icon.png +0 -0
- package/css/img/skin/popup-save.png +0 -0
- package/css/img/skin/preset.png +0 -0
- package/css/img/skin/presets-arrow-purple.png +0 -0
- package/css/img/tw-icon-w.png +0 -0
- package/css/img/warning.png +0 -0
- package/css/img/white-rounded-check.png +0 -0
- package/css/index.styl +0 -9
- package/css/mixins.styl +0 -62
- package/css/scrollable.styl +0 -101
- package/karma.conf.js +0 -73
- package/lib/control.js +0 -1
- package/lib/controls.js +0 -1
- package/lib/core.editor.js +0 -4
- package/lib/core.js +0 -1
- package/lib/core.vendor.js +0 -3
- package/lib/dev/control.js +0 -845
- package/lib/dev/controls.js +0 -7774
- package/lib/dev/core.editor.js +0 -6439
- package/lib/dev/core.js +0 -1486
- package/lib/dev/core.vendor.js +0 -10708
- package/lib/dev/editor.css +0 -166
- package/lib/dev/editor.js +0 -29710
- package/lib/dev/editor.vendor.js +0 -15119
- package/lib/dev/jquery.nanoscroller.js.map +0 -10
- package/lib/editor.css +0 -167
- package/lib/editor.js +0 -49
- package/lib/editor.vendor.js +0 -11
- package/src/api/index.coffee +0 -23
- package/src/api/mixin.coffee +0 -89
- package/src/browser/controller/entry/audio.coffee +0 -38
- package/src/browser/controller/entry/button.coffee +0 -16
- package/src/browser/controller/entry/file.coffee +0 -35
- package/src/browser/controller/entry/folder.coffee +0 -14
- package/src/browser/controller/entry/image.coffee +0 -24
- package/src/browser/controller/entry/index.coffee +0 -18
- package/src/browser/controller/entry/rss.coffee +0 -5
- package/src/browser/controller/entry/video.coffee +0 -4
- package/src/browser/controller/index.coffee +0 -289
- package/src/browser/controller/instagram.coffee +0 -129
- package/src/browser/controller/navbar.coffee +0 -48
- package/src/browser/controller/social.coffee +0 -213
- package/src/browser/model/entry.coffee +0 -18
- package/src/browser/model/index.coffee +0 -34
- package/src/core/controller/font-loader.coffee +0 -65
- package/src/core/controller/lazyload.coffee +0 -14
- package/src/core/controller/overlay.coffee +0 -146
- package/src/core/controller/popup.coffee +0 -36
- package/src/core/controller/scrollable.coffee +0 -68
- package/src/core/controls/audio.coffee +0 -56
- package/src/core/controls/browser.coffee +0 -271
- package/src/core/controls/color.coffee +0 -121
- package/src/core/controls/date-time/calendar/index.coffee +0 -99
- package/src/core/controls/date-time/calendar/months.coffee +0 -52
- package/src/core/controls/date-time/calendar/years.coffee +0 -58
- package/src/core/controls/date-time/clock.coffee +0 -62
- package/src/core/controls/date-time/helper.coffee +0 -31
- package/src/core/controls/date-time/index.coffee +0 -62
- package/src/core/controls/date-time/popup.coffee +0 -56
- package/src/core/controls/details/audio.coffee +0 -33
- package/src/core/controls/details/image.coffee +0 -19
- package/src/core/controls/details/video.coffee +0 -14
- package/src/core/controls/dropdown.coffee +0 -100
- package/src/core/controls/font/font-item.coffee +0 -22
- package/src/core/controls/font/font-list.coffee +0 -108
- package/src/core/controls/font/fonts-popup.coffee +0 -91
- package/src/core/controls/font/index.coffee +0 -66
- package/src/core/controls/image.coffee +0 -37
- package/src/core/controls/index.coffee +0 -88
- package/src/core/controls/orderPicker.coffee +0 -93
- package/src/core/controls/position/grid-panel.coffee +0 -75
- package/src/core/controls/position/index.coffee +0 -98
- package/src/core/controls/position/panel.coffee +0 -68
- package/src/core/controls/position/pixel-panel.coffee +0 -54
- package/src/core/controls/position/popup.coffee +0 -63
- package/src/core/controls/position/reference-panel.coffee +0 -26
- package/src/core/controls/range.coffee +0 -191
- package/src/core/controls/rss.coffee +0 -16
- package/src/core/controls/scale.coffee +0 -78
- package/src/core/controls/slider.coffee +0 -122
- package/src/core/controls/stepper.coffee +0 -97
- package/src/core/controls/text.coffee +0 -54
- package/src/core/controls/textarea.coffee +0 -78
- package/src/core/controls/toggle.coffee +0 -29
- package/src/core/controls/url.coffee +0 -88
- package/src/core/controls/video.coffee +0 -9
- package/src/core/embed/api.js +0 -178
- package/src/core/embed/controller.js +0 -331
- package/src/core/models/composition.coffee +0 -178
- package/src/core/models/content.coffee +0 -79
- package/src/core/models/skin.coffee +0 -91
- package/src/core/models/user.coffee +0 -6
- package/src/core/models/widget.coffee +0 -88
- package/src/core/router/index.coffee +0 -44
- package/src/core/services/container/index.js +0 -18
- package/src/core/services/container/instance.js +0 -3
- package/src/core/services/content.js +0 -80
- package/src/core/services/instagram.coffee +0 -47
- package/src/core/services/mixin/events.js +0 -17
- package/src/core/services/service.js +0 -1
- package/src/core/services/sounds.js +0 -81
- package/src/core/services/uploads.js +0 -299
- package/src/core/utils/popup.coffee +0 -67
- package/src/core/utils/should-show.coffee +0 -12
- package/src/core/views/audio-details.pug +0 -9
- package/src/core/views/audio.pug +0 -7
- package/src/core/views/browser.pug +0 -4
- package/src/core/views/color.pug +0 -8
- package/src/core/views/date-time/calendar.pug +0 -3
- package/src/core/views/date-time/clock.pug +0 -12
- package/src/core/views/date-time/grid.pug +0 -6
- package/src/core/views/date-time/input.pug +0 -4
- package/src/core/views/date-time/table.pug +0 -6
- package/src/core/views/dropdown.pug +0 -9
- package/src/core/views/font/font-item.pug +0 -3
- package/src/core/views/font/fonts-list.pug +0 -7
- package/src/core/views/font/fonts-popup.pug +0 -5
- package/src/core/views/font/index.pug +0 -7
- package/src/core/views/image-details.pug +0 -6
- package/src/core/views/image.pug +0 -5
- package/src/core/views/input-layout.pug +0 -15
- package/src/core/views/order-items.pug +0 -7
- package/src/core/views/order-picker.pug +0 -7
- package/src/core/views/pixelpanel.pug +0 -69
- package/src/core/views/position.pug +0 -10
- package/src/core/views/range.pug +0 -9
- package/src/core/views/scale.pug +0 -7
- package/src/core/views/slider.pug +0 -6
- package/src/core/views/stepper.pug +0 -6
- package/src/core/views/text.pug +0 -4
- package/src/core/views/textarea.pug +0 -8
- package/src/core/views/toggle.pug +0 -5
- package/src/core/views/video-details.pug +0 -7
- package/src/core/views/video.pug +0 -4
- package/src/core/widget/index.coffee +0 -66
- package/src/editor/content/browser/dropzone.html +0 -268
- package/src/editor/content/browser/file.html +0 -124
- package/src/editor/content/browser/scroll-pane.html +0 -32
- package/src/editor/content/browser/service.html +0 -53
- package/src/editor/content/browser/upload.coffee +0 -44
- package/src/editor/content/browser/upload.html +0 -439
- package/src/editor/content/index.coffee +0 -294
- package/src/editor/content/input/field.coffee +0 -45
- package/src/editor/content/input/index.coffee +0 -115
- package/src/editor/content/service/base/photoitem.coffee +0 -49
- package/src/editor/content/service/cache.coffee +0 -20
- package/src/editor/content/service/default.coffee +0 -59
- package/src/editor/content/service/dropbox.coffee +0 -71
- package/src/editor/content/service/facebook.coffee +0 -143
- package/src/editor/content/service/flickr.coffee +0 -85
- package/src/editor/content/service/google-drive-sdk.coffee +0 -18
- package/src/editor/content/service/google-drive.coffee +0 -144
- package/src/editor/content/service/instagram-social.coffee +0 -84
- package/src/editor/content/service/instagram.coffee +0 -168
- package/src/editor/content/service/share-popup.coffee +0 -15
- package/src/editor/content/service/soundcloud.coffee +0 -163
- package/src/editor/content/tab.coffee +0 -27
- package/src/editor/details/bulk-editor/attributes-panel.coffee +0 -11
- package/src/editor/details/bulk-editor/index.coffee +0 -86
- package/src/editor/details/index.coffee +0 -156
- package/src/editor/details/item/attributes.coffee +0 -45
- package/src/editor/details/item/index.coffee +0 -82
- package/src/editor/editor.coffee +0 -274
- package/src/editor/footer/breadcrumb.coffee +0 -17
- package/src/editor/footer/index.coffee +0 -36
- package/src/editor/index.coffee +0 -79
- package/src/editor/listeners/embed-listener.coffee +0 -143
- package/src/editor/login-popup.coffee +0 -29
- package/src/editor/notification-controller.coffee +0 -35
- package/src/editor/popup.coffee +0 -68
- package/src/editor/remote-preview.coffee +0 -68
- package/src/editor/save/index.coffee +0 -62
- package/src/editor/skin/delete.coffee +0 -33
- package/src/editor/skin/editor/index.coffee +0 -188
- package/src/editor/skin/editor/panel.coffee +0 -37
- package/src/editor/skin/index.coffee +0 -126
- package/src/editor/skin/save.coffee +0 -58
- package/src/editor/skin/selector/index.coffee +0 -59
- package/src/editor/skin/selector/item.coffee +0 -123
- package/src/editor/skin/selector/list.coffee +0 -97
- package/src/editor/views/embed.pug +0 -10
- package/src/editor/views/loginbutton.pug +0 -8
- package/src/editor/views/popup.pug +0 -17
- package/src/editor/views/presets.pug +0 -8
- package/src/editor/views/presetslist-header.pug +0 -8
- package/src/editor/views/skinitem.pug +0 -13
- package/src/modules/control.js +0 -3
- package/src/modules/controls.js +0 -29
- package/src/modules/core.js +0 -19
- package/src/modules/editor.js +0 -80
- package/test/config.js +0 -3
- package/test/core/models/skin-spec.js +0 -209
- package/webpack.config.js +0 -46
- package/webpack.config.prod.js +0 -9
- /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
|
+
|