tweaks and changes

This commit is contained in:
vabene1111
2025-04-02 09:01:52 +02:00
parent 838ce6615b
commit b18a1d0110
16 changed files with 195 additions and 1879 deletions

View File

@@ -1689,6 +1689,7 @@ class SourceImportDuplicateSerializer(serializers.Serializer):
class RecipeFromSourceResponseSerializer(serializers.Serializer):
recipe = SourceImportRecipeSerializer(default=None)
recipe_id = serializers.IntegerField(default=None)
images = serializers.ListField(child=serializers.CharField(), default=[], allow_null=False)
error = serializers.BooleanField(default=False)
msg = serializers.CharField(max_length=1024, default='')

View File

@@ -1713,27 +1713,35 @@ class RecipeUrlImportView(APIView):
elif url and not data:
if re.match('^(https?://)?(www\\.youtube\\.com|youtu\\.be)/.+$', url):
if validate_import_url(url):
# TODO new serializer
return Response({'recipe_json': get_from_youtube_scraper(url, request), 'recipe_images': []}, status=status.HTTP_200_OK)
if re.match('^(.)*/recipe/[0-9]+/[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', url):
recipe_json = requests.get(
url.replace('/recipe/', '/api/recipe/').replace(re.split('/recipe/[0-9]+', url)[1],
'') + '?share='
+ re.split('/recipe/[0-9]+', url)[1].replace('/', '')).json()
response['recipe'] = get_from_youtube_scraper(url, request)
if url and url.strip() != '':
response['duplicates'] = Recipe.objects.filter(space=request.space, source_url=url.strip()).values('id', 'name').all()
return Response(RecipeFromSourceResponseSerializer(context={'request': request}).to_representation(response), status=status.HTTP_200_OK)
tandoor_url = None
if re.match('^(.)*/recipe/[0-9]+/\?share=[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', url):
tandoor_url = url.replace('/recipe/', '/api/recipe/')
elif re.match('^(.)*/view/recipe/[0-9]+/[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', url):
tandoor_url = (url.replace('/view/recipe/', '/api/recipe/').replace(re.split('/recipe/[0-9]+', url)[1], '') + '?share=' +
re.split('/recipe/[0-9]+', url)[1].replace('/', ''))
if tandoor_url and validate_import_url(tandoor_url):
recipe_json = requests.get(tandoor_url).json()
recipe_json = clean_dict(recipe_json, 'id')
serialized_recipe = RecipeExportSerializer(data=recipe_json, context={'request': request})
if serialized_recipe.is_valid():
recipe = serialized_recipe.save()
if validate_import_url(recipe_json['image']):
if '?' in recipe_json['image']:
filetype = pathlib.Path(recipe_json['image'].split('?')[0]).suffix
else:
filetype = pathlib.Path(recipe_json["image"]).suffix
recipe.image = File(handle_image(request,
File(
io.BytesIO(requests.get(recipe_json['image']).content),
name='image'),
filetype=pathlib.Path(recipe_json['image']).suffix),
name=f'{uuid.uuid4()}_{recipe.pk}{pathlib.Path(recipe_json["image"]).suffix}')
File( io.BytesIO(requests.get(recipe_json['image']).content), name='image'),
filetype=filetype),
name=f'{uuid.uuid4()}_{recipe.pk}.{filetype}')
recipe.save()
# TODO new serializer
return Response({'link': request.build_absolute_uri('recipe/' + recipe.pk)}, status=status.HTTP_201_CREATED)
response['recipe_id'] = recipe.pk
return Response(RecipeFromSourceResponseSerializer(context={'request': request}).to_representation(response), status=status.HTTP_200_OK)
else:
try:
if validate_import_url(url):

View File

@@ -7,7 +7,7 @@ from urllib.request import urlretrieve
os.chdir('vue3/src/openapi')
# generate base API client for all models
os.system('openapi-generator-cli generate -g typescript-fetch -i http://127.0.0.1:8000/openapi/')
os.system('openapi-generator-cli generate -g typescript-fetch -t templates -i http://127.0.0.1:8000/openapi/')
sys.exit(0)

View File

@@ -3,9 +3,9 @@
<v-app-bar color="tandoor" flat density="comfortable" v-if="!useUserPreferenceStore().isAuthenticated">
</v-app-bar>
<v-app-bar color="tandoor" flat density="comfortable" v-if="useUserPreferenceStore().isAuthenticated">
<v-app-bar :color="useUserPreferenceStore().userSettings.navBgColor" flat density="comfortable" v-if="useUserPreferenceStore().isAuthenticated">
<router-link :to="{ name: 'view_home', params: {} }">
<v-img src="../../assets/brand_logo.svg" width="140px" class="ms-2"></v-img>
<v-img src="../../assets/brand_logo.svg" width="140px" class="ms-2" v-if="useUserPreferenceStore().userSettings.navShowLogo"></v-img>
</router-link>
<v-spacer></v-spacer>
<global-search-dialog></global-search-dialog>

View File

@@ -1,6 +1,7 @@
<template>
<slot name="activator">
<v-btn @click="dialog = true" variant="plain" density="default" :icon="mobile">
<v-btn @click="dialog = true" variant="plain" icon="fa-solid fa-search" class="mr-1 fa-fw" v-if="mobile"></v-btn>
<v-btn @click="dialog = true" variant="plain" v-else>
<v-icon icon="fa-solid fa-search" class="mr-1 fa-fw"></v-icon>
<span class="d-none d-sm-block">{{ $t('Search') }}</span>
<v-chip size="x-small" variant="tonal" class="d-none d-md-flex ml-1" label>{{ $t('Ctrl+K') }}</v-chip>

View File

@@ -15,6 +15,9 @@
<v-label>{{$t('Nav_Color')}}</v-label>
<v-color-picker v-model="useUserPreferenceStore().userSettings.navBgColor" mode="hex" :modes="['hex']" show-swatches :swatches="[['#ddbf86'],['#b98766'],['#b55e4f'],['#82aa8b'],['#385f84']]"></v-color-picker>
<v-select :label="$t('Theme')" class="mt-4" v-model="useUserPreferenceStore().userSettings.theme" :items="[{title: 'Tandoor', value: 'TANDOOR'}, {title: 'Tandoor Dark', value: 'TANDOOR_DARK'}, ]">
</v-select>
<v-checkbox :label="$t('Show_Logo')" :hint="$t('Show_Logo_Help')" persistent-hint v-model="useUserPreferenceStore().userSettings.navShowLogo"></v-checkbox>
<v-checkbox :label="$t('Sticky_Nav')" :hint="$t('Sticky_Nav_Help')" persistent-hint v-model="useUserPreferenceStore().userSettings.navSticky"></v-checkbox>

View File

@@ -7,8 +7,6 @@ models/AccessToken.ts
models/AuthToken.ts
models/AutoMealPlan.ts
models/Automation.ts
models/AutomationTypeEnum.ts
models/BaseUnitEnum.ts
models/BookmarkletImport.ts
models/BookmarkletImportList.ts
models/ConnectorConfigConfig.ts
@@ -35,16 +33,6 @@ models/MealPlan.ts
models/MealType.ts
models/MethodEnum.ts
models/NutritionInformation.ts
models/OpenDataCategory.ts
models/OpenDataConversion.ts
models/OpenDataFood.ts
models/OpenDataFoodProperty.ts
models/OpenDataProperty.ts
models/OpenDataStore.ts
models/OpenDataStoreCategory.ts
models/OpenDataUnit.ts
models/OpenDataUnitTypeEnum.ts
models/OpenDataVersion.ts
models/PaginatedAutomationList.ts
models/PaginatedBookmarkletImportListList.ts
models/PaginatedCookLogList.ts
@@ -91,13 +79,6 @@ models/PatchedInviteLink.ts
models/PatchedKeyword.ts
models/PatchedMealPlan.ts
models/PatchedMealType.ts
models/PatchedOpenDataCategory.ts
models/PatchedOpenDataConversion.ts
models/PatchedOpenDataFood.ts
models/PatchedOpenDataProperty.ts
models/PatchedOpenDataStore.ts
models/PatchedOpenDataUnit.ts
models/PatchedOpenDataVersion.ts
models/PatchedProperty.ts
models/PatchedPropertyType.ts
models/PatchedRecipe.ts
@@ -157,6 +138,7 @@ models/SupermarketCategoryRelation.ts
models/Sync.ts
models/SyncLog.ts
models/ThemeEnum.ts
models/TypeEnum.ts
models/Unit.ts
models/UnitConversion.ts
models/User.ts

File diff suppressed because it is too large Load Diff

View File

@@ -13,12 +13,12 @@
*/
import { mapValues } from '../runtime';
import type { AutomationTypeEnum } from './AutomationTypeEnum';
import type { TypeEnum } from './TypeEnum';
import {
AutomationTypeEnumFromJSON,
AutomationTypeEnumFromJSONTyped,
AutomationTypeEnumToJSON,
} from './AutomationTypeEnum';
TypeEnumFromJSON,
TypeEnumFromJSONTyped,
TypeEnumToJSON,
} from './TypeEnum';
/**
*
@@ -34,10 +34,10 @@ export interface Automation {
id?: number;
/**
*
* @type {AutomationTypeEnum}
* @type {TypeEnum}
* @memberof Automation
*/
type: AutomationTypeEnum;
type: TypeEnum;
/**
*
* @type {string}
@@ -108,7 +108,7 @@ export function AutomationFromJSONTyped(json: any, ignoreDiscriminator: boolean)
return {
'id': json['id'] == null ? undefined : json['id'],
'type': AutomationTypeEnumFromJSON(json['type']),
'type': TypeEnumFromJSON(json['type']),
'name': json['name'] == null ? undefined : json['name'],
'description': json['description'] == null ? undefined : json['description'],
'param1': json['param_1'] == null ? undefined : json['param_1'],
@@ -127,7 +127,7 @@ export function AutomationToJSON(value?: Omit<Automation, 'createdBy'> | null):
return {
'id': value['id'],
'type': AutomationTypeEnumToJSON(value['type']),
'type': TypeEnumToJSON(value['type']),
'name': value['name'],
'description': value['description'],
'param_1': value['param1'],

View File

@@ -13,12 +13,12 @@
*/
import { mapValues } from '../runtime';
import type { AutomationTypeEnum } from './AutomationTypeEnum';
import type { TypeEnum } from './TypeEnum';
import {
AutomationTypeEnumFromJSON,
AutomationTypeEnumFromJSONTyped,
AutomationTypeEnumToJSON,
} from './AutomationTypeEnum';
TypeEnumFromJSON,
TypeEnumFromJSONTyped,
TypeEnumToJSON,
} from './TypeEnum';
/**
*
@@ -34,10 +34,10 @@ export interface PatchedAutomation {
id?: number;
/**
*
* @type {AutomationTypeEnum}
* @type {TypeEnum}
* @memberof PatchedAutomation
*/
type?: AutomationTypeEnum;
type?: TypeEnum;
/**
*
* @type {string}
@@ -106,7 +106,7 @@ export function PatchedAutomationFromJSONTyped(json: any, ignoreDiscriminator: b
return {
'id': json['id'] == null ? undefined : json['id'],
'type': json['type'] == null ? undefined : AutomationTypeEnumFromJSON(json['type']),
'type': json['type'] == null ? undefined : TypeEnumFromJSON(json['type']),
'name': json['name'] == null ? undefined : json['name'],
'description': json['description'] == null ? undefined : json['description'],
'param1': json['param_1'] == null ? undefined : json['param_1'],
@@ -125,7 +125,7 @@ export function PatchedAutomationToJSON(value?: Omit<PatchedAutomation, 'created
return {
'id': value['id'],
'type': AutomationTypeEnumToJSON(value['type']),
'type': TypeEnumToJSON(value['type']),
'name': value['name'],
'description': value['description'],
'param_1': value['param1'],

View File

@@ -38,6 +38,12 @@ export interface RecipeFromSourceResponse {
* @memberof RecipeFromSourceResponse
*/
recipe?: SourceImportRecipe;
/**
*
* @type {number}
* @memberof RecipeFromSourceResponse
*/
recipeId?: number;
/**
*
* @type {Array<string>}
@@ -82,6 +88,7 @@ export function RecipeFromSourceResponseFromJSONTyped(json: any, ignoreDiscrimin
return {
'recipe': json['recipe'] == null ? undefined : SourceImportRecipeFromJSON(json['recipe']),
'recipeId': json['recipe_id'] == null ? undefined : json['recipe_id'],
'images': json['images'] == null ? undefined : json['images'],
'error': json['error'] == null ? undefined : json['error'],
'msg': json['msg'] == null ? undefined : json['msg'],
@@ -96,6 +103,7 @@ export function RecipeFromSourceResponseToJSON(value?: RecipeFromSourceResponse
return {
'recipe': SourceImportRecipeToJSON(value['recipe']),
'recipe_id': value['recipeId'],
'images': value['images'],
'error': value['error'],
'msg': value['msg'],

View File

@@ -44,7 +44,7 @@ export type TypeEnum = typeof TypeEnum[keyof typeof TypeEnum];
export function instanceOfTypeEnum(value: any): boolean {
for (const key in TypeEnum) {
if (Object.prototype.hasOwnProperty.call(TypeEnum, key)) {
if (TypeEnum[key as keyof typeof TypeEnum] === value) {
if (TypeEnum[key] === value) {
return true;
}
}
@@ -64,7 +64,3 @@ export function TypeEnumToJSON(value?: TypeEnum | null): any {
return value as any;
}
export function TypeEnumToJSONTyped(value: any, ignoreDiscriminator: boolean): TypeEnum {
return value as TypeEnum;
}

View File

@@ -4,8 +4,6 @@ export * from './AccessToken';
export * from './AuthToken';
export * from './AutoMealPlan';
export * from './Automation';
export * from './AutomationTypeEnum';
export * from './BaseUnitEnum';
export * from './BookmarkletImport';
export * from './BookmarkletImportList';
export * from './ConnectorConfigConfig';
@@ -32,16 +30,6 @@ export * from './MealPlan';
export * from './MealType';
export * from './MethodEnum';
export * from './NutritionInformation';
export * from './OpenDataCategory';
export * from './OpenDataConversion';
export * from './OpenDataFood';
export * from './OpenDataFoodProperty';
export * from './OpenDataProperty';
export * from './OpenDataStore';
export * from './OpenDataStoreCategory';
export * from './OpenDataUnit';
export * from './OpenDataUnitTypeEnum';
export * from './OpenDataVersion';
export * from './PaginatedAutomationList';
export * from './PaginatedBookmarkletImportListList';
export * from './PaginatedCookLogList';
@@ -88,13 +76,6 @@ export * from './PatchedInviteLink';
export * from './PatchedKeyword';
export * from './PatchedMealPlan';
export * from './PatchedMealType';
export * from './PatchedOpenDataCategory';
export * from './PatchedOpenDataConversion';
export * from './PatchedOpenDataFood';
export * from './PatchedOpenDataProperty';
export * from './PatchedOpenDataStore';
export * from './PatchedOpenDataUnit';
export * from './PatchedOpenDataVersion';
export * from './PatchedProperty';
export * from './PatchedPropertyType';
export * from './PatchedRecipe';
@@ -154,6 +135,7 @@ export * from './SupermarketCategoryRelation';
export * from './Sync';
export * from './SyncLog';
export * from './ThemeEnum';
export * from './TypeEnum';
export * from './Unit';
export * from './UnitConversion';
export * from './User';

View File

@@ -115,19 +115,30 @@
<!-- ------------ -->
<v-stepper-window-item value="url">
<v-text-field :label="$t('Website') + ' (https://...)'" v-model="importUrl" v-if="importType == 'url'" :loading="loading"></v-text-field>
<v-text-field :label="$t('Website') + ' (https://...)'" v-model="importUrl" v-if="importType == 'url'" :loading="loading" autofocus
@keydown.enter="loadRecipeFromUrl({url: importUrl})"></v-text-field>
<v-file-input v-model="image" :label="$t('Image')" v-if="importType == 'ai'" :loading="loading"></v-file-input>
<v-textarea v-model="sourceImportText" label="JSON/HTML" :loading="loading" v-if="importType == 'source'" :hint="$t('SourceImportHelp')" persistent-hint></v-textarea>
<v-textarea v-model="sourceImportText" label="JSON/HTML" :loading="loading" v-if="importType == 'source'" :hint="$t('SourceImportHelp')"
persistent-hint autofocus @keydown.enter="loadRecipeFromUrl({data: sourceImportText})"></v-textarea>
<v-alert v-if="importResponse.error" :title="$t('Error')" :text="importResponse.msg" color="warning">
</v-alert>
<v-stepper-actions>
<template #prev>
<v-btn @click="stepper = 'type'">{{ $t('Back') }}</v-btn>
</template>
<template #next>
<v-btn @click="loadRecipeFromUrl({url: importUrl})" v-if="importType == 'url'" :disabled="importUrl == ''" :loading="loading">{{ $t('Load') }}</v-btn>
<v-btn @click="loadRecipeFromUrl({data: sourceImportText})" v-if="importType == 'source'" :disabled="sourceImportText == ''" :loading="loading">{{ $t('Load') }}</v-btn>
<v-btn @click="loadRecipeFromUrl({url: importUrl})" v-if="importType == 'url'" :disabled="importUrl == ''" :loading="loading">{{
$t('Load')
}}
</v-btn>
<v-btn @click="loadRecipeFromUrl({data: sourceImportText})" v-if="importType == 'source'" :disabled="sourceImportText == ''"
:loading="loading">{{ $t('Load') }}
</v-btn>
<v-btn @click="uploadAndConvertImage()" v-if="importType == 'ai'" :disabled="image == null" :loading="loading">{{ $t('Load') }}</v-btn>
</template>
</v-stepper-actions>
@@ -387,13 +398,15 @@
<!-- Bookmarklet -->
<!-- ------------ -->
<v-stepper-window-item value="bookmarklet">
{{$t('BookmarkletImportSubtitle')}}
{{ $t('BookmarkletImportSubtitle') }}
<ol>
<li>1. {{$t('BookmarkletHelp1')}}</li>
<li> <v-btn :href="bookmarkletContent" color="primary">{{$t('ImportIntoTandoor')}}</v-btn></li>
<li>2. {{$t('BookmarkletHelp2')}}</li>
<li>3. {{$t('BookmarkletHelp3')}}</li>
<li>1. {{ $t('BookmarkletHelp1') }}</li>
<li>
<v-btn :href="bookmarkletContent" color="primary">{{ $t('ImportIntoTandoor') }}</v-btn>
</li>
<li>2. {{ $t('BookmarkletHelp2') }}</li>
<li>3. {{ $t('BookmarkletHelp3') }}</li>
</ol>
<v-stepper-actions>
@@ -499,7 +512,14 @@ onMounted(() => {
function loadRecipeFromUrl(recipeFromSourceRequest: RecipeFromSource) {
let api = new ApiApi()
loading.value = true
importResponse.value = {} as RecipeFromSourceResponse
api.apiRecipeFromSourceCreate({recipeFromSource: recipeFromSourceRequest}).then(r => {
if (r.recipeId != null) {
router.push({name: 'RecipeViewPage', params: {id: r.recipeId}})
return
}
importResponse.value = r
if (importResponse.value.duplicates && importResponse.value.duplicates.length > 0) {
@@ -508,7 +528,13 @@ function loadRecipeFromUrl(recipeFromSourceRequest: RecipeFromSource) {
stepper.value = 'image_chooser'
}
}).catch(err => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
err.response.json().then(r => {
if (r.error) {
importResponse.value = r
} else {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, r)
}
})
}).finally(() => {
loading.value = false
})

View File

@@ -5,6 +5,7 @@ import {ApiApi, ServerSettings, Space, Supermarket, UserPreference, UserSpace} f
import {ShoppingGroupingOptions} from "@/types/Shopping";
import {computed, ComputedRef, ref} from "vue";
import {DeviceSettings} from "@/types/settings";
import {useTheme} from "vuetify";
const DEVICE_SETTINGS_KEY = 'TANDOOR_DEVICE_SETTINGS'
const USER_PREFERENCE_KEY = 'TANDOOR_USER_PREFERENCE'
@@ -43,6 +44,8 @@ export const useUserPreferenceStore = defineStore('user_preference_store', () =>
*/
let isAuthenticated = ref(false)
let theme = useTheme()
/**
* holds the active user space if there is one or null if not
*/
@@ -66,6 +69,7 @@ export const useUserPreferenceStore = defineStore('user_preference_store', () =>
if (r.length == 1) {
userSettings.value = r[0]
isAuthenticated.value = true
updateTheme()
} else {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, r)
}
@@ -84,6 +88,7 @@ export const useUserPreferenceStore = defineStore('user_preference_store', () =>
api.apiUserPreferencePartialUpdate({user: userSettings.value.user.id!, patchedUserPreference: userSettings.value}).then(r => {
userSettings.value = r
updateTheme()
useMessageStore().addPreparedMessage(PreparedMessage.UPDATE_SUCCESS)
}).catch(err => {
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
@@ -196,12 +201,24 @@ export const useUserPreferenceStore = defineStore('user_preference_store', () =>
}
}
/**
* applies user settings regarding themes/styling
*/
function updateTheme() {
if (userSettings.value.theme == 'TANDOOR') {
theme.global.name.value = 'light'
} else if (userSettings.value.theme == 'TANDOOR_DARK') {
theme.global.name.value = 'dark'
}
}
// always load settings on first initialization of store
loadUserSettings()
loadServerSettings()
loadActiveSpace()
loadUserSpaces()
loadSpaces()
updateTheme()
return {
deviceSettings,
@@ -216,7 +233,8 @@ export const useUserPreferenceStore = defineStore('user_preference_store', () =>
loadServerSettings,
updateUserSettings,
switchSpace,
resetDeviceSettings
resetDeviceSettings,
updateTheme,
}
})

View File

@@ -179,6 +179,72 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
"@napi-rs/canvas-android-arm64@0.1.68":
version "0.1.68"
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.68.tgz#505e788487776a146a45f1923fc38d01b2d676a2"
integrity sha512-h1KcSR4LKLfRfzeBH65xMxbWOGa1OtMFQbCMVlxPCkN1Zr+2gK+70pXO5ktojIYcUrP6KDcOwoc8clho5ccM/w==
"@napi-rs/canvas-darwin-arm64@0.1.68":
version "0.1.68"
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.68.tgz#525e389971ce66c53e27fef1457a4970ed5cc7f2"
integrity sha512-/VURlrAD4gDoxW1GT/b0nP3fRz/fhxmHI/xznTq2FTwkQLPOlLkDLCvTmQ7v6LtGKdc2Ed6rvYpRan+JXThInQ==
"@napi-rs/canvas-darwin-x64@0.1.68":
version "0.1.68"
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.68.tgz#97f91b6a6f2b765ee47e69edf68e822cfed649e7"
integrity sha512-tEpvGR6vCLTo1Tx9wmDnoOKROpw57wiCWwCpDOuVlj/7rqEJOUYr9ixW4aRJgmeGBrZHgevI0EURys2ER6whmg==
"@napi-rs/canvas-linux-arm-gnueabihf@0.1.68":
version "0.1.68"
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.68.tgz#28566dfe9e3728b31ff08031035bf534f9f2f8d9"
integrity sha512-U9xbJsumPOiAYeAFZMlHf62b9dGs2HJ6Q5xt7xTB0uEyPeurwhgYBWGgabdsEidyj38YuzI/c3LGBbSQB3vagw==
"@napi-rs/canvas-linux-arm64-gnu@0.1.68":
version "0.1.68"
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.68.tgz#80cc5356edb0e15f82bf44929f27b91e95be8aae"
integrity sha512-KFkn8wEm3mPnWD4l8+OUUkxylSJuN5q9PnJRZJgv15RtCA1bgxIwTkBhI/+xuyVMcHqON9sXq7cDkEJtHm35dg==
"@napi-rs/canvas-linux-arm64-musl@0.1.68":
version "0.1.68"
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.68.tgz#7f0d6dfa7508e37ba9d52867e6c6e1448dec238f"
integrity sha512-IQzts91rCdOALXBWQxLZRCEDrfFTGDtNRJMNu+2SKZ1uT8cmPQkPwVk5rycvFpvgAcmiFiOSCp1aRrlfU8KPpQ==
"@napi-rs/canvas-linux-riscv64-gnu@0.1.68":
version "0.1.68"
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.68.tgz#050ea7e7f6cb57bc8b8310235e066a0ffd50d385"
integrity sha512-e9AS5UttoIKqXSmBzKZdd3NErSVyOEYzJfNOCGtafGk1//gibTwQXGlSXmAKuErqMp09pyk9aqQRSYzm1AQfBw==
"@napi-rs/canvas-linux-x64-gnu@0.1.68":
version "0.1.68"
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.68.tgz#fa8361125da2a0a914a234a626b0f28b27332b8f"
integrity sha512-Pa/I36VE3j57I3Obhrr+J48KGFfkZk2cJN/2NmW/vCgmoF7kCP6aTVq5n+cGdGWLd/cN9CJ9JvNwEoMRDghu0g==
"@napi-rs/canvas-linux-x64-musl@0.1.68":
version "0.1.68"
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.68.tgz#a9eefc4e82189dca64f400c921b0387acf990f6f"
integrity sha512-9c6rkc5195wNxuUHJdf4/mmnq433OQey9TNvQ9LspJazvHbfSkTij8wtKjASVQsJyPDva4fkWOeV/OQ7cLw0GQ==
"@napi-rs/canvas-win32-x64-msvc@0.1.68":
version "0.1.68"
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.68.tgz#07e879f1c1759a31724871cc6202aba6a3b580f2"
integrity sha512-Fc5Dez23u0FoSATurT6/w1oMytiRnKWEinHivdMvXpge6nG4YvhrASrtqMk8dGJMVQpHr8QJYF45rOrx2YU2Aw==
"@napi-rs/canvas@^0.1.67":
version "0.1.68"
resolved "https://registry.yarnpkg.com/@napi-rs/canvas/-/canvas-0.1.68.tgz#265af2971aafcfeb60dbb591c7dc03c3f6e247f1"
integrity sha512-LQESrePLEBLvhuFkXx9jjBXRC2ClYsO5mqQ1m/puth5z9SOuM3N/B3vDuqnC3RJFktDktyK9khGvo7dTkqO9uQ==
optionalDependencies:
"@napi-rs/canvas-android-arm64" "0.1.68"
"@napi-rs/canvas-darwin-arm64" "0.1.68"
"@napi-rs/canvas-darwin-x64" "0.1.68"
"@napi-rs/canvas-linux-arm-gnueabihf" "0.1.68"
"@napi-rs/canvas-linux-arm64-gnu" "0.1.68"
"@napi-rs/canvas-linux-arm64-musl" "0.1.68"
"@napi-rs/canvas-linux-riscv64-gnu" "0.1.68"
"@napi-rs/canvas-linux-x64-gnu" "0.1.68"
"@napi-rs/canvas-linux-x64-musl" "0.1.68"
"@napi-rs/canvas-win32-x64-msvc" "0.1.68"
"@rollup/rollup-android-arm-eabi@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz#8b613b9725e8f9479d142970b106b6ae878610d5"
@@ -860,6 +926,13 @@ path-browserify@^1.0.1:
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
pdfjs-dist@^5.1.91:
version "5.1.91"
resolved "https://registry.yarnpkg.com/pdfjs-dist/-/pdfjs-dist-5.1.91.tgz#d5d90efacaf17d1c8d12310740eb7070322886d8"
integrity sha512-qSIADdagooJB4wWCBnrBJjRvASevmxL0BwafvOuKJG5uTQdYoFBrhrRYnucKNiSc9qS6JIk0hC5y1yktFljXkA==
optionalDependencies:
"@napi-rs/canvas" "^0.1.67"
perfect-debounce@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz#9c2e8bc30b169cc984a58b7d5b28049839591d2a"