From 2f139f94b4b684e45ff28eed7a3c5fc2cf875028 Mon Sep 17 00:00:00 2001 From: Matthias Mair Date: Thu, 15 Oct 2026 10:46:06 +0200 Subject: [PATCH] feat: Add First Time User Guide and tipp backend data structure (#12978) * add backend stuff * update str method * add to admin * [FR] Add FTUE Fixes #2907 * bump deps * bump api * add missing ruleset entry * fix init * add jumpoff to nav * try disableing specific test * fix lookup * fix mounting * disable more * make tour conditional * only show ftue once * remove temp disable * add actual content * enable using the menu as well * reorder steps * finish propagation * only allow delete * lower testing runtime for now * fix nitpicks * revert frontend debug changes * fix migration * patch out ftu behaviour on the backend * rename to first time use guide * change tipp (DE) to tip (EN) * deduplicate notification --- .../InvenTree/InvenTree/api_version.py | 5 +- src/backend/InvenTree/users/admin.py | 8 +- src/backend/InvenTree/users/api.py | 17 +- .../users/migrations/0018_tippresult.py | 33 ++ src/backend/InvenTree/users/models.py | 60 +++ src/backend/InvenTree/users/ruleset.py | 1 + src/backend/InvenTree/users/serializers.py | 26 +- src/backend/InvenTree/web/tips.py | 13 + src/frontend/lib/enums/ApiEndpoints.tsx | 1 + src/frontend/lib/types/User.tsx | 11 + src/frontend/package.json | 43 +- .../src/components/buttons/ScanButton.tsx | 5 +- .../components/buttons/SpotlightButton.tsx | 1 + .../src/components/nav/FirstUseTour.tsx | 229 +++++++++ src/frontend/src/components/nav/Header.tsx | 9 +- src/frontend/src/components/nav/Layout.tsx | 2 + src/frontend/src/components/nav/MainMenu.tsx | 31 +- .../src/components/nav/NavHoverMenu.tsx | 6 +- src/frontend/src/defaults/links.tsx | 9 + src/frontend/src/functions/icons.tsx | 4 +- src/frontend/src/states/ServerApiState.tsx | 7 + src/frontend/src/states/UserState.tsx | 20 +- src/frontend/tests/api.ts | 26 ++ src/frontend/tests/login.ts | 7 +- src/frontend/yarn.lock | 440 +++++++++--------- 25 files changed, 764 insertions(+), 250 deletions(-) create mode 100644 src/backend/InvenTree/users/migrations/0018_tippresult.py create mode 100644 src/backend/InvenTree/web/tips.py create mode 100644 src/frontend/src/components/nav/FirstUseTour.tsx diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index fc92f412d6..2177d7b844 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,11 +1,14 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 556 +INVENTREE_API_VERSION = 557 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v557 -> 2026-10-15 : https://github.com/inventree/InvenTree/pull/12978 + - Adds tips endpoints + v556 -> 2026-10-14 : https://github.com/inventree/InvenTree/pull/13017 - Adds 'is_internal' field (and filter) to the Company API endpoint - Adds 'INVENTREE_DEFAULT_INTERNAL_COMPANY' global setting diff --git a/src/backend/InvenTree/users/admin.py b/src/backend/InvenTree/users/admin.py index d730572195..293c6afa19 100644 --- a/src/backend/InvenTree/users/admin.py +++ b/src/backend/InvenTree/users/admin.py @@ -11,7 +11,8 @@ from django.db.models import Q from django.utils.text import smart_split from django.utils.translation import gettext_lazy as _ -from users.models import ApiToken, Owner, RuleSet +from common.admin_shared import NoEditAdminMixin +from users.models import ApiToken, Owner, RuleSet, TipResult from users.ruleset import RULESET_CHOICES User = get_user_model() @@ -207,5 +208,10 @@ class OwnerAdmin(admin.ModelAdmin): return queryset, True +@admin.register(TipResult) +class DataOutputAdmin(NoEditAdminMixin, admin.ModelAdmin): + """Custom admin interface for the TipResult model.""" + + admin.site.unregister(User) admin.site.register(User, InvenTreeUserAdmin) diff --git a/src/backend/InvenTree/users/api.py b/src/backend/InvenTree/users/api.py index df9dca3186..8410539a14 100644 --- a/src/backend/InvenTree/users/api.py +++ b/src/backend/InvenTree/users/api.py @@ -36,7 +36,7 @@ from InvenTree.mixins import ( ) from InvenTree.schema import exclude_from_schema from InvenTree.settings import FRONTEND_URL_BASE -from users.models import ApiToken, Owner, RuleSet, UserProfile +from users.models import ApiToken, Owner, RuleSet, TipResult, UserProfile from users.serializers import ( ApiTokenSerializer, ExtendedUserSerializer, @@ -46,6 +46,7 @@ from users.serializers import ( OwnerSerializer, RoleSerializer, RuleSetSerializer, + TipResultSerializer, UserCreateSerializer, UserProfileSerializer, UserSetPasswordSerializer, @@ -572,6 +573,19 @@ class UserProfileDetail(RetrieveUpdateAPI): return self.request.user.profile +class TipResultDetail(RetrieveUpdateAPI): + """Detail endpoint for tip result manipulation.""" + + queryset = TipResult.objects.all() + serializer_class = TipResultSerializer + permission_classes = [InvenTree.permissions.IsAuthenticatedOrReadScope] + + def get_queryset(self): + """Limit retrieval and updates to results owned by the current user.""" + TipResult.backfill(self.request.user) + return super().get_queryset().filter(user=self.request.user) + + user_urls = [ # Legacy endpoints (to avoid breaking existing API clients) # TODO @matmair - remove these legacy endpoints in the next breaking release @@ -601,6 +615,7 @@ user_urls = [ path( 'token/', ensure_csrf_cookie(GetAuthToken.as_view()), name='api-token' ), + path('tips//', TipResultDetail.as_view(), name='api-user-tip'), path('', MeUserDetail.as_view(), name='api-user-me'), ]), ), diff --git a/src/backend/InvenTree/users/migrations/0018_tippresult.py b/src/backend/InvenTree/users/migrations/0018_tippresult.py new file mode 100644 index 0000000000..81c045adaf --- /dev/null +++ b/src/backend/InvenTree/users/migrations/0018_tippresult.py @@ -0,0 +1,33 @@ +# Generated by Django 5.2.17 on 2026-10-08 22:56 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0017_apitoken_hmac_digest_apitoken_issued_by_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='TipResult', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('metadata', models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata')), + ('tip_id', models.CharField(max_length=255, verbose_name='Tip ID/Name')), + ('shown', models.BooleanField(default=False, help_text='Was this tip shown?', verbose_name='Shown')), + ('finished', models.BooleanField(default=False, help_text='Was this tip finished?', verbose_name='Finished')), + ('updated', models.DateTimeField(auto_now=True, help_text='The last time this tip result was updated', verbose_name='Updated')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tip_results', to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + options={ + 'verbose_name': 'Tip Result', + 'verbose_name_plural': 'Tip Results', + 'unique_together': {('user', 'tip_id')}, + }, + ), + ] diff --git a/src/backend/InvenTree/users/models.py b/src/backend/InvenTree/users/models.py index cc283874fe..7175755e8b 100644 --- a/src/backend/InvenTree/users/models.py +++ b/src/backend/InvenTree/users/models.py @@ -29,6 +29,7 @@ import InvenTree.helpers import InvenTree.models from common.settings import get_global_setting from InvenTree.ready import isImportingData, isReadOnlyCommand +from web.tips import KNOWN_IDS from .ruleset import RULESET_CHOICES, get_ruleset_models @@ -804,6 +805,65 @@ def create_or_update_user_profile(sender, instance, created, **kwargs): instance.profile.save() +class TipResult(InvenTree.models.MetadataMixin): + """Model representing a tip result.""" + + class Meta: + """Meta options.""" + + verbose_name = _('Tip Result') + verbose_name_plural = _('Tip Results') + unique_together = ('user', 'tip_id') + + user = models.ForeignKey( + User, + on_delete=models.CASCADE, + related_name='tip_results', + verbose_name=_('User'), + ) + tip_id = models.CharField(max_length=255, verbose_name=_('Tip ID/Name')) + shown = models.BooleanField( + default=False, verbose_name=_('Shown'), help_text=_('Was this tip shown?') + ) + finished = models.BooleanField( + default=False, verbose_name=_('Finished'), help_text=_('Was this tip finished?') + ) + + updated = models.DateTimeField( + auto_now=True, + verbose_name=_('Updated'), + help_text=_('The last time this tip result was updated'), + ) + + def __str__(self): + """Return string representation of the tip result.""" + if self.finished: + return f'{self.user}: {self.tip_id} (Finished)' + if self.shown: + return f'{self.user}: {self.tip_id} (Shown)' + return f'{self.user}: {self.tip_id}' + + @classmethod + def backfill(cls, user): + """Backfill missing state for all known IDs without an existing record.""" + known_ids = KNOWN_IDS + if not known_ids: + return + + existing_ids = set( + cls.objects.filter(user=user, tip_id__in=known_ids).values_list( + 'tip_id', flat=True + ) + ) + + # bulk create + missing_ids = set(known_ids) - existing_ids + if missing_ids: + cls.objects.bulk_create([ + cls(user=user, tip_id=tip_id) for tip_id in missing_ids + ]) + + # Validate groups @receiver(post_save, sender=Group) def validate_primary_group_on_save(sender, instance, **kwargs): diff --git a/src/backend/InvenTree/users/ruleset.py b/src/backend/InvenTree/users/ruleset.py index ff9968963b..0ae2f66bfd 100644 --- a/src/backend/InvenTree/users/ruleset.py +++ b/src/backend/InvenTree/users/ruleset.py @@ -216,6 +216,7 @@ def get_ruleset_ignore() -> list[str]: 'common_selectionlist', 'users_owner', 'users_userprofile', # User profile is handled in the serializer - only own user can change + 'users_tipresult', # perms are handled in the serializer # Third-party tables 'error_report_error', 'exchange_rate', diff --git a/src/backend/InvenTree/users/serializers.py b/src/backend/InvenTree/users/serializers.py index a297b7b8c4..528b1beeee 100644 --- a/src/backend/InvenTree/users/serializers.py +++ b/src/backend/InvenTree/users/serializers.py @@ -16,7 +16,7 @@ from InvenTree.serializers import ( OptionalField, ) -from .models import ApiToken, Owner, RuleSet, UserProfile +from .models import ApiToken, Owner, RuleSet, TipResult, UserProfile from .permissions import check_user_role, prefetch_rule_sets from .ruleset import RULESET_CHOICES, RULESET_PERMISSIONS, RuleSetEnum @@ -183,6 +183,17 @@ class UserProfileSerializer(BriefUserProfileSerializer): ] +class TipResultSerializer(InvenTreeModelSerializer): + """Serializer for the TipResult model.""" + + class Meta: + """Meta options for TipResultSerializer.""" + + model = TipResult + fields = ['pk', 'tip_id', 'shown', 'finished', 'updated'] + read_only_fields = ['pk', 'tip_id', 'updated'] + + class UserSerializer(InvenTreeModelSerializer): """Serializer for a User.""" @@ -336,9 +347,10 @@ class ExtendedUserSerializer(UserSerializer): 'is_superuser', 'is_active', 'profile', + 'tips', ] - read_only_fields = [*UserSerializer.Meta.read_only_fields, 'groups'] + read_only_fields = [*UserSerializer.Meta.read_only_fields, 'groups', 'tips'] # 'group_ids' is a write-only serializer field which does not exist on the model SKIP_CREATE_FIELDS = ['group_ids'] @@ -366,6 +378,15 @@ class ExtendedUserSerializer(UserSerializer): profile = BriefUserProfileSerializer(many=False, read_only=True) + tips = serializers.SerializerMethodField() + + def get_tips(self, user: User) -> list[dict]: + """Return the user's known tip results, creating missing records first.""" + TipResult.backfill(user) + return TipResultSerializer( + user.tip_results.all(), many=True, context=self.context + ).data + def validate_is_superuser(self, value): """Only a superuser account can adjust this value!""" request_user = self.context['request'].user @@ -430,6 +451,7 @@ class MeUserSerializer(FilterableSerializerMixin, ExtendedUserSerializer): *(f for f in ExtendedUserSerializer.Meta.fields if f != 'group_ids'), 'roles', 'permissions', + 'tips', ] read_only_fields = [ diff --git a/src/backend/InvenTree/web/tips.py b/src/backend/InvenTree/web/tips.py new file mode 100644 index 0000000000..a6f6fb60a1 --- /dev/null +++ b/src/backend/InvenTree/web/tips.py @@ -0,0 +1,13 @@ +"""Central registration for frontend tips. + +This is a pretty simple system for now, could be enhanced in the future with plugin registrations, more complex server side logic, and dynamic tips. +""" + +COMMON_PREFIX = 'org.inventree.i.tip' + +available_tips: dict = { + COMMON_PREFIX + '.ftug': {} # First-time user guide + # COMMON_PREFIX + '.example': {}, # Example tip +} + +KNOWN_IDS: list = list(available_tips.keys()) diff --git a/src/frontend/lib/enums/ApiEndpoints.tsx b/src/frontend/lib/enums/ApiEndpoints.tsx index 1e7d2da8e4..2804f63fb7 100644 --- a/src/frontend/lib/enums/ApiEndpoints.tsx +++ b/src/frontend/lib/enums/ApiEndpoints.tsx @@ -17,6 +17,7 @@ export enum ApiEndpoints { // Individual user endpoints user_me_profile = 'user/me/profile/', user_me_roles = 'user/me/roles/', + user_me_tips = 'user/me/tips/', user_me_token = 'user/me/token/', user_me = 'user/me/', diff --git a/src/frontend/lib/types/User.tsx b/src/frontend/lib/types/User.tsx index 3a63fbabf1..6cc8164ad3 100644 --- a/src/frontend/lib/types/User.tsx +++ b/src/frontend/lib/types/User.tsx @@ -16,6 +16,14 @@ export interface UserProfile { primary_group: number | null; } +export interface UserTips { + pk: number; + tip_id: string; + shown: boolean; + finished: boolean; + updated: string; +} + // Type interface fully defining the current user export interface UserProps { pk: number; @@ -29,11 +37,14 @@ export interface UserProps { permissions?: Record; groups: any[] | null; profile: UserProfile; + tips: UserTips[] | null; } export interface UserStateProps { user: UserProps | undefined; is_authed: boolean; + done_tips: string[] | null; + setTips: (tips: UserTips[] | null) => void; userId: () => number | undefined; username: () => string; setAuthenticated: (authed?: boolean) => void; diff --git a/src/frontend/package.json b/src/frontend/package.json index 18cced8c3b..5bd5b3572c 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -49,8 +49,8 @@ "@codemirror/state": "^6.6.0", "@codemirror/theme-one-dark": "^6.1.3", "@codemirror/view": "^6.40.0", - "@floating-ui/dom": "^1.0.0", "@emotion/react": "^11.14.0", + "@floating-ui/dom": "^1.0.0", "@fortawesome/fontawesome-svg-core": "^7.2.0", "@fortawesome/free-regular-svg-icons": "^7.2.0", "@fortawesome/free-solid-svg-icons": "^7.2.0", @@ -61,30 +61,30 @@ "@fullcalendar/react": "6.1.21", "@lingui/core": "^5.9.2", "@lingui/react": "^5.9.2", - "@mantine/carousel": "^9.2.1", - "@mantine/charts": "^9.2.1", - "@mantine/core": "^9.2.1", - "@mantine/dates": "^9.2.1", - "@mantine/dropzone": "^9.2.1", - "@mantine/form": "^9.2.1", - "@mantine/hooks": "^9.2.1", - "@mantine/modals": "^9.2.1", - "@mantine/notifications": "^9.2.1", - "@mantine/spotlight": "^9.2.1", - "@mantine/tiptap": "^9.2.1", + "@mantine/carousel": "^9.7.1", + "@mantine/charts": "^9.7.1", + "@mantine/core": "^9.7.1", + "@mantine/dates": "^9.7.1", + "@mantine/dropzone": "^9.7.1", + "@mantine/form": "^9.7.1", + "@mantine/hooks": "^9.7.1", + "@mantine/modals": "^9.7.1", + "@mantine/notifications": "^9.7.1", + "@mantine/spotlight": "^9.7.1", + "@mantine/tiptap": "^9.7.1", "@mantine/utils": "^6.0.22", - "@mantine/vanilla-extract": "^9.2.1", + "@mantine/vanilla-extract": "^9.7.1", "@messageformat/date-skeleton": "^1.1.0", "@sentry/react": "^10.57.0", "@tabler/icons-react": "^3.44.0", "@tanstack/react-query": "^5.101.0", - "@tiptap/core": "^3.23.6", - "@tiptap/extension-image": "^3.23.6", - "@tiptap/extension-link": "^3.23.6", - "@tiptap/extension-table": "^3.23.6", - "@tiptap/pm": "^3.23.6", - "@tiptap/react": "^3.23.6", - "@tiptap/starter-kit": "^3.23.6", + "@tiptap/core": "^3.31.4", + "@tiptap/extension-image": "^3.31.4", + "@tiptap/extension-link": "^3.31.4", + "@tiptap/extension-table": "^3.31.4", + "@tiptap/pm": "^3.31.4", + "@tiptap/react": "^3.31.4", + "@tiptap/starter-kit": "^3.31.4", "@uiw/codemirror-theme-vscode": "^4.25.8", "@uiw/react-codemirror": "^4.25.8", "@uiw/react-split": "^5.9.4", @@ -149,8 +149,7 @@ "vite-plugin-externals": "^0.6.2", "vite-plugin-istanbul": "^9.0.1" }, - "overrides": { - }, + "overrides": {}, "resolutions": { "glob": "^13.0.0", "undici": "^6.24.0", diff --git a/src/frontend/src/components/buttons/ScanButton.tsx b/src/frontend/src/components/buttons/ScanButton.tsx index 220345e8b3..ddbaa664f5 100644 --- a/src/frontend/src/components/buttons/ScanButton.tsx +++ b/src/frontend/src/components/buttons/ScanButton.tsx @@ -24,12 +24,14 @@ export function ScanButton({ modelType, callback, onScanSuccess, - hotkey = false + hotkey = false, + id }: { modelType?: ModelType; callback?: BarcodeScanCallback; onScanSuccess?: BarcodeScanSuccessCallback; hotkey?: boolean; + id?: string; }) { const [opened, { open, close }] = useDisclosure(false); const [everOpened, setEverOpened] = useState(false); @@ -58,6 +60,7 @@ export function ScanButton({ aria-label={`barcode-scan-button-${modelType ?? 'any'}`} onClick={handleOpen} variant='transparent' + id={id} > diff --git a/src/frontend/src/components/buttons/SpotlightButton.tsx b/src/frontend/src/components/buttons/SpotlightButton.tsx index 3331e5a3df..473f1af188 100644 --- a/src/frontend/src/components/buttons/SpotlightButton.tsx +++ b/src/frontend/src/components/buttons/SpotlightButton.tsx @@ -20,6 +20,7 @@ export function SpotlightButton({ hotkey = false }: { hotkey?: boolean }) { onClick={() => firstSpotlight.open()} variant='transparent' aria-label='open-spotlight' + id='tip-ftu-cmd' > diff --git a/src/frontend/src/components/nav/FirstUseTour.tsx b/src/frontend/src/components/nav/FirstUseTour.tsx new file mode 100644 index 0000000000..3b1a6b97ee --- /dev/null +++ b/src/frontend/src/components/nav/FirstUseTour.tsx @@ -0,0 +1,229 @@ +import { ApiEndpoints, apiUrl } from '@lib/index'; +import { t } from '@lingui/core/macro'; +import { Trans } from '@lingui/react/macro'; +import { Button, Group, Text, Tour, rem } from '@mantine/core'; +import { useWindowEvent } from '@mantine/hooks'; +import { type NotificationData, notifications } from '@mantine/notifications'; + +import { + type Dispatch, + type SetStateAction, + useEffect, + useMemo, + useRef, + useState +} from 'react'; +import { api } from '../../App'; +import { useServerApiState } from '../../states/ServerApiState'; +import { + useGlobalSettingsState, + useUserSettingsState +} from '../../states/SettingsStates'; +import { useUserState } from '../../states/UserState'; + +interface FtuStep { + target: string; + title: string; + description: string; + action_before?: () => void; +} + +const ftuKex = 'org.inventree.i.tip.ftug'; + +export function FirstUseTour() { + const { user, done_tips, setTips } = useUserState(); + const hasCompletedFtu = done_tips?.includes(ftuKex) ?? false; + const [active, setActive] = useState(false); + const nudgeShown = useRef(false); + const globalSettings = useGlobalSettingsState(); + const userSettings = useUserSettingsState(); + const { ftuShown, setFtuShown } = useServerApiState(); + const showNudge = !!user && !hasCompletedFtu; + + const ftusteps = useMemo(() => { + const _steps: FtuStep[] = [ + { + target: 'tip-ftu-nav1', + title: t`Navigation Area`, + description: t`The navigation area provides quick access to different sections of the application.` + }, + { + target: 'tip-ftu-nav2', + title: t`Navigation Drawer`, + description: t`Using the icon you can open and close the navigation drawer which contains most available pages accessible to you.` + }, + { + target: 'tip-ftu-nav3', + title: t`Main Menu`, + description: t`The main menu provides access to top level navigation targets. Plugins can extend the menu with additional options.` + }, + { + target: 'tip-ftu-search', + title: t`Search`, + description: t`The global search allows you to quickly find items across all objects. You can configure which objects categories are included in the search and how terms are matched.` + } + ]; + if (userSettings.isSet('SHOW_SPOTLIGHT')) { + _steps.push({ + target: 'tip-ftu-cmd', + title: t`Command Palette`, + description: t`Quickly access commands and actions anywhere using the command palette. Available commands are dependent on your current context. Use cmd + k to open it anywhere.` + }); + } + + if (globalSettings.isSet('BARCODE_ENABLE')) { + _steps.push({ + target: 'tip-ftu-barcode', + title: t`Barcode Scanner`, + description: t`Allows for quick scanning of barcodes with your device's camera or a handheld scanner.` + }); + } + + _steps.push( + { + target: 'tip-ftu-notif', + title: t`Notifications`, + description: t`Depending on your instance and user settings notifications are delivered through various channels. This notification bar is annotated with a bell when there are unread user interface notifications.` + }, + { + target: 'tip-ftu-settings', + title: t`Settings`, + description: t`The main menu allows you to access preferences and access system-wide settings. Options depend on your permissions.`, + action_before: () => { + window.dispatchEvent(new Event('inventree:open-main-menu')); + } + }, + { + target: 'tip-ftu-usersettings', + title: t`User Settings`, + description: t`The user settings menu allows you to configure your personal preferences and account settings. This also contains security and notification settings.` + } + ); + + if (user?.is_staff) { + _steps.push({ + target: 'tip-ftu-admincenter', + title: t`Admin Center`, + description: t`The admin center provides access to administrative functions and can be used to manage various functions required for operation of the instance like user management, templates, parameters, plugins.` + }); + } + + _steps.push({ + target: 'tip-ftu-about', + title: t`About InvenTree`, + description: t`Provides information about the InvenTree instance, including version details and relevant links. Important for bug reports.` + }); + return _steps; + }, [userSettings, globalSettings]); + + const completeTour = () => { + setActive(false); + window.dispatchEvent(new Event('inventree:close-main-menu')); + + if (!user) return; + const tip_pk = user.tips?.find((tip) => tip.tip_id === ftuKex)?.pk; + if (!tip_pk) return; + api + .patch(apiUrl(ApiEndpoints.user_me_tips, tip_pk), { finished: true }) + .then((result) => { + setTips( + user.tips?.map((tip) => (tip.pk === tip_pk ? result.data : tip)) ?? + null + ); + }); + }; + + useWindowEvent('inventree:start-ftu', () => setActive(true)); + + // show notif to nudge the user to complete the First Use Tour + useEffect(() => { + if (showNudge && !active) { + // session deduplication + if (nudgeShown.current || ftuShown) return; + nudgeShown.current = true; + setFtuShown(true); + + notifications.show({ + autoClose: false, + renderNotification: (notification) => + ftuNotification(setActive, completeTour, notification), + message: '' + }); + } + }, [showNudge]); + + // rendering + if (!user) { + return null; + } + return ( + + {ftusteps.map((step, index) => ( + + {step.description} + + ))} + + ); +} + +function ftuNotification( + setActive: Dispatch>, + completeTour: () => void, + notification: NotificationData +): import('react').ReactNode { + return ( +
+
+ + First Use Tour available + + + + It looks like you did not complete the First Use Tour yet. + + + + + + +
+
+ ); +} diff --git a/src/frontend/src/components/nav/Header.tsx b/src/frontend/src/components/nav/Header.tsx index 262103bcd4..3e4d34fd21 100644 --- a/src/frontend/src/components/nav/Header.tsx +++ b/src/frontend/src/components/nav/Header.tsx @@ -182,7 +182,7 @@ export function Header() { /> - + @@ -198,12 +198,15 @@ export function Header() { onClick={openSearchDrawer} variant='transparent' aria-label='open-search' + id='tip-ftu-search' > {userSettings.isSet('SHOW_SPOTLIGHT') && } - {globalSettings.isSet('BARCODE_ENABLE') && } + {globalSettings.isSet('BARCODE_ENABLE') && ( + + )} @@ -347,6 +351,7 @@ function NavTabs() { }} // Select either a static or dynamic tab to be highlighted. value={dynamicTabValue ?? tabValue} + id='tip-ftu-nav3' > {tabs.map((tab) => tab)} diff --git a/src/frontend/src/components/nav/Layout.tsx b/src/frontend/src/components/nav/Layout.tsx index 3c81b6542a..bb8d28ebe7 100644 --- a/src/frontend/src/components/nav/Layout.tsx +++ b/src/frontend/src/components/nav/Layout.tsx @@ -30,6 +30,7 @@ import { PluginUIFeatureType } from '../plugins/PluginUIFeature'; import GlobalPreviewDrawer from '../previews/GlobalPreviewDrawer'; +import { FirstUseTour } from './FirstUseTour'; import { Footer } from './Footer'; import { Header } from './Header'; @@ -123,6 +124,7 @@ export default function LayoutComponent() { return ( <> +
diff --git a/src/frontend/src/components/nav/MainMenu.tsx b/src/frontend/src/components/nav/MainMenu.tsx index eb25eadf40..ad4617046c 100644 --- a/src/frontend/src/components/nav/MainMenu.tsx +++ b/src/frontend/src/components/nav/MainMenu.tsx @@ -7,6 +7,7 @@ import { UnstyledButton, useMantineColorScheme } from '@mantine/core'; +import { useWindowEvent } from '@mantine/hooks'; import { IconChevronDown, IconInfoCircle, @@ -17,6 +18,7 @@ import { IconUserBolt, IconUserCog } from '@tabler/icons-react'; +import { useState } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import { useShallow } from 'zustand/react/shallow'; import { aboutInvenTree } from '../../defaults/links'; @@ -27,14 +29,38 @@ import { vars } from '../../theme'; export function MainMenu() { const navigate = useNavigate(); + const [opened, setOpened] = useState(false); + const [tourActive, setTourActive] = useState(false); const [user, username] = useUserState( useShallow((state) => [state.user, state.username]) ); const { colorScheme, toggleColorScheme } = useMantineColorScheme(); + useWindowEvent('inventree:open-main-menu', () => { + setTourActive(true); + setOpened(true); + }); + useWindowEvent('inventree:close-main-menu', () => { + setTourActive(false); + setOpened(false); + }); + return ( <> - + { + if (event || !tourActive) { + setOpened(event); + } + }} + zIndex={10001} + id='tip-ftu-settings' + > @@ -57,6 +83,7 @@ export function MainMenu() { leftSection={} component={Link} to='/settings/user' + id='tip-ftu-usersettings' > User Settings @@ -74,6 +101,7 @@ export function MainMenu() { leftSection={} component={Link} to='/settings/admin' + id='tip-ftu-admincenter' > Admin Center @@ -95,6 +123,7 @@ export function MainMenu() { aboutInvenTree()} leftSection={} + id='tip-ftu-about' > About InvenTree diff --git a/src/frontend/src/components/nav/NavHoverMenu.tsx b/src/frontend/src/components/nav/NavHoverMenu.tsx index 21e8555e03..465f6aa305 100644 --- a/src/frontend/src/components/nav/NavHoverMenu.tsx +++ b/src/frontend/src/components/nav/NavHoverMenu.tsx @@ -8,7 +8,11 @@ export function NavHoverMenu({ openDrawer: () => void; }>) { return ( - openDrawer()} aria-label='navigation-menu'> + openDrawer()} + aria-label='navigation-menu' + id='tip-ftu-nav2' + > ); diff --git a/src/frontend/src/defaults/links.tsx b/src/frontend/src/defaults/links.tsx index 4b3615637c..da7a9b614c 100644 --- a/src/frontend/src/defaults/links.tsx +++ b/src/frontend/src/defaults/links.tsx @@ -195,6 +195,15 @@ export function AboutLinks( description: t`Licenses for dependencies of the InvenTree software`, icon: 'license', action: licenseInfo + }, + { + id: 'ftug', + title: t`First Use Tour`, + description: t`Guided tour for first-time users`, + icon: 'book', + action: () => { + window.dispatchEvent(new Event('inventree:start-ftu')); + } } ]; diff --git a/src/frontend/src/functions/icons.tsx b/src/frontend/src/functions/icons.tsx index e34723e9a3..fa21eccd4c 100644 --- a/src/frontend/src/functions/icons.tsx +++ b/src/frontend/src/functions/icons.tsx @@ -5,6 +5,7 @@ import { IconArrowMerge, IconBell, IconBinaryTree2, + IconBook, IconBookmarks, IconBox, IconBrandTelegram, @@ -280,7 +281,8 @@ const icons: InvenTreeIconType = { chart_bar: IconChartBar, chart_line: IconChartLine, - news: IconNews + news: IconNews, + book: IconBook }; /** diff --git a/src/frontend/src/states/ServerApiState.tsx b/src/frontend/src/states/ServerApiState.tsx index fdb036e2ab..5610d9a128 100644 --- a/src/frontend/src/states/ServerApiState.tsx +++ b/src/frontend/src/states/ServerApiState.tsx @@ -22,6 +22,9 @@ interface ServerApiStateProps { registration_enabled: () => boolean; sso_registration_enabled: () => boolean; password_forgotten_enabled: () => boolean; + // ftu + ftuShown: boolean; + setFtuShown: (value: boolean) => void; } function get_server_setting(val: any) { @@ -105,6 +108,10 @@ export const useServerApiState = create()( return get_server_setting( get().server?.settings?.password_forgotten_enabled ); + }, + ftuShown: false, + setFtuShown: (value) => { + set({ ftuShown: value }); } }), { diff --git a/src/frontend/src/states/UserState.tsx b/src/frontend/src/states/UserState.tsx index 1a2a19e3d7..7e2564d2a7 100644 --- a/src/frontend/src/states/UserState.tsx +++ b/src/frontend/src/states/UserState.tsx @@ -8,7 +8,7 @@ import { roleToViewSettingMap } from '@lib/enums/Roles'; import { apiUrl } from '@lib/functions/Api'; -import type { UserProps, UserStateProps } from '@lib/types/User'; +import type { UserProps, UserStateProps, UserTips } from '@lib/types/User'; import { api, setApiDefaults } from '../App'; import { clearCsrfCookie } from '../functions/auth'; import { useServerApiState } from './ServerApiState'; @@ -20,6 +20,15 @@ import { useGlobalSettingsState } from './SettingsStates'; export const useUserState = create((set, get) => ({ user: undefined, is_authed: false, + done_tips: null, + setTips: (tips: UserTips[] | null) => { + const done_tips = + tips?.filter((tip) => tip.finished === true).map((tip) => tip.tip_id) ?? + null; + const user = get().user as UserProps; + user.tips = tips; + set({ done_tips: done_tips, user: user }); + }, setAuthenticated: (authed = true) => { set({ is_authed: authed }); setApiDefaults(); @@ -37,7 +46,13 @@ export const useUserState = create((set, get) => ({ return user?.username ?? ''; } }, - setUser: (newUser: UserProps | undefined) => set({ user: newUser }), + setUser: (newUser: UserProps | undefined) => { + const done_tips = + newUser?.tips + ?.filter((tip) => tip.finished === true) + .map((tip) => tip.tip_id) ?? null; + set({ user: newUser, done_tips: done_tips }); + }, getUser: () => get().user, clearUserState: () => { set({ user: undefined, is_authed: false }); @@ -107,6 +122,7 @@ export const useUserState = create((set, get) => ({ username: response.data.username, groups: response.data.groups, profile: response.data.profile, + tips: response.data.tips, roles: response.data?.roles ?? {}, permissions: response.data?.permissions ?? {}, is_staff: response.data?.is_staff ?? false, diff --git a/src/frontend/tests/api.ts b/src/frontend/tests/api.ts index 649b953f36..c114d63402 100644 --- a/src/frontend/tests/api.ts +++ b/src/frontend/tests/api.ts @@ -14,3 +14,29 @@ export const createApi = ({ Authorization: `Basic ${btoa(`${username || adminuser.username}:${password || adminuser.testcred}`)}` } }); + +export const dismissFtu = async ({ + username, + password +}: { + username: string; + password: string; +}) => { + const api = await createApi({ username, password }); + + // find tip and patch out ftu + try { + const response = await api.get('user/me/'); + if (!response.ok()) return; + const tip = ((await response.json())?.tips ?? []).find( + (t: any) => t.tip_id === 'org.inventree.i.tip.ftug' + ); + if (tip && !tip.finished) { + await api.patch(`user/me/tips/${tip.pk}/`, { + data: { finished: true } + }); + } + } finally { + await api.dispose(); + } +}; diff --git a/src/frontend/tests/login.ts b/src/frontend/tests/login.ts index 4bb52d44f9..9db3a95197 100644 --- a/src/frontend/tests/login.ts +++ b/src/frontend/tests/login.ts @@ -1,4 +1,7 @@ +import fs from 'node:fs'; +import path from 'node:path'; import type { Browser, Page } from '@playwright/test'; +import { dismissFtu } from './api'; import { type UserType, allaccessuser, @@ -6,9 +9,6 @@ import { logoutUrl, webUrl } from './defaults'; - -import fs from 'node:fs'; -import path from 'node:path'; import { navigate } from './helpers.js'; interface LoginOptions { @@ -28,6 +28,7 @@ export const doLogin = async (page: Page, options?: LoginOptions) => { options?.password ?? options?.user?.testcred ?? allaccessuser.testcred; console.log('- Logging in with username:', username); + await dismissFtu({ username, password }); await navigate(page, loginUrl, { baseUrl: options?.baseUrl, diff --git a/src/frontend/yarn.lock b/src/frontend/yarn.lock index 5c5e0ae1b2..ff793c46b6 100644 --- a/src/frontend/yarn.lock +++ b/src/frontend/yarn.lock @@ -1205,94 +1205,94 @@ "@babel/runtime" "^7.20.13" "@lingui/core" "5.9.5" -"@mantine/carousel@^9.2.1": - version "9.6.0" - resolved "https://registry.yarnpkg.com/@mantine/carousel/-/carousel-9.6.0.tgz#c3f6f40cf6b2822c57d67f07bc67da60e86b36cf" - integrity sha512-+bYTs7XfsRkv+vR+ojL+EKUxY8wBXeirOMIB3d/4YKmKleMiqJh2J3ejEVcINelzLVjvod7+fg+edKDy9N2aSA== +"@mantine/carousel@^9.7.1": + version "9.7.1" + resolved "https://registry.yarnpkg.com/@mantine/carousel/-/carousel-9.7.1.tgz#884d75a3a6cbd95c0dda1569e21a3b97932a2568" + integrity sha512-lEa4BjqsrCUp4xJ4pJyaOCvcXGyqRfSYUumUvY/Q1KHrkqTdReiZCdURcsX+OHjqrju1KruddWH/0qNzAYJ9Ew== -"@mantine/charts@^9.2.1": - version "9.6.0" - resolved "https://registry.yarnpkg.com/@mantine/charts/-/charts-9.6.0.tgz#b38cc1dabf866a8353e3b6d7a87925a08a889650" - integrity sha512-xGRzoY0bJSR3r1DrIhpW8k81xtvh2EfYnRiZi2QtgdtkzSXFx9gMDJfr7Jo0i0joj5XuL0O4tPhCg0ZxjGFNrw== +"@mantine/charts@^9.7.1": + version "9.7.1" + resolved "https://registry.yarnpkg.com/@mantine/charts/-/charts-9.7.1.tgz#aecea556f527b6d697d151b15f4a987e738d55c1" + integrity sha512-bSuKFm8L7zr9y2n96JF/kcwU1/F2NvZk9+7QaWxQH1y1IpdpQWGniGBZfB9yBzjiKIoDQIAqhvWLrLESBV0azQ== -"@mantine/core@^9.2.1": - version "9.6.0" - resolved "https://registry.yarnpkg.com/@mantine/core/-/core-9.6.0.tgz#9d4489de141be13c42932247c63e57615695a4bf" - integrity sha512-WpdtSv9q2k4RrAVUxBlABMyEul4vv94EkjoVuQglBBC7jVmLhBBgI64HltE6di/l6zvj9D7k1vgkbVjyxQsn1g== +"@mantine/core@^9.7.1": + version "9.7.1" + resolved "https://registry.yarnpkg.com/@mantine/core/-/core-9.7.1.tgz#f5db858b22a32cf150cba88e30971bc94af1830a" + integrity sha512-SU1xklOfdEDzpPhw35ti9rF4QRE9HUongCe37C+xCO2Ea8MLJkAzreCrMxb8qcCij+gnF7SQbtTCBa1r+pKxYA== dependencies: "@floating-ui/react" "^0.27.20" clsx "^2.1.1" react-number-format "^5.4.5" react-remove-scroll "^2.7.2" - type-fest "^5.8.0" + type-fest "^5.10.0" -"@mantine/dates@^9.2.1": - version "9.6.0" - resolved "https://registry.yarnpkg.com/@mantine/dates/-/dates-9.6.0.tgz#676bcc64c37ec96c41d05613706131fca5693bf5" - integrity sha512-fcMGrbwKj7F5KkRtwuKeXz/MBh+l0Y3ljLyJ3eaF7NL0CLJSXO7YJxpeZDXBMDF0w+iUs9c2zilnTTidh9ICPQ== +"@mantine/dates@^9.7.1": + version "9.7.1" + resolved "https://registry.yarnpkg.com/@mantine/dates/-/dates-9.7.1.tgz#93bae3004f35b4444f96e391c9f8300a0b6383db" + integrity sha512-oFQf8b0cW2ArcdP6r0ATMA4gJN3D8Nc29J7wE4E2xM5mI1+nW6k47L0E45uKwxFb9CqdhXaN+XA9lbrkWkMfsg== dependencies: clsx "^2.1.1" -"@mantine/dropzone@^9.2.1": - version "9.6.0" - resolved "https://registry.yarnpkg.com/@mantine/dropzone/-/dropzone-9.6.0.tgz#6af362a618cc9a2b1c7bb9235d3cdd7d5066ee58" - integrity sha512-XvUx2Mxl2wKMUB1HoRPc+b6h5WDJ1ZPfYt4g6xDzNI4m8mnyKXmPJSgmEJD1RWzNA1CtxTX6rqRaiOUar4hbAw== +"@mantine/dropzone@^9.7.1": + version "9.7.1" + resolved "https://registry.yarnpkg.com/@mantine/dropzone/-/dropzone-9.7.1.tgz#c73ea8af33411e094631d9146c1090093e0996a1" + integrity sha512-WP4xW/2kUkfGVrMbWDsV5y3164CMvmabxoQ+jNZ77IuTsF7j7b8jHYPlgcnjMYolYG/dyOHI6UAniI8t/wmlKw== dependencies: - react-dropzone "20.1.1" + react-dropzone "20.1.2" -"@mantine/form@^9.2.1": - version "9.6.0" - resolved "https://registry.yarnpkg.com/@mantine/form/-/form-9.6.0.tgz#0bf78ac8fa9035616d95634935389d72dd49909d" - integrity sha512-4TbT/DiixG5uWjgm/HNQlS3Ac0Ov35WVcAW6HQ7Q4+NGuj8mED4gBXHzqVywNi7PhshivdNMox3zADvHIOyUdA== +"@mantine/form@^9.7.1": + version "9.7.1" + resolved "https://registry.yarnpkg.com/@mantine/form/-/form-9.7.1.tgz#5c5c1bf168898871e5e3fc9d649e24ecf47408fb" + integrity sha512-fVjWYdiMBw21z/bqUkFghyV1MsANHoqCZaXji0k4KJaRCHh7cD3qhBuLIVhc2hUCKXQ3cHiJTHLz3ayoE/mRZg== dependencies: "@standard-schema/spec" "^1.1.0" fast-deep-equal "^3.1.3" klona "^2.0.6" -"@mantine/hooks@^9.2.1": - version "9.6.0" - resolved "https://registry.yarnpkg.com/@mantine/hooks/-/hooks-9.6.0.tgz#476aa8f9eb13b020190475de53c76177d67d28be" - integrity sha512-r+1RWxc0F2XK9exKIk3Cxc17eyDTEtRqvomeQMCqI4S2Z3A78ObAGuNS8WXn8aqDGN66MJlO+WV7oCgQn2iQfQ== +"@mantine/hooks@^9.7.1": + version "9.7.1" + resolved "https://registry.yarnpkg.com/@mantine/hooks/-/hooks-9.7.1.tgz#08247cee0069b03840d9080de7ca2f83c21c1a8d" + integrity sha512-C1xD4A3bfB87nUsLHr42WqOabnK4G+fCyEf8FN14EEiZAcKjdcWJbK63qxhIXFKuHCc3acLbpm2ts9UqmLASTw== -"@mantine/modals@^9.2.1": - version "9.6.0" - resolved "https://registry.yarnpkg.com/@mantine/modals/-/modals-9.6.0.tgz#d4d378e9a7a231233dc92d995a0e3b3b1a1ee61d" - integrity sha512-uNNwdYkk5ZqsPKi6zVe+bHq09RCM/HRbzC+UFGXmmOKq2AgOuCoLPNimvVV3y6GQ+jcPFFlkR4D7wbv4eQKPAQ== +"@mantine/modals@^9.7.1": + version "9.7.1" + resolved "https://registry.yarnpkg.com/@mantine/modals/-/modals-9.7.1.tgz#c198820cc457b14b0558e2173b1548be34549f5c" + integrity sha512-pJbnRaqBmOUaiMk8ByJ/SZ9nbLwAn7JuaYYmwyToi+uQWut2gdxo1MZRE594Foaiw1lkHqzBoGBzTJlo/iut3g== -"@mantine/notifications@^9.2.1": - version "9.6.0" - resolved "https://registry.yarnpkg.com/@mantine/notifications/-/notifications-9.6.0.tgz#8e265127347219f07a73aef2bc84ee6c351615b4" - integrity sha512-1sTE7EaGpr6LX7ykE8DOwpTNxi3U7AHF7Ve/oBd1VEe2k/5RmEYLTWpexxf7WwNpEmHCi99MvG7qUMQJyqc9Fg== +"@mantine/notifications@^9.7.1": + version "9.7.1" + resolved "https://registry.yarnpkg.com/@mantine/notifications/-/notifications-9.7.1.tgz#541ac2474ca1a86484cbcf85963376361d5f7609" + integrity sha512-gmN+rTrjwYFJqaIqDQtQfoTJ/OR0wSxqxIihOHD5/S/sYUycVi9wDqK4tub5y8x0HeQH4kNkehPMN4l1Uk0A6A== dependencies: - "@mantine/store" "9.6.0" + "@mantine/store" "9.7.1" react-transition-group "4.4.5" -"@mantine/spotlight@^9.2.1": - version "9.6.0" - resolved "https://registry.yarnpkg.com/@mantine/spotlight/-/spotlight-9.6.0.tgz#daeb336d5dfd39ddc03cc7a05fb00145f48770bf" - integrity sha512-ZJgdrMWnhZDWSu6/OHcUkk5cXgLhkBnICCWWnHpuaapAKIa+ya3jj0BXDA8fzb3iY6yKCF06bqpKY5rhiuKaCw== +"@mantine/spotlight@^9.7.1": + version "9.7.1" + resolved "https://registry.yarnpkg.com/@mantine/spotlight/-/spotlight-9.7.1.tgz#6487b49570cb9bfa5e30727011f1693cb5128b1e" + integrity sha512-ICfB5/Q1I2mYhnSZmnwmmihW2ecSSGrL0mV0YdDnWCaKxbRm7OsbDPmCEmPJfKfCf1K2mwsY6Wz2bq9e/cRYKw== dependencies: - "@mantine/store" "9.6.0" + "@mantine/store" "9.7.1" -"@mantine/store@9.6.0": - version "9.6.0" - resolved "https://registry.yarnpkg.com/@mantine/store/-/store-9.6.0.tgz#fb76e8579fdb7f3908ac97abc92225597a8c3d73" - integrity sha512-nYt2tfn1/e6CK35a5TWlPbOL3Xwf7OzBmBElV0l+Ae6mswVaMip1WHm0mCaHAbTKgsOnvL/tooDTVxBBrmm87Q== +"@mantine/store@9.7.1": + version "9.7.1" + resolved "https://registry.yarnpkg.com/@mantine/store/-/store-9.7.1.tgz#cbb8862189abd9fada59843846077a22779910d2" + integrity sha512-CYCQHWt7zFrUe2kVQmFAWnQaZbiDUoN2VeMJBI/vf1XybGj+itTEB6cnvoX4C8yOrln3sXe4BIoIf46pUhnEhA== -"@mantine/tiptap@^9.2.1": - version "9.6.0" - resolved "https://registry.yarnpkg.com/@mantine/tiptap/-/tiptap-9.6.0.tgz#e033660e84e77907b6e2408e201177f4191c2476" - integrity sha512-OnENN4bVCc6OZaFLmT0KIbuRM8b1MxiooEyjc+1AQc/4lj5pJ9uae1k4zaCKGGok2i5WwJEejlDdgLjPn9YQyQ== +"@mantine/tiptap@^9.7.1": + version "9.7.1" + resolved "https://registry.yarnpkg.com/@mantine/tiptap/-/tiptap-9.7.1.tgz#d898e29b878166bc8e456a3e4234899b6770847d" + integrity sha512-6y3sT8jzMR/HxrZjN+XTtRpF8JSWfl95kjqR6j9Jn33w3RNjJEIDd8o7DPR+lBJGFeOwoyIZ8whcoBmNLPNSsA== "@mantine/utils@^6.0.22": version "6.0.22" resolved "https://registry.yarnpkg.com/@mantine/utils/-/utils-6.0.22.tgz#7eace697084e2bc5a831eb0fd7cbbc04cc1b0354" integrity sha512-RSKlNZvxhMCkOFZ6slbYvZYbWjHUM+PxDQnupIOxIdsTZQQjx/BFfrfJ7kQFOP+g7MtpOds8weAetEs5obwMOQ== -"@mantine/vanilla-extract@^9.2.1": - version "9.6.0" - resolved "https://registry.yarnpkg.com/@mantine/vanilla-extract/-/vanilla-extract-9.6.0.tgz#32356863125c39f2241d9cddcba73c180a822967" - integrity sha512-dLTSnwb9+kbe4GmfkJbmnlszHch/MrEkClX6eIimyzzP14YTJEiz7l+U5Wv/hYzFmWPPiSHhBizRYohS0+2zNQ== +"@mantine/vanilla-extract@^9.7.1": + version "9.7.1" + resolved "https://registry.yarnpkg.com/@mantine/vanilla-extract/-/vanilla-extract-9.7.1.tgz#e33a4281270c3b0de27b2c2a90f7e6d43cfc7f7f" + integrity sha512-9MX7htxw6JTx7hJUPAIdITQh7Mse4gKfspN1EdEsl93ulu7cdi4BC9x4aotXIlv3ShC/uc5MzOlWAEz084qaIQ== "@marijn/find-cluster-break@^1.0.0": version "1.0.4" @@ -1691,149 +1691,149 @@ dependencies: "@tanstack/query-core" "5.102.8" -"@tiptap/core@3.30.6", "@tiptap/core@^3.23.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-3.30.6.tgz#f60079cd863a3bcc42791b2411b2e76aa8ce22d3" - integrity sha512-M32+WOx1IrPRbPunhG1Xl7ETgUZJGABpGi/y969Uio6rR4tPBnOgFG5xXE3Xw6OQtYUqUE2n9nnnckoObZwBzA== +"@tiptap/core@3.31.4", "@tiptap/core@^3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-3.31.4.tgz#6deb89d6a7db4e20a7e12ca50784328ed826b410" + integrity sha512-cIVUQYBycighzsk6GbmCef8ZiEDVxoT5EuAe4xg2q9dNkDqCXVqI4YGr9aavCQEiAS4uHiRwqvcsPqJHipFDfg== -"@tiptap/extension-blockquote@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-3.30.6.tgz#81895ccfe540a97609c98db1a8590c0628e03c10" - integrity sha512-tOBZ1Ol7zWCAlsuHK76cWoMMJyyAhJG+cZn4iFRtmGQZGyZUQMYYlSahgvxbDLFdcuB977nuUNAI2XniEKA6cQ== +"@tiptap/extension-blockquote@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-3.31.4.tgz#5911721e9328b60964819926fd51cf0aaee5679b" + integrity sha512-FHnbvCIW0eDKgUUFjmvEB0ax9YUBW7/Unm+38qpuQROvU872yRqhnDtycKT9IKk46jNdjM4A4fAGUbge10CrAA== -"@tiptap/extension-bold@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-3.30.6.tgz#f4bac134fc0ab18983cad8805a75f875cb2e44a8" - integrity sha512-vQSqHcnoe5K9f5/XpWH8tBDvrFI3uvEv17qFj28dlNH/DOIMHPSBSFKzrfinuFcDGHHb1B1pTE5Yu0KH2Qomow== +"@tiptap/extension-bold@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-3.31.4.tgz#d059630d72f0088505e4066c65922b3f5ffc2477" + integrity sha512-QX1xoXvO0gz6uVsoLegumqQ4MJXL7lcUDQZ4J7la7LBqiIh4efrCmp2EXbhpr37M/YQL7w8q3g7H/VovSh7xIg== -"@tiptap/extension-bubble-menu@^3.30.6": - version "3.31.3" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.31.3.tgz#79e8ed9ec774900994b1ab1d320b5d10247c17d4" - integrity sha512-EV6ZnwKc++2OM/OcD54n8s1B7C9LP7GKAtdEPwu0t3BYJf3sG8Ueoinf7SgGPO9oMPg96GneOkDNm1urMV167g== +"@tiptap/extension-bubble-menu@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.31.4.tgz#412b5c56f77d0511bc465666f8e89658e4cf68d4" + integrity sha512-Ml/yswuys+3QkOMajfXyAGlUKCxEbT3dMyF8vHwQ5mDk9MDm+dqp10It5CrDBkUT/KkVTa6AJI3kb0TXZJZiAA== dependencies: "@floating-ui/dom" "^1.0.0" -"@tiptap/extension-bullet-list@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-3.30.6.tgz#51233a48ea8943d838c5aef8ebb9c45e20f51ec6" - integrity sha512-rVPEN4fZqwDcNrxfxSgPkoK4gPbwVLgwEJWvb9Y3Nhf4SkwrKDvO05QHTa6+DGe5LP3Tl5dC305l5UsmnwfHqg== +"@tiptap/extension-bullet-list@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-3.31.4.tgz#90d0748f01e27835edebabdf10cd4892909eb404" + integrity sha512-gHn1RDR4olCnmKpH+G9x+f8R+iKPNVdLlvbFkIpR/stjabZi7fB4Rr5GiHWsulbtxAjdQrNjnVeNdGM4rBxApw== -"@tiptap/extension-code-block@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-3.30.6.tgz#013471746bc737b25fcf29923f5a775386a49764" - integrity sha512-zsAcDEcVIDxxCh+Em0O1yd252gh3T2WmoezPYQOqVIiZwW8yztebjiKMoagFvLyVKZBtHdkW9cYgb62PrR17Pw== +"@tiptap/extension-code-block@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-3.31.4.tgz#a3b6161b83161fc88625f76e0bb87e4516c42675" + integrity sha512-su/B/cfQ1HpiHl6JfJ6wa+RVEptiYsV4dCntE8XQNj2dUcII4y6w6PQbbswOoDrU79uXHsAvpy44215rghG4LA== -"@tiptap/extension-code@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-3.30.6.tgz#ae4950083a7e7c6a5686ede2b589963014822293" - integrity sha512-az8RByq9Aftg+AwrQ2fS08o4SJZ9VIwJNYngwDwAGY8OxM1z/tPVTZRz771XV6V+ngbfh7J9Rv3Gk4MNmNUp0w== +"@tiptap/extension-code@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-3.31.4.tgz#0da4874c90aa708ead8b9719edc6e8f645529c78" + integrity sha512-pzi8GNDs5J1DEoO1HNYiJ5WZpgOZzlc/2VIa77mMpEuD4hZxuK0BQzlF/O6Ps/O5P1lGJT4xS0Yowpzb9mD9RA== -"@tiptap/extension-document@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-3.30.6.tgz#dd0c91250f0a4f10336a143693f32d946eda88dd" - integrity sha512-eaDIPRJNAzc3hpRizrySColkdxXDBtrFhZHRU3TZv2O6HVKmg3LVSSCpJiHSxY6iR8+TC41p4bg8Ql6O20Y9xQ== +"@tiptap/extension-document@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-3.31.4.tgz#49c53a2c2b1b2a319b7d20f28cb7413dc3bd834d" + integrity sha512-vT20u62RWR+BLIyNJ54e24MPUY4zm8VF7tcgJnxQXi7BgVJWcNMtMWWDIkPJ4JNXwfoIznKh3jzYsaYmbCJgQQ== -"@tiptap/extension-dropcursor@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-3.30.6.tgz#62d5b1bd8b65a4b3c34750ee1f5f5dabbc0fcdd9" - integrity sha512-GDuhDsUxIIiluLS1RuZ9zoPqHdybqJpuV6VhCGFJuzDmiraYPctj3AbbkX9xYZyukgO6tPN9MQtX26LJuFt7og== +"@tiptap/extension-dropcursor@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-3.31.4.tgz#deef7743648eff12ae2afbf33d75378d6a8202a0" + integrity sha512-5TbCPCC4RL+QT1NYHjXCYDBHBtVqYOFvFEnk3v98lvXCM2PNL13ofXUfJPuykAVy1OdTAx4DlZOQAD9WKy/IdQ== -"@tiptap/extension-floating-menu@^3.30.6": - version "3.31.3" - resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-3.31.3.tgz#82fe3deb3411378a674c57a0625716e77fbf31c8" - integrity sha512-rd4VJ9PGSP9Eop8ZTEwaLZcMzMXLuJKe3hUNf58rq+zANpwM+9fI+vB7g9MAc3eXwUNDxNDVACFIL2oeBqpQ3A== +"@tiptap/extension-floating-menu@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-3.31.4.tgz#3889c994c86ca86d9425f8dc666734e70747a0dc" + integrity sha512-pKzulkDmolpVt7IP9FSPsC5pKbXQ5pyeqkaAYr8cyOVW+KUObpPRIzAuXvEso8PCjWgwJ1f2qyishOEMw8MbfQ== -"@tiptap/extension-gapcursor@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-3.30.6.tgz#9914ea89898f9205f75d33ceba4115729b4091c1" - integrity sha512-QDEip3y0k2W2oMhNc1Yyi1NhD+4Q+BtCjuCRpRld9VH60cuRROIq60KU+VkOoCJ9o6p6djrn6j1g6PZsG69Bxw== +"@tiptap/extension-gapcursor@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-3.31.4.tgz#407e640910cc97db91b420b2bd109fde57703f75" + integrity sha512-hBogUAepQH6IQUhaePhPWzQ1Oeu+yqlJVMKO06Eue5m36uRy1PZZAQ780pCGT0SC4R5MH2td1N8EUQ7bupQdDA== -"@tiptap/extension-hard-break@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-3.30.6.tgz#50c4b8f2b2e60de85b67090123ea43ac3295b2a0" - integrity sha512-8ALo10VWFYLF/9eS3iRZrx3EMbokMhRdJbSgk9+dU+zuh8yGFQnMyf30Wpu3K9DIseHtDZQTI7rjl0o8ayNPZw== +"@tiptap/extension-hard-break@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-3.31.4.tgz#1f1bbb8102cb46d845c99289bab95975e074fb1e" + integrity sha512-oLNajLyUvRMg1qtzFgKGduygTlpaHFB9PU+M+pUWdA4sa1bRnxS2Y++Ao4lHYopJ7nxatLndrYoCIHkotqkPDQ== -"@tiptap/extension-heading@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-3.30.6.tgz#3ae25de14461bdf93564f60be1ee871ed441b0a6" - integrity sha512-32lPwP8BeLFGQV+U/HrwbK+ItQMiN13LppKnQtUUNl3FwG440qPpnQo7eCEAxTkUg/M+VTjNn/UHnJMsOFPklw== +"@tiptap/extension-heading@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-3.31.4.tgz#227cbe95012a4bfa9872960a8844ed330a5adc10" + integrity sha512-KqlSeLathpdxCc3kVHBCA/RkHvb+V7u5vgiSEu1Cv3L70r7F19COMGODav/k1AGYBgEXKL6lM9cy8cloE5/28g== -"@tiptap/extension-horizontal-rule@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.30.6.tgz#e4d312244f2f33c0ab85c31455265b0db1d61033" - integrity sha512-38SokgorOsmLNWwMHhrkoxIaWd73Setoo4J0slrlqvMItovshqjqYjtSohdUk09Yr+cxxcGUP9m8zxBEyi5ZsA== +"@tiptap/extension-horizontal-rule@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.31.4.tgz#d48fee5d0317d3761dc193b81191c53e1a6a24f8" + integrity sha512-ws0Kh8kfHL7ESILSXj0HJAXs8G10ewlW1sedXKm8dm0op9L8il+xl8bEJVoqG/jMf77900Fc2CaQldcCGRpF1g== -"@tiptap/extension-image@^3.23.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-3.30.6.tgz#910e5282e21ad717c0d1722e8820308c646cb4cd" - integrity sha512-Y4oWgODXa+Jd/JiWWCxnr7v+1qtK1CzgliLHTk0x/H6RTuzduvL5/zTt0P4+inh7s/vQewliDyg/iagj+gBpXg== +"@tiptap/extension-image@^3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-3.31.4.tgz#6d7f629c02151b045a271f89d034d82c3a042b0b" + integrity sha512-vtD+rK73abJnPRKOjU37sQTCa5Auyg451yhqZD/3lZKiFJDBHAZKI7lQARkXBQYK289hBhK6yMIwofoqCBvi3g== -"@tiptap/extension-italic@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-3.30.6.tgz#284a198c8ead4e75a7516c42a0b16ad96611bb70" - integrity sha512-vbltMPEePHWsGhvPH01yfT9GNJNMs2oUwPhqC0z/Brychu/oFe+0FR4RKFBVXnZg8oRfGQEOJ6OjD9jKDAZ0zQ== +"@tiptap/extension-italic@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-3.31.4.tgz#bed5bdf17e65a1e6d8a34f592ff84b3a73d52af1" + integrity sha512-pftTQBPYZocNDAj/u6OyDIp7YxmN0+N895cQPogJQ6qMHdLiRhzgawiEewJkniyN1w8k3Wx/Etp8pTKrxaOOBQ== -"@tiptap/extension-link@3.30.6", "@tiptap/extension-link@^3.23.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-3.30.6.tgz#8d918ac92d0da0715af946ab5a9439bb861627a1" - integrity sha512-xyQ5e3oKphCE84ig8bXCJ6I2esMmFvwHJk4/o8uIxQBVT1yOUyG3ZNJG0431SQ2xrUw0bPWY80F9Kla7HFiSgA== +"@tiptap/extension-link@3.31.4", "@tiptap/extension-link@^3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-3.31.4.tgz#9fdfc30f684c0f502943a5228c93c1ba74586139" + integrity sha512-LLxW3qfeL9gvDdMPVdxYFYZ0zZSjsoIDhOqjJ5U2UfaK3U2iwkKTBgV2qT+FWf3uUHmBRjBZ08q3dRPmWBAD5g== dependencies: linkifyjs "^4.3.3" -"@tiptap/extension-list-item@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-3.30.6.tgz#08d5ca452a2c84abafaa66d88782cd5946907b49" - integrity sha512-a97knOHxLI/QF1nvtn6SIC9abspB7miKXBSXMGQwlt5iqJcwHDyj1neXi2KAruam8i4932uc+zishYeFka9yvA== +"@tiptap/extension-list-item@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-3.31.4.tgz#c986b451d476174702045302d2a214f8207461c3" + integrity sha512-ao8XAPvhOrtGFILs+fKtOM91omDSpUC8X19m44gbaq5xJDVpIMZ0rP+NAVUS72G7LskhcHa0bu/Pm022bUVjVw== -"@tiptap/extension-list-keymap@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-list-keymap/-/extension-list-keymap-3.30.6.tgz#128d4b8c4fa91caaea58e8fd0bdc6c4c4aded317" - integrity sha512-FTNFPR+DYDWR48Ak46tvpNzHPQ1exUgxFVUv4grHJq8tdMl1KWYW4DZmcOIe2Tk7kmlDmC14rKmUvFcdKMUgAA== +"@tiptap/extension-list-keymap@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-list-keymap/-/extension-list-keymap-3.31.4.tgz#a73c1c32731c31c3cbdc292fca6a71d30458ac47" + integrity sha512-etdkl/YnYfwtALDxecJ+4WYgJAez1fn1vzzZqJklZBpgqiZtny3NxpvWS7/JhsltNG84Wo/oovpfJgVmzhddrw== -"@tiptap/extension-list@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-list/-/extension-list-3.30.6.tgz#cfe173c444f91551c6f59b73668c435a2e4a790f" - integrity sha512-vNzXMap27plACDvyeEUlUyB01Rw1IOWn+wABIEuiIyJzVpZ6n5T8ck2nwHXp6lTy5xxGi7MnVq8epOjqCG1g8A== +"@tiptap/extension-list@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-list/-/extension-list-3.31.4.tgz#7b339e7c1fbc590cdb475eaca4823a8b90c31484" + integrity sha512-Wd4YRYxOdf+TtF33/QsRo1RK1CecVxjYGjUBIKNGHAywfh8tghgV4YOUCiV1iJqYt2IBs+RWlIqTmdIPzBrd4A== -"@tiptap/extension-ordered-list@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-3.30.6.tgz#f3ed6ef7e6db0a269ab77272edca9f61daddabb5" - integrity sha512-DWQELBaaCtL9ZRdkeIjgNsz0wZ93SAdPVBpH0pIxOsf/p2JbSBI1ipCVGVysr9SB8ULGsCthZk+cZwZoAOu7qA== +"@tiptap/extension-ordered-list@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-3.31.4.tgz#5b47a963d452e6258946519a2271965ebcbb34e8" + integrity sha512-UNWyF+Dhm08znszIMnT8Y2kJ1lvtiCvUGs1aG7m9cPHxDa7VycWnOqdtXWkc+6xRLNiC0zgWCFWgLLHduV8pRQ== -"@tiptap/extension-paragraph@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-3.30.6.tgz#a6b50b8b229fd4da32bf98cbfdf1fb1d9763d441" - integrity sha512-l+syX39aTG3BSNmkIXgrawRgSrFmQ/Hlz+5j2+OYXcHFFqj2cnnpZMzZFMynJUFKRLDgldfUs+UMsgelZNDScw== +"@tiptap/extension-paragraph@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-3.31.4.tgz#b90574a72ae832b8ed76cf7d08a44d71ea0d80ae" + integrity sha512-WZlBgw+XVKXerRlmEsoQ7x1PwSU+dh3X+b70+ViZsLebdfsmmYBIaNDrvIGwG9UQr5eLnddJnZplMSTI2YKs+g== -"@tiptap/extension-strike@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-3.30.6.tgz#6645ca0befffcba05172089e3030a9619dd8dbb1" - integrity sha512-aRtyDDS88+eT6r3krn94ajLTVccxaukEHWJoDwq0devAqFq9d0ftaYU9aNSSKzG/TAsIObXX/1MKSiJQC+Z/Rw== +"@tiptap/extension-strike@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-3.31.4.tgz#99bf79b26cdb38c62056c8c06c4aed0ce98574f9" + integrity sha512-4MBr63TJIMqxCjE+gAPdlY1IdELgvbAP516K3Ntn/BkcpEbEoPMEYVz+XTxULyHQEuARDO6jsXPPxRXpA74jaw== -"@tiptap/extension-table@^3.23.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-3.30.6.tgz#a35ead79ffe83f5870d705d0b93bdf2efca0a8fe" - integrity sha512-O3y8ncZ0+2vTLXJ19W8UDg2hR+oV5QMojOGMqMX4dZ+cv55j2q1R3DZODY1D7PtVFFfScd6aWSMcXSn8TtMVmw== +"@tiptap/extension-table@^3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-3.31.4.tgz#ded2536323240281ed6f4f67e7c365528a5b985a" + integrity sha512-ai7DLwg/xmhlUas3LgWaPQdPlZ8StliY6Dn+iUH6eyZ/TQqRDY/oeIsKWaHpXq22T5M5dMlZPTg172ZdtwKswQ== -"@tiptap/extension-text@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-3.30.6.tgz#7d6be07cd7835eb4f80a55589a7e1286cb50108c" - integrity sha512-N55Mf9Xo3Zl5f7MSlBpxIYWktYIgE7yY3QT9IGrhm6VBSF9bAHToVDOEWCkgTEinerBcWraPFMDl8NMtX7Cdqw== +"@tiptap/extension-text@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-3.31.4.tgz#f51ed21ef44197c35f08b903795236deb1908c2c" + integrity sha512-WL2ZRpFksgQcXJN05/UQH09YCH9LlhdLrSEcviASvEH/aAfsKiJlDrouN8PET+YAWK/F5xFc5/Q1z0xj+I23jw== -"@tiptap/extension-underline@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-3.30.6.tgz#7642f8213655d72c19550938eb4ebf1385bb28ec" - integrity sha512-6NyWmSOGzbeiwT6Vita3F/msePEQ2fuuzjSguPU1ebXUG04D3h8rNjoBrrfGt6OrwP/pdIri3VVVP5H1suxxkA== +"@tiptap/extension-underline@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-3.31.4.tgz#e893adfc2d0d2c4694bbc2331b6f9c48f73ef844" + integrity sha512-0iFGPIqmqGkCrOFh1OPo7Dzv5s86dVN3bZaugsz0jK04otkb8bn04ZDKP2/6Wq+lcK0M9DfJ0QqfW5hC2nBZxQ== -"@tiptap/extensions@3.30.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/extensions/-/extensions-3.30.6.tgz#61402321bef055dc69500bf25a60895f77d1ea1a" - integrity sha512-8SYpu3E+ZuWhTGWtjcWPtcJmcotrADYBuXIu+bgdZjcuaxRHs+6IpNvdLabl3q46W7+0A11GginPDJNqrMKk2A== +"@tiptap/extensions@3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/extensions/-/extensions-3.31.4.tgz#ac29dfeb89f78434936e21d3daa8bf54089afe81" + integrity sha512-nIupIne/sHpV2Kw1M4MOmr4+G454KI8BXM5+LWKOU2EpzfALZWVSy7koF9lVRna4Zp5Gv8hfg5Z8kvmrYQvhkw== -"@tiptap/pm@3.30.6", "@tiptap/pm@^3.23.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-3.30.6.tgz#0bf87c4cf65caf55989be448f80457bd3c5b1096" - integrity sha512-FVzgc+7TMKUreBqFHmyhDSkFUMZPgIzWdeNt4sWLFEzwzdklgKy/+MZClAq9m+bJJU2FKC2+SVmWTatZD6didg== +"@tiptap/pm@3.31.4", "@tiptap/pm@^3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-3.31.4.tgz#f76cbacc32038921e41f0530a44fa1c40a818dce" + integrity sha512-x7dwth79cQp4QGae66/RJqMDJQv0+n7b7dD7cyFQBWqzPFLJxN/4paHY4qbWedZjNLhRyIOAD9a5q4AYM1zmPA== dependencies: prosemirror-changeset "^2.4.1" prosemirror-commands "^1.7.1" @@ -1847,49 +1847,49 @@ prosemirror-state "^1.4.4" prosemirror-tables "^1.8.5" prosemirror-transform "^1.12.0" - prosemirror-view "^1.41.9" + prosemirror-view "^1.42.3" -"@tiptap/react@^3.23.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-3.30.6.tgz#c9fea4bfc57ef41eafd47aaea6a9d061d1b1279b" - integrity sha512-0MhwGYB2JbMnXs2sjjFFOUzA0td7BmWOBIEFW66aw+MhramoGwH3pTfv08BHuS4++CAOthSo3dRQV3ZIaeYNmQ== +"@tiptap/react@^3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-3.31.4.tgz#bc27a1965c0040b82200915d411f0e74853c19f7" + integrity sha512-mBvvXgdPhMinT1NPfwHvFK1dFdMXHVWMSeNn0IPj7/zV0XBmOnASkEOwoIvl8NluKYEtpZBE1yA+6pN2enLa5g== dependencies: "@types/use-sync-external-store" "^0.0.6" fast-equals "^5.3.3" use-sync-external-store "^1.4.0" optionalDependencies: - "@tiptap/extension-bubble-menu" "^3.30.6" - "@tiptap/extension-floating-menu" "^3.30.6" + "@tiptap/extension-bubble-menu" "3.31.4" + "@tiptap/extension-floating-menu" "3.31.4" -"@tiptap/starter-kit@^3.23.6": - version "3.30.6" - resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-3.30.6.tgz#3badc527b20d1d2005026ef9d867b236188a8758" - integrity sha512-qQ+awpHaTNYhYbAAxLavWAo0CJBq43m1088yCxHhsITT1b2gGI0Mp2tTJ4aWtm0GxaJIlERUEvHfZRj/HuOJRg== +"@tiptap/starter-kit@^3.31.4": + version "3.31.4" + resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-3.31.4.tgz#95edc876d390cf4757de291b525628515c4b5c6e" + integrity sha512-u7HIJ9vK6kFe4S4J2CIcDLrhRZzoTMSyU0XxjQ6lUNF7pWW3rMi//pvi8RcsZmxGjyb8CpXAqYgNl7f9WyaUeA== dependencies: - "@tiptap/core" "3.30.6" - "@tiptap/extension-blockquote" "3.30.6" - "@tiptap/extension-bold" "3.30.6" - "@tiptap/extension-bullet-list" "3.30.6" - "@tiptap/extension-code" "3.30.6" - "@tiptap/extension-code-block" "3.30.6" - "@tiptap/extension-document" "3.30.6" - "@tiptap/extension-dropcursor" "3.30.6" - "@tiptap/extension-gapcursor" "3.30.6" - "@tiptap/extension-hard-break" "3.30.6" - "@tiptap/extension-heading" "3.30.6" - "@tiptap/extension-horizontal-rule" "3.30.6" - "@tiptap/extension-italic" "3.30.6" - "@tiptap/extension-link" "3.30.6" - "@tiptap/extension-list" "3.30.6" - "@tiptap/extension-list-item" "3.30.6" - "@tiptap/extension-list-keymap" "3.30.6" - "@tiptap/extension-ordered-list" "3.30.6" - "@tiptap/extension-paragraph" "3.30.6" - "@tiptap/extension-strike" "3.30.6" - "@tiptap/extension-text" "3.30.6" - "@tiptap/extension-underline" "3.30.6" - "@tiptap/extensions" "3.30.6" - "@tiptap/pm" "3.30.6" + "@tiptap/core" "3.31.4" + "@tiptap/extension-blockquote" "3.31.4" + "@tiptap/extension-bold" "3.31.4" + "@tiptap/extension-bullet-list" "3.31.4" + "@tiptap/extension-code" "3.31.4" + "@tiptap/extension-code-block" "3.31.4" + "@tiptap/extension-document" "3.31.4" + "@tiptap/extension-dropcursor" "3.31.4" + "@tiptap/extension-gapcursor" "3.31.4" + "@tiptap/extension-hard-break" "3.31.4" + "@tiptap/extension-heading" "3.31.4" + "@tiptap/extension-horizontal-rule" "3.31.4" + "@tiptap/extension-italic" "3.31.4" + "@tiptap/extension-link" "3.31.4" + "@tiptap/extension-list" "3.31.4" + "@tiptap/extension-list-item" "3.31.4" + "@tiptap/extension-list-keymap" "3.31.4" + "@tiptap/extension-ordered-list" "3.31.4" + "@tiptap/extension-paragraph" "3.31.4" + "@tiptap/extension-strike" "3.31.4" + "@tiptap/extension-text" "3.31.4" + "@tiptap/extension-underline" "3.31.4" + "@tiptap/extensions" "3.31.4" + "@tiptap/pm" "3.31.4" "@types/babel__core@^7.1.18", "@types/babel__core@^7.20.5": version "7.20.5" @@ -4175,6 +4175,13 @@ prosemirror-model@^1.0.0, prosemirror-model@^1.21.0, prosemirror-model@^1.25.11, dependencies: orderedmap "^2.0.0" +prosemirror-model@^1.25.12: + version "1.25.12" + resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.25.12.tgz#00232836bf4f5bdfdfe8300d00706564515bfc28" + integrity sha512-Ue2gTmXMa7EhpLNhC7J+h4+ykD8ha12K6rrZFFKKJHBForfIStw5gJ6Zrf1mqqAa9NmpmEB4wp9bKmA4eUVjWg== + dependencies: + orderedmap "^2.0.0" + prosemirror-schema-list@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz#5869c8f749e8745c394548bb11820b0feb1e32f5" @@ -4211,7 +4218,7 @@ prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transfor dependencies: prosemirror-model "^1.21.0" -prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.27.0, prosemirror-view@^1.31.0, prosemirror-view@^1.41.4, prosemirror-view@^1.41.9: +prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.27.0, prosemirror-view@^1.31.0, prosemirror-view@^1.41.4: version "1.42.3" resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.42.3.tgz#5b1c8f1b084f9ef2bd7f67057d1784383ee6ac55" integrity sha512-oTN7EtH+CpwxU9NrwEYWd0UZ4JUx7l048l5A2Xppm4p/60isZYLnth9QVQmC3VRIvdrIWCxwZSd+Uz791G31/w== @@ -4220,6 +4227,15 @@ prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.27.0, pros prosemirror-state "^1.0.0" prosemirror-transform "^1.1.0" +prosemirror-view@^1.42.3: + version "1.42.6" + resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.42.6.tgz#d72d3ddafdc9c97db870f051f5cdd7c872eb53fc" + integrity sha512-3g25f6hKV2bBxo3puH3EcYWmmVNSH0iWmNsMVT7sugDSpA025jgpOLarcDWr39SdzPjYMaiYtRdNtXialPSjEQ== + dependencies: + prosemirror-model "^1.25.12" + prosemirror-state "^1.0.0" + prosemirror-transform "^1.1.0" + proxy-from-env@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz#a7487568adad577cfaaa7e88c49cab3ab3081aba" @@ -4261,10 +4277,10 @@ react-draggable@^4.4.5, react-draggable@^4.5.0: clsx "^2.1.1" prop-types "^15.8.1" -react-dropzone@20.1.1: - version "20.1.1" - resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-20.1.1.tgz#8ec530ea9421ca006ef6df986635adeb69e93ed4" - integrity sha512-2cilRFP8bsjDOHpV0sJ6XY8pzJhmz4/cQ6s9yeckOACWYDR+n4MGGtnJh3Rycq/9SLhDWugqDx1z5mtfmOOZhw== +react-dropzone@20.1.2: + version "20.1.2" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-20.1.2.tgz#f05d27cb331800c8019f1faf3c2699e21d66806d" + integrity sha512-gj2m31ZmYOZjTuW6CDfx38/WW2JlbuQ/X2Il8cOwIWa7Znp7vQSPJFtRfDbAjusBuoXzAYktI+r2R6iKMaRTPw== dependencies: attr-accept "^4.0.0" file-selector "^5.0.0" @@ -4858,10 +4874,10 @@ type-fest@^0.8.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -type-fest@^5.8.0: - version "5.8.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-5.8.0.tgz#6d517998257c33159db4d4da6f18efa33bd47df3" - integrity sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA== +type-fest@^5.10.0: + version "5.10.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-5.10.0.tgz#bf0131fdf662889c94c409d37402e364fb2016f7" + integrity sha512-NoSdpq/WEiAg5sjmBkmV/hfxv6HJH4NqPNrqjtSO5CwRmpsDfaf4begxW34KdJykH/l1yHtwBWQkCRdoXO8mPA== dependencies: tagged-tag "^1.0.0"