├── .env.sample ├── .npmrc ├── .husky └── pre-commit ├── .mise.toml ├── cover.png ├── icon.png ├── .gitignore ├── src ├── i18n │ ├── main.ts │ ├── ui.ts │ ├── index.ts │ └── locales │ │ ├── ja.json │ │ └── en.json ├── types │ ├── i18n.d.ts │ ├── common.d.ts │ └── eventHandler.d.ts ├── ui │ ├── index.tsx │ ├── Store.ts │ ├── hooks │ │ ├── useNotionKeyValue.ts │ │ ├── useResizeWindow.ts │ │ ├── useCache.ts │ │ ├── useOptions.ts │ │ └── useNotion.ts │ ├── styles │ │ └── input.css │ ├── tabs │ │ ├── Settings.tsx │ │ ├── Utilities.tsx │ │ ├── Fetch.tsx │ │ └── List.tsx │ ├── App.tsx │ └── components │ │ ├── KeyValueList.tsx │ │ └── KeyValueRow.tsx ├── constants.ts └── main │ ├── applyKeyValue.ts │ ├── renameLayer.ts │ ├── applyValue.ts │ ├── highlightText.ts │ ├── index.ts │ └── util.ts ├── .editorconfig ├── .vscode └── settings.json ├── tsconfig.json ├── LICENSE ├── biome.json ├── tailwind.config.js ├── package.json ├── README.md └── pnpm-lock.yaml /.env.sample: -------------------------------------------------------------------------------- 1 | PROXY_URL="" 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | pnpm lint-staged 2 | -------------------------------------------------------------------------------- /.mise.toml: -------------------------------------------------------------------------------- 1 | [tools] 2 | node = "20.17.0" 3 | pnpm = "9.9.0" 4 | -------------------------------------------------------------------------------- /cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryonakae/figma-plugin-sync-notion/HEAD/cover.png -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryonakae/figma-plugin-sync-notion/HEAD/icon.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | *.css.d.ts 4 | /build/ 5 | /manifest.json 6 | node_modules/ 7 | 8 | .yarn/ 9 | src/ui/styles/output.css 10 | 11 | .env 12 | -------------------------------------------------------------------------------- /src/i18n/main.ts: -------------------------------------------------------------------------------- 1 | import i18n from 'i18next' 2 | 3 | import { initOptions } from '@/i18n' 4 | 5 | const i18nForMain = i18n.createInstance() 6 | i18nForMain.init(initOptions) 7 | 8 | export default i18nForMain 9 | -------------------------------------------------------------------------------- /src/types/i18n.d.ts: -------------------------------------------------------------------------------- 1 | import type { defaultNS, resources } from '@/ui/i18n' 2 | 3 | declare module 'i18next' { 4 | interface CustomTypeOptions { 5 | returnNull: false 6 | resources: (typeof resources)['en'] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /src/i18n/ui.ts: -------------------------------------------------------------------------------- 1 | import i18n from 'i18next' 2 | import { initReactI18next } from 'react-i18next' 3 | 4 | import { initOptions } from '@/i18n' 5 | 6 | const i18nForUI = i18n.createInstance() 7 | i18nForUI.use(initReactI18next).init(initOptions) 8 | 9 | export default i18nForUI 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "json.schemas": [ 3 | { 4 | "fileMatch": ["package.json"], 5 | "url": "https://yuanqing.github.io/create-figma-plugin/figma-plugin.json" 6 | } 7 | ], 8 | "path-intellisense.mappings": { 9 | "@": "${workspaceFolder}/src" 10 | }, 11 | "i18n-ally.localesPaths": ["src/i18n/locales"], 12 | "i18n-ally.keystyle": "nested" 13 | } 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@create-figma-plugin/tsconfig", 3 | "compilerOptions": { 4 | "typeRoots": ["node_modules/@figma", "node_modules/@types", "src/types"], 5 | "baseUrl": ".", 6 | "paths": { 7 | "@/*": ["src/*"] 8 | }, 9 | "skipLibCheck": true, 10 | "resolveJsonModule": true 11 | }, 12 | "include": ["src/**/*.ts", "src/**/*.tsx"] 13 | } 14 | -------------------------------------------------------------------------------- /src/ui/index.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx h */ 2 | import { h } from 'preact' 3 | 4 | import { render } from '@create-figma-plugin/ui' 5 | import { I18nextProvider } from 'react-i18next' 6 | 7 | import i18n from '@/i18n/ui' 8 | import App from '@/ui/App' 9 | import '!./styles/output.css' 10 | 11 | function Plugin() { 12 | return ( 13 | 14 | 15 | 16 | ) 17 | } 18 | 19 | export default render(Plugin) 20 | -------------------------------------------------------------------------------- /src/ui/Store.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand' 2 | 3 | import { 4 | DEFAULT_CLIENT_STORAGE_OPTIONS, 5 | DEFAULT_DOCUMENT_OPTIONS, 6 | } from '@/constants' 7 | import type { NotionKeyValue, Options } from '@/types/common' 8 | 9 | const defaultOptions: Options = { 10 | ...DEFAULT_DOCUMENT_OPTIONS, 11 | ...DEFAULT_CLIENT_STORAGE_OPTIONS, 12 | } 13 | export const useStore = create(set => defaultOptions) 14 | 15 | export const useKeyValuesStore = create<{ keyValues: NotionKeyValue[] }>( 16 | set => ({ 17 | keyValues: [], 18 | }), 19 | ) 20 | -------------------------------------------------------------------------------- /src/i18n/index.ts: -------------------------------------------------------------------------------- 1 | import type { InitOptions } from 'i18next' 2 | 3 | import enJson from '@/i18n/locales/en.json' 4 | import jaJson from '@/i18n/locales/ja.json' 5 | 6 | export const defaultNS = 'translation' 7 | 8 | export const resources = { 9 | en: { 10 | [defaultNS]: enJson, 11 | }, 12 | ja: { 13 | [defaultNS]: jaJson, 14 | }, 15 | } as const 16 | 17 | export const initOptions: InitOptions = { 18 | debug: true, 19 | lng: 'en', 20 | defaultNS, 21 | resources, 22 | interpolation: { 23 | escapeValue: false, 24 | }, 25 | compatibilityJSON: 'v3', 26 | } 27 | -------------------------------------------------------------------------------- /src/ui/hooks/useNotionKeyValue.ts: -------------------------------------------------------------------------------- 1 | import type { NotionKeyValue } from '@/types/common' 2 | 3 | export default function useNotionKeyValue() { 4 | function getKeyWithQueryStrings(keyValue: NotionKeyValue) { 5 | // 波括弧で囲まれたパラメータを抽出 6 | const params = 7 | keyValue.value.match(/\{([^}]+)\}/g)?.map(param => param.slice(1, -1)) || 8 | [] 9 | 10 | // パラメータが存在する場合、クエリ文字列を生成 11 | if (params.length > 0) { 12 | const queryString = params.map(param => `${param}=${param}`).join('&') 13 | return `#${keyValue.key}?${queryString}` 14 | } 15 | 16 | // パラメータが存在しない場合、キーのみを返す 17 | return `#${keyValue.key}` 18 | } 19 | 20 | return { getKeyWithQueryStrings } 21 | } 22 | -------------------------------------------------------------------------------- /src/ui/styles/input.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=block'); 2 | 3 | @tailwind base; 4 | @tailwind components; 5 | @tailwind utilities; 6 | 7 | @layer components { 8 | .icon { 9 | @apply font-icon font-normal not-italic text-base leading-none tracking-normal normal-case 10 | inline-block flex-shrink-0 self-center 11 | whitespace-nowrap antialiased; 12 | word-wrap: normal; 13 | direction: ltr; 14 | -webkit-font-feature-settings: 'liga'; 15 | font-variation-settings: 16 | 'FILL' 0, 17 | 'wght' 300, 18 | 'GRAD' 0, 19 | 'opsz' 24; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/ui/hooks/useResizeWindow.ts: -------------------------------------------------------------------------------- 1 | import { emit } from '@create-figma-plugin/utilities' 2 | 3 | import { DEFAULT_WIDTH } from '@/constants' 4 | 5 | import type { ResizeWindowHandler } from '@/types/eventHandler' 6 | 7 | export default function useResizeWindow() { 8 | function resizeWindow(options?: { 9 | width?: number 10 | delay?: number 11 | }) { 12 | window.setTimeout(() => { 13 | const wrapper = document.getElementById('wrapper') 14 | const height = wrapper?.clientHeight || 0 15 | 16 | console.log('resizeWindow', options, wrapper, height) 17 | 18 | emit('RESIZE_WINDOW', { 19 | width: options?.width || DEFAULT_WIDTH, 20 | height, 21 | }) 22 | }, options?.delay || 16) 23 | } 24 | 25 | return { resizeWindow } 26 | } 27 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | import type { ClientStorageOptions, DocumentOptions } from '@/types/common' 2 | 3 | export const SETTINGS_KEY = 'sync-notion' 4 | export const CACHE_KEY = 'sync-notion-cache' 5 | export const GROUP_ID_KEY = 'sync-notion-group-id' 6 | 7 | export const DEFAULT_WIDTH = 400 8 | 9 | export const DEFAULT_DOCUMENT_OPTIONS: DocumentOptions = { 10 | databaseId: '', 11 | integrationToken: '', 12 | keyPropertyName: '', 13 | valuePropertyName: '', 14 | } 15 | 16 | export const DEFAULT_CLIENT_STORAGE_OPTIONS: ClientStorageOptions = { 17 | // common 18 | selectedTabKey: 'fetch', 19 | // list 20 | filterString: '', 21 | sortValue: 'created_time', 22 | sortOrder: 'descending', 23 | selectedRowId: null, 24 | scrollPosition: 0, 25 | // utilities 26 | targetTextRange: 'selection', 27 | includeComponents: true, 28 | includeInstances: false, 29 | // settings 30 | pluginLanguage: 'en', 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ryo Nakae 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/ui/hooks/useCache.ts: -------------------------------------------------------------------------------- 1 | import { emit, once } from '@create-figma-plugin/utilities' 2 | 3 | import { useKeyValuesStore } from '@/ui/Store' 4 | 5 | import type { NotionKeyValue } from '@/types/common' 6 | import type { 7 | LoadCacheFromMainHandler, 8 | LoadCacheFromUIHandler, 9 | SaveCacheHandler, 10 | } from '@/types/eventHandler' 11 | 12 | export default function useCache() { 13 | function loadCacheFromClientStorage() { 14 | return new Promise(resolve => { 15 | console.log('loadCacheFromClientStorage') 16 | 17 | once('LOAD_CACHE_FROM_MAIN', keyValues => { 18 | console.log('cached keyValues', keyValues) 19 | useKeyValuesStore.setState({ keyValues }) 20 | resolve(keyValues) 21 | }) 22 | 23 | emit('LOAD_CACHE_FROM_UI') 24 | }) 25 | } 26 | 27 | function saveCacheToClientStorage(keyValues: NotionKeyValue[]) { 28 | console.log('saveCacheToClientStorage', keyValues) 29 | emit('SAVE_CACHE', keyValues) 30 | } 31 | 32 | return { loadCacheFromClientStorage, saveCacheToClientStorage } 33 | } 34 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", 3 | "organizeImports": { 4 | "enabled": true 5 | }, 6 | "linter": { 7 | "enabled": true, 8 | "rules": { 9 | "recommended": true, 10 | "a11y": { 11 | "useValidAnchor": "warn", 12 | "useKeyWithClickEvents": "warn" 13 | }, 14 | "complexity": { 15 | "noForEach": "off" 16 | }, 17 | "correctness": { 18 | "useExhaustiveDependencies": "warn" 19 | }, 20 | "style": { 21 | "noNonNullAssertion": "warn", 22 | "useConst": "warn" 23 | }, 24 | "suspicious": { 25 | "noArrayIndexKey": "warn", 26 | "noExplicitAny": "warn" 27 | } 28 | } 29 | }, 30 | "formatter": { 31 | "enabled": true, 32 | "formatWithErrors": true, 33 | "indentStyle": "space", 34 | "indentWidth": 2 35 | }, 36 | "javascript": { 37 | "parser": { 38 | "unsafeParameterDecoratorsEnabled": true 39 | }, 40 | "formatter": { 41 | "enabled": true, 42 | "quoteStyle": "single", 43 | "trailingCommas": "all", 44 | "semicolons": "asNeeded", 45 | "arrowParentheses": "asNeeded", 46 | "quoteProperties": "asNeeded" 47 | } 48 | }, 49 | "vcs": { 50 | "enabled": true, 51 | "clientKind": "git", 52 | "useIgnoreFile": true 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/ui/hooks/useOptions.ts: -------------------------------------------------------------------------------- 1 | import { emit, once } from '@create-figma-plugin/utilities' 2 | 3 | import { useStore } from '@/ui/Store' 4 | 5 | import type { Options } from '@/types/common' 6 | import type { 7 | LoadOptionsFromMainHandler, 8 | LoadOptionsFromUIHandler, 9 | SaveOptionsHandler, 10 | } from '@/types/eventHandler' 11 | 12 | export default function useOptions(isApp?: boolean) { 13 | function updateOptions(keyValue: { [T in keyof Options]?: Options[T] }) { 14 | console.log('updateOptions', { 15 | ...useStore.getState(), 16 | ...keyValue, 17 | }) 18 | 19 | // Storeを更新 20 | useStore.setState({ ...useStore.getState(), ...keyValue }) 21 | } 22 | 23 | function loadOptionsFromMain() { 24 | return new Promise(resolve => { 25 | console.log('loadOptionsFromMain') 26 | 27 | once( 28 | 'LOAD_OPTIONS_FROM_MAIN', 29 | (options: Options) => { 30 | updateOptions(options) 31 | resolve(options) 32 | }, 33 | ) 34 | 35 | emit('LOAD_OPTIONS_FROM_UI') 36 | }) 37 | } 38 | 39 | function saveOptionsToMain(options: Options) { 40 | console.log('saveOptionsToMain', options) 41 | emit('SAVE_OPTIONS', options) 42 | } 43 | 44 | return { 45 | updateOptions, 46 | loadOptionsFromMain, 47 | saveOptionsToMain, 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ['./src/**/*.{ts,tsx}'], 4 | theme: { 5 | extend: { 6 | fontFamily: { 7 | icon: 'Material Symbols Outlined', 8 | }, 9 | fontSize: { 10 | 11: 'var(--font-size-11)', 11 | 12: 'var(--font-size-12)', 12 | }, 13 | textColor: { 14 | primary: 'var(--figma-color-text)', 15 | secondary: 'var(--figma-color-text-secondary)', 16 | link: 'var(--figma-color-text-brand)', 17 | }, 18 | backgroundColor: { 19 | primary: 'var(--figma-color-bg)', 20 | secondary: 'var(--figma-color-bg-secondary)', 21 | tertiary: 'var(--figma-color-bg-tertiary)', 22 | selected: 'var(--figma-color-bg-selected)', 23 | selectedSecondary: 'var(--figma-color-bg-selected-secondary)', 24 | selectedTertiary: 'var(--figma-color-bg-selected-tertiary)', 25 | hover: 'var(--figma-color-bg-hover)', 26 | }, 27 | borderColor: { 28 | primary: 'var(--figma-color-border)', 29 | }, 30 | borderRadius: { 31 | 2: 'var(--border-radius-2)', 32 | 6: 'var(--border-radius-6)', 33 | }, 34 | height: { 35 | 500: '500px', 36 | }, 37 | spacing: { 38 | '0_5': '2px', 39 | '1_5': '6px', 40 | }, 41 | }, 42 | }, 43 | plugins: [], 44 | darkMode: ['class', '.figma-dark'], 45 | } 46 | -------------------------------------------------------------------------------- /src/main/applyKeyValue.ts: -------------------------------------------------------------------------------- 1 | import { loadFontsAsync } from '@create-figma-plugin/utilities' 2 | 3 | import i18n from '@/i18n/main' 4 | 5 | import type { NotionKeyValue } from '@/types/common' 6 | 7 | export default async function applyKeyValue(keyValue: NotionKeyValue) { 8 | console.log('applyKeyValue', keyValue) 9 | 10 | // 何も選択していない場合は処理を終了 11 | if (figma.currentPage.selection.length === 0) { 12 | figma.notify(i18n.t('notifications.main.noSelections')) 13 | return 14 | } 15 | 16 | // textNodeを格納する配列を用意 17 | const textNodes: TextNode[] = [] 18 | 19 | // 選択要素ごとに処理を実行 20 | figma.currentPage.selection.forEach(node => { 21 | // 要素がテキストの場合、textNodesに追加 22 | if (node.type === 'TEXT') { 23 | textNodes.push(node) 24 | } 25 | }) 26 | 27 | console.log('textNodes', textNodes) 28 | 29 | // textNodeが1つも無かったら処理を中断 30 | if (textNodes.length === 0) { 31 | figma.notify(i18n.t('notifications.main.noTextInSelection')) 32 | return 33 | } 34 | 35 | // 事前にフォントをロード 36 | await loadFontsAsync(textNodes).catch((error: Error) => { 37 | const errorMessage = i18n.t('notifications.main.errorLoadFonts') 38 | figma.notify(errorMessage, { error: true }) 39 | throw new Error(errorMessage) 40 | }) 41 | 42 | // textNodeごとに処理を実行 43 | textNodes.forEach(textNode => { 44 | // レイヤー名をkeyPropertyにする 45 | // UI側で`#${keyValue.key}?${queryString}`に加工したもの 46 | textNode.name = keyValue.key 47 | 48 | // テキスト本文をvaluePropertyにする 49 | textNode.characters = keyValue.value 50 | }) 51 | 52 | // 完了通知 53 | figma.notify(i18n.t('notifications.applyKeyValue.finish')) 54 | } 55 | -------------------------------------------------------------------------------- /src/types/common.d.ts: -------------------------------------------------------------------------------- 1 | export type SelectedTabKey = 'fetch' | 'list' | 'utilities' | 'settings' 2 | export type SelectedTabValue = 'Fetch' | 'List' | 'Utilities' | 'Settings' 3 | 4 | export type SortValue = 'key' | 'value' | 'created_time' | 'last_edited_time' 5 | 6 | export type SortOrder = 'ascending' | 'descending' 7 | 8 | export type TargetTextRange = 'selection' | 'currentPage' | 'allPages' 9 | 10 | export type PluginLanguage = 'en' | 'ja' 11 | 12 | export type DocumentOptions = { 13 | // fetch 14 | databaseId: string 15 | integrationToken: string 16 | keyPropertyName: string 17 | valuePropertyName: string 18 | } 19 | 20 | export type ClientStorageOptions = { 21 | // common 22 | selectedTabKey: SelectedTabKey 23 | // list 24 | filterString: string 25 | sortValue: SortValue 26 | sortOrder: SortOrder 27 | selectedRowId: string | null 28 | scrollPosition: number 29 | // utilities 30 | targetTextRange: TargetTextRange 31 | includeComponents: boolean 32 | includeInstances: boolean 33 | // settings 34 | pluginLanguage: PluginLanguage 35 | } 36 | 37 | export type Options = DocumentOptions & ClientStorageOptions 38 | 39 | export type NotionTitle = { 40 | type: 'title' 41 | title: { plain_text: string }[] 42 | } 43 | 44 | export type NotionFomula = { 45 | type: 'formula' 46 | formula: { 47 | string: string 48 | } 49 | } 50 | 51 | export type NotionRichText = { 52 | type: 'rich_text' 53 | rich_text: { plain_text: string }[] 54 | } 55 | 56 | export type NotionPage = { 57 | object: 'page' 58 | id: string 59 | properties: { 60 | [key: string]: NotionTitle | NotionFomula | NotionRichText 61 | } 62 | created_time: string 63 | last_edited_time: string 64 | url: string 65 | } 66 | 67 | export type NotionKeyValue = { 68 | id: string 69 | key: string 70 | value: string 71 | created_time: string 72 | last_edited_time: string 73 | url: string 74 | } 75 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "figma-plugin-sync-notion", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "author": "Ryo Nakae", 6 | "scripts": { 7 | "build": "NODE_ENV=production concurrently -P 'npm:build:* -- {*}' -- --minify", 8 | "build:css": "tailwindcss --input ./src/ui/styles/input.css --output ./src/ui/styles/output.css", 9 | "build:js": "build-figma-plugin --typecheck", 10 | "check": "biome check", 11 | "prepare": "husky || true", 12 | "watch": "npm run build:css && concurrently -P 'npm:build:* -- {*}' -- --watch" 13 | }, 14 | "lint-staged": { 15 | "*.{js,jsx,ts,tsx,cjs,mjs,json}": ["biome check --apply"] 16 | }, 17 | "dependencies": { 18 | "@create-figma-plugin/ui": "3.2.0", 19 | "@create-figma-plugin/utilities": "3.2.0", 20 | "clsx": "2.1.1", 21 | "i18next": "23.14.0", 22 | "lodash": "4.17.21", 23 | "preact": "10.23.2", 24 | "query-string": "9.1.0", 25 | "react-i18next": "15.0.1", 26 | "react-use": "17.5.1", 27 | "zustand": "4.5.5" 28 | }, 29 | "devDependencies": { 30 | "@biomejs/biome": "1.8.3", 31 | "@create-figma-plugin/build": "3.2.0", 32 | "@create-figma-plugin/tsconfig": "3.2.0", 33 | "@figma/plugin-typings": "1.98.0", 34 | "@types/lodash": "4.17.7", 35 | "@types/node": "22.5.0", 36 | "concurrently": "8.2.2", 37 | "dotenv": "16.4.5", 38 | "husky": "9.1.5", 39 | "lint-staged": "15.2.9", 40 | "tailwindcss": "3.4.10", 41 | "typescript": "5.5.4" 42 | }, 43 | "figma-plugin": { 44 | "editorType": ["figma"], 45 | "id": "1116202373222780315", 46 | "name": "Sync Text with Notion", 47 | "main": "src/main/index.ts", 48 | "ui": "src/ui/index.tsx", 49 | "relaunchButtons": { 50 | "open": { 51 | "name": "Sync Text with Notion", 52 | "main": "src/main/index.ts", 53 | "ui": "src/ui/index.tsx" 54 | } 55 | }, 56 | "documentAccess": "dynamic-page", 57 | "networkAccess": { 58 | "allowedDomains": [ 59 | "https://fonts.googleapis.com", 60 | "https://fonts.gstatic.com", 61 | "https://*.workers.dev" 62 | ] 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/renameLayer.ts: -------------------------------------------------------------------------------- 1 | import { loadFontsAsync } from '@create-figma-plugin/utilities' 2 | 3 | import i18n from '@/i18n/main' 4 | import { getTextNodes } from '@/main/util' 5 | 6 | import type { NotionKeyValue, TargetTextRange } from '@/types/common' 7 | 8 | export default async function renameLayer( 9 | keyValues: NotionKeyValue[], 10 | options: { 11 | targetTextRange: TargetTextRange 12 | includeComponents: boolean 13 | includeInstances: boolean 14 | }, 15 | ) { 16 | console.log('renameLayer', keyValues, options) 17 | 18 | // textNodeを取得 19 | const textNodes = await getTextNodes(options) 20 | 21 | console.log('textNodes', textNodes) 22 | 23 | // textNodeが1つもなかったら処理を終了 24 | if (textNodes.length === 0) { 25 | // 選択状態によってトーストを出し分け 26 | if (options.targetTextRange === 'selection') { 27 | figma.notify(i18n.t('notifications.main.noTextInSelection')) 28 | } else if (options.targetTextRange === 'currentPage') { 29 | figma.notify(i18n.t('notifications.main.noTextInCurrentPage')) 30 | } else if (options.targetTextRange === 'allPages') { 31 | figma.notify(i18n.t('notifications.main.noTextInAllPages')) 32 | } 33 | 34 | return 35 | } 36 | 37 | // 事前にフォントをロード 38 | await loadFontsAsync(textNodes).catch((error: Error) => { 39 | const errorMessage = i18n.t('notifications.main.errorLoadFonts') 40 | figma.notify(errorMessage, { error: true }) 41 | throw new Error(errorMessage) 42 | }) 43 | 44 | // keyValuesのvalueをキーとするマップを作成 45 | const valueKeyMap = new Map( 46 | keyValues.map(keyValue => [keyValue.value, keyValue]), 47 | ) 48 | 49 | // textNodesごとに処理を実行 50 | textNodes.forEach(textNode => { 51 | // keyValuesからvalueがtextNode.charactersと一致するものを探す 52 | const matchedKeyValue = valueKeyMap.get(textNode.characters) 53 | 54 | // matchedKeyValueがある場合、keyをtextNodeのレイヤー名にする 55 | if (matchedKeyValue) { 56 | console.log('matchedKeyValue exist', textNode.characters, matchedKeyValue) 57 | textNode.name = `#${matchedKeyValue.key}` 58 | } else { 59 | console.log('no matchedKeyValue', textNode.characters) 60 | } 61 | }) 62 | 63 | // 完了通知 64 | figma.notify(i18n.t('notifications.renameLayer.finish')) 65 | } 66 | -------------------------------------------------------------------------------- /src/types/eventHandler.d.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | NotionKeyValue, 3 | Options, 4 | PluginLanguage, 5 | TargetTextRange, 6 | } from '@/types/common' 7 | import type { EventHandler } from '@create-figma-plugin/utilities' 8 | 9 | interface LoadOptionsFromUIHandler extends EventHandler { 10 | name: 'LOAD_OPTIONS_FROM_UI' 11 | handler: () => void 12 | } 13 | 14 | interface LoadOptionsFromMainHandler extends EventHandler { 15 | name: 'LOAD_OPTIONS_FROM_MAIN' 16 | handler: (options: Options) => void 17 | } 18 | 19 | interface SaveOptionsHandler extends EventHandler { 20 | name: 'SAVE_OPTIONS' 21 | handler: (options: Options) => void 22 | } 23 | 24 | interface NotifyHandler extends EventHandler { 25 | name: 'NOTIFY' 26 | handler: (options: { message: string; options?: NotificationOptions }) => void 27 | } 28 | 29 | interface ResizeWindowHandler extends EventHandler { 30 | name: 'RESIZE_WINDOW' 31 | handler: (windowSize: { width: number; height: number }) => void 32 | } 33 | 34 | interface LoadCacheFromUIHandler extends EventHandler { 35 | name: 'LOAD_CACHE_FROM_UI' 36 | handler: () => void 37 | } 38 | 39 | interface LoadCacheFromMainHandler extends EventHandler { 40 | name: 'LOAD_CACHE_FROM_MAIN' 41 | handler: (keyValues: NotionKeyValue[]) => void 42 | } 43 | 44 | interface SaveCacheHandler extends EventHandler { 45 | name: 'SAVE_CACHE' 46 | handler: (keyValues: NotionKeyValue[]) => void 47 | } 48 | 49 | interface ApplyKeyValueHandler extends EventHandler { 50 | name: 'APPLY_KEY_VALUE' 51 | handler: (keyValue: NotionKeyValue) => void 52 | } 53 | 54 | interface ApplyValueHandler extends EventHandler { 55 | name: 'APPLY_VALUE' 56 | handler: ( 57 | keyValues: NotionKeyValue[], 58 | options: { 59 | targetTextRange: TargetTextRange 60 | includeComponents: boolean 61 | includeInstances: boolean 62 | }, 63 | ) => void 64 | } 65 | 66 | interface RenameLayerHandler extends EventHandler { 67 | name: 'RENAME_LAYER' 68 | handler: ( 69 | keyValues: NotionKeyValue[], 70 | options: { 71 | targetTextRange: TargetTextRange 72 | includeComponents: boolean 73 | includeInstances: boolean 74 | }, 75 | ) => void 76 | } 77 | 78 | interface HighlightTextHandler extends EventHandler { 79 | name: 'HIGHLIGHT_TEXT' 80 | handler: ( 81 | keyValues: NotionKeyValue[], 82 | options: { 83 | targetTextRange: TargetTextRange 84 | includeComponents: boolean 85 | includeInstances: boolean 86 | }, 87 | ) => void 88 | } 89 | 90 | interface ChangeLanguageHandler extends EventHandler { 91 | name: 'CHANGE_LANGUAGE' 92 | handler: (language: PluginLanguage, options?: { notify?: boolean }) => void 93 | } 94 | -------------------------------------------------------------------------------- /src/ui/tabs/Settings.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx h */ 2 | import { type JSX, h } from 'preact' 3 | 4 | import { 5 | Button, 6 | Container, 7 | Dropdown, 8 | type DropdownOption, 9 | Stack, 10 | VerticalSpace, 11 | } from '@create-figma-plugin/ui' 12 | import { emit } from '@create-figma-plugin/utilities' 13 | import { useTranslation } from 'react-i18next' 14 | import { useMount, useUnmount } from 'react-use' 15 | 16 | import { useStore } from '@/ui/Store' 17 | import useOptions from '@/ui/hooks/useOptions' 18 | import useResizeWindow from '@/ui/hooks/useResizeWindow' 19 | 20 | import type { PluginLanguage } from '@/types/common' 21 | import type { ChangeLanguageHandler, NotifyHandler } from '@/types/eventHandler' 22 | 23 | export default function Settings() { 24 | const { t } = useTranslation() 25 | const options = useStore() 26 | const { updateOptions } = useOptions() 27 | const { resizeWindow } = useResizeWindow() 28 | 29 | const languageDropdownOptions: DropdownOption[] & 30 | { 31 | value?: PluginLanguage 32 | }[] = [ 33 | { 34 | text: t('Settings.language.options.english'), 35 | value: 'en', 36 | }, 37 | { 38 | text: t('Settings.language.options.japanese'), 39 | value: 'ja', 40 | }, 41 | ] 42 | 43 | async function handleLanguageDropdownChange( 44 | event: JSX.TargetedEvent, 45 | ) { 46 | const newLanguage = event.currentTarget.value as PluginLanguage 47 | 48 | // pluginLanguageをアップデート 49 | updateOptions({ 50 | pluginLanguage: newLanguage, 51 | }) 52 | 53 | // 言語切替 54 | emit('CHANGE_LANGUAGE', newLanguage, { 55 | notify: true, 56 | }) 57 | } 58 | 59 | useMount(() => { 60 | console.log('Settings mounted') 61 | resizeWindow() 62 | }) 63 | 64 | useUnmount(() => { 65 | console.log('Settings unmounted') 66 | }) 67 | 68 | return ( 69 | 70 | 71 | 72 |
73 | {t('Settings.language.label')} 74 | 80 |
81 | 82 | 83 | 84 | 85 | 98 | 99 | 112 | 113 | 114 | 115 |
116 | ) 117 | } 118 | -------------------------------------------------------------------------------- /src/ui/App.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx h */ 2 | import { type JSX, h } from 'preact' 3 | import { useState } from 'preact/hooks' 4 | 5 | import { Tabs, type TabsOption } from '@create-figma-plugin/ui' 6 | import { emit } from '@create-figma-plugin/utilities' 7 | import { useTranslation } from 'react-i18next' 8 | import { useMount, useUnmount, useUpdateEffect } from 'react-use' 9 | 10 | import { useStore } from '@/ui/Store' 11 | import useCache from '@/ui/hooks/useCache' 12 | import useOptions from '@/ui/hooks/useOptions' 13 | import useResizeWindow from '@/ui/hooks/useResizeWindow' 14 | import Fetch from '@/ui/tabs/Fetch' 15 | import List from '@/ui/tabs/List' 16 | import Settings from '@/ui/tabs/Settings' 17 | import Utilities from '@/ui/tabs/Utilities' 18 | 19 | import type { SelectedTabKey, SelectedTabValue } from '@/types/common' 20 | import type { ChangeLanguageHandler } from '@/types/eventHandler' 21 | 22 | export default function App() { 23 | const { t, i18n } = useTranslation() 24 | const options = useStore() 25 | const { updateOptions, loadOptionsFromMain, saveOptionsToMain } = useOptions() 26 | const { resizeWindow } = useResizeWindow() 27 | const { loadCacheFromClientStorage } = useCache() 28 | const [mounted, setMounted] = useState(false) 29 | const [selectedTabValue, setSelectedTabValue] = 30 | useState('Fetch') 31 | 32 | const tabOptions: TabsOption[] & 33 | { 34 | value: SelectedTabValue 35 | }[] = [ 36 | { 37 | children: , 38 | value: t('Tabs.fetch'), 39 | }, 40 | { 41 | children: , 42 | value: t('Tabs.list'), 43 | }, 44 | { 45 | children: , 46 | value: t('Tabs.utilities'), 47 | }, 48 | { 49 | children: , 50 | value: t('Tabs.settings'), 51 | }, 52 | ] 53 | 54 | function handleTabChange(event: JSX.TargetedEvent) { 55 | const newTabValue = event.currentTarget.value as SelectedTabValue 56 | let newTabKey: SelectedTabKey = 'fetch' 57 | 58 | if (newTabValue === t('Tabs.fetch')) { 59 | newTabKey = 'fetch' 60 | } else if (newTabValue === t('Tabs.list')) { 61 | newTabKey = 'list' 62 | } else if (newTabValue === t('Tabs.utilities')) { 63 | newTabKey = 'utilities' 64 | } else if (newTabValue === t('Tabs.settings')) { 65 | newTabKey = 'settings' 66 | } 67 | 68 | updateOptions({ 69 | selectedTabKey: newTabKey, 70 | }) 71 | } 72 | 73 | useMount(async () => { 74 | console.log('App mounted start') 75 | 76 | // 設定をclientStorageから取得 77 | await loadOptionsFromMain() 78 | 79 | // keyValuesのキャッシュをclientStorageから取得 80 | await loadCacheFromClientStorage() 81 | 82 | // マウント完了 83 | console.log('App mounted done') 84 | setMounted(true) 85 | 86 | resizeWindow() 87 | }) 88 | 89 | useUnmount(() => { 90 | console.log('App unmounted') 91 | }) 92 | 93 | // UI側で設定が更新されたら設定をアップデート 94 | useUpdateEffect(() => { 95 | saveOptionsToMain(options) 96 | }, [options]) 97 | 98 | // selectedTab(key)がアップデートされたらselectedTabValueをアップデート 99 | useUpdateEffect(() => { 100 | const translatedTabValue = t(`Tabs.${options.selectedTabKey}` as const) 101 | setSelectedTabValue(translatedTabValue as SelectedTabValue) 102 | }, [options.selectedTabKey]) 103 | 104 | // options.pluginLanguageが変更されたら言語を切り替え 105 | useUpdateEffect(async () => { 106 | console.log('pluginLanguage update on App', options.pluginLanguage) 107 | 108 | // UI側の言語を切り替え 109 | await i18n.changeLanguage(options.pluginLanguage) 110 | 111 | // main側の言語も切り替える 112 | emit('CHANGE_LANGUAGE', options.pluginLanguage) 113 | 114 | // selectedTabValueを言語に合わせてアップデート 115 | const translatedTabValue = t(`Tabs.${options.selectedTabKey}` as const) 116 | setSelectedTabValue(translatedTabValue as SelectedTabValue) 117 | }, [options.pluginLanguage]) 118 | 119 | if (!mounted) { 120 | return null 121 | } 122 | 123 | return ( 124 |
125 | 130 |
131 | ) 132 | } 133 | -------------------------------------------------------------------------------- /src/i18n/locales/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tabs": { 3 | "fetch": "取得", 4 | "list": "リスト", 5 | "utilities": "ユーティリティ", 6 | "settings": "設定" 7 | }, 8 | "Fetch": { 9 | "databaseId": "データベースID", 10 | "integrationToken": "インテグレーショントークン", 11 | "keyPropertyName": "キーのプロパティ名", 12 | "valuePropertyName": "値のプロパティ名", 13 | "fetchButton": { 14 | "label": "Notionからテキストを取得", 15 | "description": "Notionのデータベースからテキストを取得します。データはこのドキュメントにキャッシュされ、次回起動時に復元されます。Notionデータベースを更新した場合は、このボタンを再度クリックしてください" 16 | }, 17 | "clearCacheButton": "{{length}}項目のキャッシュを削除" 18 | }, 19 | "List": { 20 | "filter": { 21 | "placeholder": "キーまたは値で絞り込む", 22 | "clearButton": "消去" 23 | }, 24 | "sortValue": { 25 | "key": "キー", 26 | "value": "値", 27 | "created_time": "作成日時 (デフォルト)", 28 | "last_edited_time": "最終編集日時" 29 | }, 30 | "sortOrder": { 31 | "ascending": "昇順", 32 | "descending": "降順 (デフォルト)" 33 | }, 34 | "statusBar": { 35 | "text": "行をクリックしてテキストにキーと値を適用またはコピー", 36 | "count": "{{length}}項目" 37 | }, 38 | "empty": { 39 | "text": "項目がありません", 40 | "fetchButton": "Notionからテキストを取得" 41 | } 42 | }, 43 | "Utilities": { 44 | "targetTextRange": { 45 | "label": "対象テキストの範囲", 46 | "options": { 47 | "selection": "選択した要素", 48 | "currentPage": "現在のページ", 49 | "allPages": "すべてのページ" 50 | } 51 | }, 52 | "includeComponents": "コンポーネント内のテキストを含める", 53 | "includeInstances": "インスタンス内のテキストを含める", 54 | "applyValueButton": { 55 | "label": "テキストに値を適用", 56 | "description": "テキストレイヤーの名前が#<キー>になっている場合(例: #userInfo)、テキストの内容を置き換えます。値に変数が埋め込まれている場合、レイヤー名にパラメータを渡すことができます(例: #userInfo?name=John&age=24)。値が「Name: {name} / Age: {age}」の場合、テキストの内容は「Name: Jogn / Age: 24」となります。" 57 | }, 58 | "renameLayerButton": { 59 | "label": "レイヤー名を変更", 60 | "description": "テキストの内容がデータベースの値と一致している場合、レイヤー名を#<キー>に変更します。レイヤーのリネームが面倒な場合に便利です。" 61 | }, 62 | "highlightTextButton": { 63 | "label": "テキストをハイライト", 64 | "description": "テキストレイヤーの名前が#<キー>になっているものをハイライトします。キーがデータベースに存在する場合は青色、存在しない場合またはキーが間違っている場合は赤色でハイライトされます。レイヤー名が正しくフォーマットされているか、またはタイプミスがないかを確認するのに便利です。" 65 | } 66 | }, 67 | "Settings": { 68 | "language": { 69 | "label": "言語", 70 | "options": { 71 | "english": "英語 (デフォルト)", 72 | "japanese": "日本語" 73 | } 74 | }, 75 | "buttons": { 76 | "pluginPage": "プラグインページ", 77 | "github": "GitHub" 78 | } 79 | }, 80 | "KeyValueList": { 81 | "empty": "項目がありません", 82 | "loading": "読み込み中..." 83 | }, 84 | "KeyValueRow": { 85 | "key": "キー", 86 | "value": "値", 87 | "applyKeyValueButton": "選択したテキストにキーと値を適用", 88 | "openBrowserButton": "ブラウザで開く" 89 | }, 90 | "notifications": { 91 | "Fetch": { 92 | "loading": "しばらくお待ちください", 93 | "error": { 94 | "failedFetch": "データベースの取得に失敗しました", 95 | "noPages": "このデータベースにページがありません", 96 | "wrongKeyName": "キーのプロパティ名が間違っています", 97 | "wrongValueName": "値のプロパティ名が間違っています", 98 | "wrongKeyType": "キーのプロパティの形式が間違っています", 99 | "wrongValueType": "値のプロパティの形式が間違っています" 100 | }, 101 | "finish": "取得が完了しました", 102 | "clearCache": "キャッシュがクリアされました" 103 | }, 104 | "Settings": { 105 | "updateLanguage": "言語が更新されました" 106 | }, 107 | "KeyValueRow": { 108 | "copy": "{{title}}をクリップボードにコピーしました" 109 | }, 110 | "main": { 111 | "noSelections": "1つ以上の要素を選択してください", 112 | "noTextInSelection": "選択した要素内にテキストがありません", 113 | "noTextInCurrentPage": "このページにテキストがありません", 114 | "noTextInAllPages": "すべてのページにテキストがありません", 115 | "errorLoadFonts": "フォントの読み込みでエラーが発生しました" 116 | }, 117 | "applyKeyValue": { 118 | "finish": "選択したテキストにキーと値を適用しました" 119 | }, 120 | "applyValue": { 121 | "noMatchingText": "一致するテキストがありません", 122 | "finishSelection": "選択したテキストに値を適用しました", 123 | "finishCurrentPage": "このページのテキストに値を適用しました", 124 | "finishAllPages": "すべてのページのテキストに値を適用しました" 125 | }, 126 | "renameLayer": { 127 | "finish": "テキストのレイヤー名を変更しました" 128 | }, 129 | "highlightText": { 130 | "finish": "テキストをハイライトしました" 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/ui/components/KeyValueList.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx h */ 2 | import { h } from 'preact' 3 | import { useCallback, useRef, useState } from 'preact/hooks' 4 | 5 | import clsx from 'clsx' 6 | import { useTranslation } from 'react-i18next' 7 | import { useDebounce, useMount, useUnmount, useUpdateEffect } from 'react-use' 8 | 9 | import { useStore } from '@/ui/Store' 10 | import KeyValueRow from '@/ui/components/KeyValueRow' 11 | import useOptions from '@/ui/hooks/useOptions' 12 | 13 | import type { NotionKeyValue } from '@/types/common' 14 | 15 | type KeyValueProps = { 16 | rows: NotionKeyValue[] 17 | className?: string 18 | } 19 | 20 | export default function KeyValueList({ rows, className }: KeyValueProps) { 21 | const { t } = useTranslation() 22 | const options = useStore() 23 | const { updateOptions } = useOptions() 24 | const listRef = useRef(null) 25 | const [tmpScrollPosition, setTmpScrollPosition] = useState(0) 26 | const [scrollPositionRestored, setScrollPositionRestored] = useState(false) 27 | 28 | // rowをクリックした時に実行する関数 29 | const handleRowClick = useCallback( 30 | (id: string) => { 31 | console.log('handleRowClick', id, options.selectedRowId) 32 | 33 | // 選択されてなければ選択済みにする 34 | // すでに選択済みだったら選択解除 35 | if (id !== options.selectedRowId) { 36 | updateOptions({ selectedRowId: id }) 37 | } else { 38 | updateOptions({ selectedRowId: null }) 39 | } 40 | }, 41 | [options.selectedRowId], 42 | ) 43 | 44 | // スクロール時にscrollPositionを更新する関数 45 | const handleScroll = useCallback(() => { 46 | if (listRef.current) { 47 | setTmpScrollPosition(listRef.current.scrollTop) 48 | } 49 | }, []) 50 | 51 | // tmpScrollPositionが更新されたらdebounceさせてからStoreに保存 52 | // scrollPositionRestoredがtrueのときだけ 53 | useDebounce( 54 | () => { 55 | if (scrollPositionRestored) { 56 | console.log('scrollPosition update (debounced)', tmpScrollPosition) 57 | updateOptions({ scrollPosition: tmpScrollPosition }) 58 | } 59 | }, 60 | 100, 61 | [tmpScrollPosition], 62 | ) 63 | 64 | useMount(() => { 65 | console.log('KeyValueList mounted') 66 | }) 67 | 68 | useUnmount(() => { 69 | console.log('KeyValueList unmounted') 70 | }) 71 | 72 | // rowsが変更されたら 73 | // スクロール位置が復元されていない→スクロール位置を復元 74 | // スクロール位置が復元済み(フィルター or ソート時)→スクロール位置を0にする 75 | useUpdateEffect(() => { 76 | console.log( 77 | 'rows updated', 78 | `scrollPositionRestored: ${scrollPositionRestored}`, 79 | ) 80 | 81 | if (!listRef.current) { 82 | return 83 | } 84 | 85 | if (!scrollPositionRestored) { 86 | console.log('restore scroll position', options.scrollPosition) 87 | listRef.current.scrollTo({ 88 | top: options.scrollPosition, 89 | }) 90 | setTmpScrollPosition(options.scrollPosition) 91 | setScrollPositionRestored(true) 92 | } else { 93 | console.log('reset scroll position to top') 94 | listRef.current.scrollTo({ 95 | top: 0, 96 | }) 97 | setTmpScrollPosition(0) 98 | } 99 | }, [rows]) 100 | 101 | // rowsが変更される度にイベントリスナを再設定 102 | useUpdateEffect(() => { 103 | const listElement = listRef.current 104 | 105 | if (listElement) { 106 | listElement.addEventListener('scroll', handleScroll) 107 | } 108 | 109 | return () => { 110 | if (listElement) { 111 | listElement.removeEventListener('scroll', handleScroll) 112 | } 113 | } 114 | }, [rows]) 115 | 116 | return ( 117 |
118 | {rows.length > 0 ? ( 119 |
    120 | {rows.map((row, index) => ( 121 | 127 | ))} 128 |
129 | ) : ( 130 | // empty 131 |
132 | {t('KeyValueList.empty')} 133 |
134 | )} 135 | 136 | {/* loading */} 137 | {!scrollPositionRestored && ( 138 |
139 | {t('KeyValueList.loading')} 140 |
141 | )} 142 |
143 | ) 144 | } 145 | -------------------------------------------------------------------------------- /src/ui/hooks/useNotion.ts: -------------------------------------------------------------------------------- 1 | import { useTranslation } from 'react-i18next' 2 | 3 | import type { 4 | NotionFomula, 5 | NotionKeyValue, 6 | NotionPage, 7 | NotionRichText, 8 | NotionTitle, 9 | } from '@/types/common' 10 | 11 | export default function useNotion() { 12 | const { t } = useTranslation() 13 | 14 | async function fetchNotion(options: { 15 | proxyUrl: string 16 | integrationToken: string 17 | databaseId: string 18 | keyPropertyName: string 19 | valuePropertyName: string 20 | nextCursor?: string 21 | keyValuesArray: NotionKeyValue[] 22 | }) { 23 | console.log('fetchNotion', options) 24 | 25 | // proxyUrlから末尾のスラッシュを削除 26 | const proxyUrl = options.proxyUrl.replace(/\/$/, '') 27 | 28 | // パラメータを定義 29 | // 引数nextCursorがある場合は、start_cursorを設定 30 | const reqParams = { 31 | page_size: 100, 32 | start_cursor: options.nextCursor || undefined, 33 | } 34 | 35 | // データベースをfetchしてpageの配列を取得 36 | const res = await fetch( 37 | `${proxyUrl}/https://api.notion.com/v1/databases/${options.databaseId}/query`, 38 | { 39 | method: 'POST', 40 | headers: { 41 | Authorization: `Bearer ${options.integrationToken}`, 42 | 'Notion-Version': '2021-08-16', 43 | 'Content-Type': 'application/json', 44 | }, 45 | body: JSON.stringify(reqParams), 46 | }, 47 | ).catch(() => { 48 | throw new Error(t('notifications.Fetch.error.failedFetch')) 49 | }) 50 | const resJson = await res.json() 51 | console.log(resJson) 52 | const pages = resJson.results as NotionPage[] 53 | 54 | if (!pages) { 55 | // pagesが無かったら処理中断 56 | throw new Error(t('notifications.Fetch.error.noPages')) 57 | } 58 | 59 | // pageごとに処理実行 60 | pages.forEach(row => { 61 | // keyPropertyNameと同じプロパティが無かったら処理中断 62 | if (!row.properties[options.keyPropertyName]) { 63 | throw new Error(t('notifications.Fetch.error.wrongKeyName')) 64 | } 65 | 66 | // valuePropertyNameと同じプロパティが無かったら処理中断 67 | if (!row.properties[options.valuePropertyName]) { 68 | throw new Error(t('notifications.Fetch.error.wrongValueName')) 69 | } 70 | 71 | // keyPropertyNameからpropertyを探す 72 | const keyProperty = row.properties[options.keyPropertyName] 73 | // keyのtypeがtitle, formula, textでない場合は処理中断 74 | if ( 75 | keyProperty.type !== 'title' && 76 | keyProperty.type !== 'rich_text' && 77 | keyProperty.type !== 'formula' 78 | ) { 79 | throw new Error(t('notifications.Fetch.error.wrongKeyType')) 80 | } 81 | // propertyのtypeを判別してkeyを取得する 82 | const key = getPropertyValue(keyProperty) 83 | 84 | // valuePropertyNameからpropertyを探す 85 | const valueProperty = row.properties[options.valuePropertyName] 86 | // valueのtypeがtitle, formula, textでない場合は処理中断 87 | if ( 88 | valueProperty.type !== 'title' && 89 | valueProperty.type !== 'rich_text' && 90 | valueProperty.type !== 'formula' 91 | ) { 92 | throw new Error(t('notifications.Fetch.error.wrongValueType')) 93 | } 94 | // propertyのtypeを判別してvalueを取得する 95 | const value = getPropertyValue(valueProperty) 96 | 97 | // keyValuesの配列にkeyとvalueを追加 98 | options.keyValuesArray.push({ 99 | id: row.id, 100 | key, 101 | value, 102 | created_time: row.created_time, 103 | last_edited_time: row.last_edited_time, 104 | url: row.url, 105 | }) 106 | }) 107 | 108 | if (resJson.has_more) { 109 | // resのhas_moreフラグがtrueなら、nextCursorに値を入れて再度fetchNotion関数を実行 110 | // falseなら終了 111 | await fetchNotion({ ...options, nextCursor: resJson.next_cursor }) 112 | } else { 113 | return 114 | } 115 | } 116 | 117 | function getPropertyValue( 118 | property: NotionTitle | NotionFomula | NotionRichText, 119 | ): string { 120 | let value: string 121 | 122 | if (property.type === 'title') { 123 | if (property.title.length) { 124 | value = property.title[0].plain_text 125 | } else { 126 | value = '' 127 | } 128 | } else if (property.type === 'rich_text') { 129 | if (property.rich_text.length) { 130 | value = property.rich_text[0].plain_text 131 | } else { 132 | value = '' 133 | } 134 | } else if (property.type === 'formula') { 135 | value = property.formula.string 136 | } else { 137 | value = '' 138 | } 139 | 140 | return value 141 | } 142 | 143 | return { fetchNotion } 144 | } 145 | -------------------------------------------------------------------------------- /src/main/applyValue.ts: -------------------------------------------------------------------------------- 1 | import { loadFontsAsync } from '@create-figma-plugin/utilities' 2 | import queryString, { type ParsedQuery } from 'query-string' 3 | 4 | import i18n from '@/i18n/main' 5 | import { getTextNodes } from '@/main/util' 6 | 7 | import type { NotionKeyValue, TargetTextRange } from '@/types/common' 8 | 9 | export default async function applyValue( 10 | keyValues: NotionKeyValue[], 11 | options: { 12 | targetTextRange: TargetTextRange 13 | includeComponents: boolean 14 | includeInstances: boolean 15 | }, 16 | ) { 17 | console.log('applyValue', keyValues, options) 18 | 19 | // textNodeを取得 20 | const textNodes = await getTextNodes(options) 21 | 22 | console.log('textNodes', textNodes) 23 | 24 | // textNodeが1つもなかったら処理を終了 25 | if (textNodes.length === 0) { 26 | // 選択状態によってトーストを出し分け 27 | if (options.targetTextRange === 'selection') { 28 | figma.notify(i18n.t('notifications.main.noTextInSelection')) 29 | } else if (options.targetTextRange === 'currentPage') { 30 | figma.notify(i18n.t('notifications.main.noTextInCurrentPage')) 31 | } else if (options.targetTextRange === 'allPages') { 32 | figma.notify(i18n.t('notifications.main.noTextInAllPages')) 33 | } 34 | 35 | return 36 | } 37 | 38 | // matchedTextNodesを格納する配列を用意 39 | let matchedTextNodes: TextNode[] = [] 40 | 41 | // textNodeの中からレイヤー名が#で始まるものだけを探してmatchedTextNodesに追加 42 | matchedTextNodes = textNodes.filter(textNode => { 43 | return textNode.name.startsWith('#') 44 | }) 45 | 46 | console.log('matchedTextNodes', matchedTextNodes) 47 | 48 | // matchedTextNodesが空なら処理を終了 49 | if (matchedTextNodes.length === 0) { 50 | figma.notify(i18n.t('notifications.applyValue.noMatchingText')) 51 | return 52 | } 53 | 54 | // 事前にフォントをロード 55 | await loadFontsAsync(matchedTextNodes).catch((error: Error) => { 56 | const errorMessage = i18n.t('notifications.main.errorLoadFonts') 57 | figma.notify(errorMessage, { error: true }) 58 | throw new Error(errorMessage) 59 | }) 60 | 61 | // matchedTextNodesごとに処理を実行 62 | matchedTextNodes.forEach(textNode => { 63 | // クエリパラメータを取得する 64 | // ?から後ろの部分をクエリパラメータと見なす 65 | // 一部の文字がうまくパースされないので、パース前にエンコードする 66 | const originalParam = textNode.name.split('?')[1] as string | undefined 67 | let param: ParsedQuery 68 | if (originalParam) { 69 | const replacedParam = originalParam.replace(/\+/g, '%2B') 70 | param = queryString.parse(replacedParam) 71 | } else { 72 | param = queryString.parse('') 73 | } 74 | 75 | // レイヤー名から#を取ってkey名にする 76 | // #から、?までの部分をkey名と見なす 77 | const key = textNode.name.split('?')[0].replace(/^#/, '') 78 | 79 | // key名を使ってkeyValuesからオブジェクトを検索する 80 | const keyValue = keyValues.find(keyValue => { 81 | return keyValue.key === key 82 | }) 83 | 84 | // テキストを置換する 85 | // keyValueオブジェクトが見つかった場合 86 | if (keyValue) { 87 | console.log('keyValue found', key) 88 | 89 | // 見つかったkeyValueオブジェクトからvalueを取り出す 90 | const value = keyValue.value 91 | 92 | console.log('value', value) 93 | console.log('param', param) 94 | 95 | // テキストをvalueに置換 96 | // paramがある場合→valueの中の{paramKey}の置き換えを試みる 97 | if (Object.keys(param).length) { 98 | // valueの中に{paramKey}があるか探す 99 | const matchedParamKeys = value.match(/(?<=\{).*?(?=\})/g) 100 | 101 | // 無い場合、仕方がないので普通にvalueを入れる 102 | if (!matchedParamKeys) { 103 | textNode.characters = value 104 | return 105 | } 106 | 107 | // value内にある{paramKey}毎に置換 108 | let replacedValue = value 109 | matchedParamKeys.forEach(paramKey => { 110 | replacedValue = replacedValue.replace( 111 | new RegExp(`{${paramKey}}`, 'g'), 112 | param[paramKey] !== undefined 113 | ? String(param[paramKey]) 114 | : `{${paramKey}}`, 115 | ) 116 | }) 117 | 118 | // replacedValueをテキスト本文にする 119 | textNode.characters = replacedValue 120 | } 121 | // paramが無い場合→普通にvalueを入れる 122 | else { 123 | textNode.characters = value 124 | } 125 | } 126 | // keyValueオブジェクトが見つからなかった場合 127 | else { 128 | console.log('keyValue not found', key) 129 | } 130 | }) 131 | 132 | // 完了通知 133 | // 選択状態によってトーストを出し分け 134 | if (options.targetTextRange === 'selection') { 135 | figma.notify(i18n.t('notifications.applyValue.finishSelection')) 136 | } else if (options.targetTextRange === 'currentPage') { 137 | figma.notify(i18n.t('notifications.applyValue.finishCurrentPage')) 138 | } else if (options.targetTextRange === 'allPages') { 139 | figma.notify(i18n.t('notifications.applyValue.finishAllPages')) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/i18n/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tabs": { 3 | "fetch": "Fetch", 4 | "list": "List", 5 | "utilities": "Utilities", 6 | "settings": "Settings" 7 | }, 8 | "Fetch": { 9 | "databaseId": "Database ID", 10 | "integrationToken": "Integration token", 11 | "keyPropertyName": "Key property name", 12 | "valuePropertyName": "Value property name", 13 | "fetchButton": { 14 | "label": "Fetch text from Notion", 15 | "description": "Fetches text from a database in Notion. The data is cached to this document and restored at next time it is launched. If you have updated Notion database, click this button again." 16 | }, 17 | "clearCacheButton": "Clear cache ({{length}} items)" 18 | }, 19 | "List": { 20 | "filter": { 21 | "placeholder": "Filter key or value property", 22 | "clearButton": "Clear" 23 | }, 24 | "sortValue": { 25 | "key": "Key", 26 | "value": "Value", 27 | "created_time": "Created (default)", 28 | "last_edited_time": "Last edited" 29 | }, 30 | "sortOrder": { 31 | "ascending": "Ascending", 32 | "descending": "Descending (Default)" 33 | }, 34 | "statusBar": { 35 | "text": "Click row to apply key & value to text or copy", 36 | "count": "{{length}} items" 37 | }, 38 | "empty": { 39 | "text": "No items.", 40 | "fetchButton": "Fetch text from Notion" 41 | } 42 | }, 43 | "Utilities": { 44 | "targetTextRange": { 45 | "label": "Target text range", 46 | "options": { 47 | "selection": "Selection", 48 | "currentPage": "Current page", 49 | "allPages": "All pages" 50 | } 51 | }, 52 | "includeComponents": "Include text in components", 53 | "includeInstances": "Include text in instances", 54 | "applyValueButton": { 55 | "label": "Apply value to text", 56 | "description": "If the name of the text layer is # (e.g. #userInfo), the text content will be replaced. If the value has an embedded variable, a parameter can be passed to the layer name (e.g. #userInfo?name=John&age=24). If the value is \"Name: {name} / Age: {age}\", the text content will be \"Name: Jogn / Age: 24\"." 57 | }, 58 | "renameLayerButton": { 59 | "label": "Rename layer", 60 | "description": "If the text content has the same value in the database, rename the layer to #. This is useful when renaming layers is troublesome." 61 | }, 62 | "highlightTextButton": { 63 | "label": "Highlight text", 64 | "description": "Highlight the text layer with the name #. If the key exists in the database, it is highlighted in blue; if it does not exist or the key is incorrect, it is highlighted in red. This is useful to verify that layer names are formatted correctly or that there are no typos." 65 | } 66 | }, 67 | "Settings": { 68 | "language": { 69 | "label": "Language", 70 | "options": { 71 | "english": "English (Default)", 72 | "japanese": "Japanese" 73 | } 74 | }, 75 | "buttons": { 76 | "pluginPage": "Plugin page", 77 | "github": "GitHub" 78 | } 79 | }, 80 | "KeyValueList": { 81 | "empty": "No items.", 82 | "loading": "Loading..." 83 | }, 84 | "KeyValueRow": { 85 | "key": "Key", 86 | "value": "Value", 87 | "applyKeyValueButton": "Apply key & value to selected text", 88 | "openBrowserButton": "Open in browser" 89 | }, 90 | "notifications": { 91 | "Fetch": { 92 | "loading": "Please wait a moment.", 93 | "error": { 94 | "failedFetch": "Failed to fetch database.", 95 | "noPages": "No pages in this database.", 96 | "wrongKeyName": "Key property name is wrong.", 97 | "wrongValueName": "Value property name is wrong.", 98 | "wrongKeyType": "Key property type is wrong.", 99 | "wrongValueType": "Value property type is wrong." 100 | }, 101 | "finish": "Fetch finish.", 102 | "clearCache": "Cache cleared." 103 | }, 104 | "Settings": { 105 | "updateLanguage": "Language updated." 106 | }, 107 | "KeyValueRow": { 108 | "copy": "Copied {{title}} to clipboard." 109 | }, 110 | "main": { 111 | "noSelections": "Please select at least one layer.", 112 | "noTextInSelection": "No text layers in selection.", 113 | "noTextInCurrentPage": "No text layers in this page.", 114 | "noTextInAllPages": "No text layers in all pages.", 115 | "errorLoadFonts": "Error on loading font." 116 | }, 117 | "applyKeyValue": { 118 | "finish": "Applied key & value to selected text." 119 | }, 120 | "applyValue": { 121 | "noMatchingText": "No matching text.", 122 | "finishSelection": "Apply value to text content in selection.", 123 | "finishCurrentPage": "Apply value to text content in this page.", 124 | "finishAllPages": "Apply value to text content in all pages." 125 | }, 126 | "renameLayer": { 127 | "finish": "Renamed layer name." 128 | }, 129 | "highlightText": { 130 | "finish": "Highlighted text layer." 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/highlightText.ts: -------------------------------------------------------------------------------- 1 | import { GROUP_ID_KEY } from '@/constants' 2 | import i18n from '@/i18n/main' 3 | import { getTextNodes } from '@/main/util' 4 | 5 | import type { NotionKeyValue, TargetTextRange } from '@/types/common' 6 | 7 | async function createHighlightRectOnPage( 8 | keyValues: NotionKeyValue[], 9 | textNodes: TextNode[], 10 | pageNode: PageNode, 11 | ) { 12 | console.log('createHighlightRectOnPage', textNodes, pageNode) 13 | 14 | // correctLayerNameFormatTextNodesを格納する配列を用意 15 | let correctLayerNameFormatTextNodes: TextNode[] = [] 16 | 17 | // Rectangleを格納する配列を用意 18 | let rectNodes: RectangleNode[] = [] 19 | const correctRectNodes: RectangleNode[] = [] 20 | const incorrectRectNodes: RectangleNode[] = [] 21 | 22 | // textNodeの中からレイヤー名が#で始まるものだけを探してcorrectLayerNameFormatTextNodesに追加する 23 | correctLayerNameFormatTextNodes = textNodes.filter(textNode => { 24 | console.log(textNode.name) 25 | return textNode.name.startsWith('#') 26 | }) 27 | 28 | console.log( 29 | 'correctLayerNameFormatTextNodes', 30 | correctLayerNameFormatTextNodes, 31 | ) 32 | 33 | if (correctLayerNameFormatTextNodes.length === 0) { 34 | // correctLayerNameFormatTextNodesが空の場合は処理を終了 35 | console.log('no matched text') 36 | return 37 | } 38 | 39 | // 以前生成したgroupを探して、削除 40 | const generatedGroupId = pageNode.getPluginData(GROUP_ID_KEY) 41 | const previousGeneratedGroup = pageNode.findOne( 42 | node => node.id === generatedGroupId, 43 | ) 44 | console.log('generatedGroupId', generatedGroupId) 45 | console.log('previousGeneratedGroup', previousGeneratedGroup) 46 | if (previousGeneratedGroup) { 47 | previousGeneratedGroup.remove() 48 | } 49 | 50 | // correctLayerNameFormatTextNodesごとに処理を実行 51 | await Promise.all( 52 | correctLayerNameFormatTextNodes.map(async textNode => { 53 | // レイヤー名から#を取ってkey名にする 54 | // #から、?までの部分をkey名と見なす 55 | const key = textNode.name.split('?')[0].replace(/^#/, '') 56 | 57 | // key名を使ってkeyValuesからオブジェクトを検索する 58 | const matchedKeyValue = keyValues.find(keyValue => { 59 | return keyValue.key === key 60 | }) 61 | 62 | // ハイライト用のrectを作る(レイヤーが表示されているものだけ) 63 | if (textNode.absoluteRenderBounds) { 64 | // rectを作って、サイズとかstrokeとか設定 65 | const rect = figma.createRectangle() 66 | rect.x = textNode.absoluteRenderBounds.x 67 | rect.y = textNode.absoluteRenderBounds.y 68 | rect.resize( 69 | textNode.absoluteRenderBounds.width, 70 | textNode.absoluteRenderBounds.height, 71 | ) 72 | 73 | // keyValueオブジェクトが見つかったら、rectを青で塗りつぶす 74 | if (matchedKeyValue) { 75 | rect.fills = [figma.util.solidPaint({ r: 0, g: 0, b: 1, a: 0.3 })] 76 | rect.name = `⭕️ ${textNode.name}` 77 | // correctRectNodes配列にrectを追加 78 | correctRectNodes.push(rect) 79 | } 80 | // keyValueオブジェクトが見つからない場合 81 | // (レイヤー名は#で始まっているがkeyが間違っている場合)は、rectを赤で塗りつぶす 82 | else { 83 | rect.fills = [figma.util.solidPaint({ r: 1, g: 0, b: 0, a: 0.3 })] 84 | rect.name = `❌ ${textNode.name}` 85 | // incorrectRectNodes配列にrectを追加 86 | incorrectRectNodes.push(rect) 87 | } 88 | } 89 | }), 90 | ) 91 | 92 | // correctRectNodesとincorrectRectNodesをrectNodesにマージする 93 | rectNodes = [...correctRectNodes, ...incorrectRectNodes] 94 | console.log('rectNodes', rectNodes) 95 | 96 | if (rectNodes.length > 0) { 97 | // rectNodeが1つ以上ある場合、rectをグルーピングする 98 | const group = figma.group(rectNodes, figma.currentPage) 99 | 100 | // グループをリネーム 101 | group.name = `${rectNodes.length} Highlights (⭕️ ${correctRectNodes.length} / ❌ ${incorrectRectNodes.length}) - Generated with Sync Text with Notion` 102 | 103 | // グループをロック 104 | group.locked = true 105 | 106 | // グループを折りたたむ 107 | group.expanded = false 108 | 109 | // 生成したgroupのidをcurrentPageのgeneratedGroupIdに保存する 110 | pageNode.setPluginData(GROUP_ID_KEY, group.id) 111 | } 112 | } 113 | 114 | export default async function highlightText( 115 | keyValues: NotionKeyValue[], 116 | options: { 117 | targetTextRange: TargetTextRange 118 | includeComponents: boolean 119 | includeInstances: boolean 120 | }, 121 | ) { 122 | console.log('highlightText', keyValues, options) 123 | 124 | // targetTextRangeに応じて処理を分岐 125 | if (options.targetTextRange === 'allPages') { 126 | for (const page of figma.root.children) { 127 | // まず対象のpageNodeに移動 128 | await figma.setCurrentPageAsync(page) 129 | 130 | // そのページ内のtextNodeを取得 131 | const textNodes = await getTextNodes({ 132 | targetTextRange: 'currentPage', 133 | includeComponents: options.includeComponents, 134 | includeInstances: options.includeInstances, 135 | }) 136 | 137 | // ハイライトを実行 138 | await createHighlightRectOnPage(keyValues, textNodes, page) 139 | } 140 | } else { 141 | // targetTextRangeにもとづいてtextNodeを取得 142 | const textNodes = await getTextNodes(options) 143 | 144 | // ハイライトを実行 145 | await createHighlightRectOnPage(keyValues, textNodes, figma.currentPage) 146 | } 147 | 148 | // 完了通知 149 | figma.notify(i18n.t('notifications.highlightText.finish')) 150 | } 151 | -------------------------------------------------------------------------------- /src/ui/components/KeyValueRow.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx h */ 2 | import { type JSX, h } from 'preact' 3 | 4 | import { Button } from '@create-figma-plugin/ui' 5 | import { emit } from '@create-figma-plugin/utilities' 6 | import clsx from 'clsx' 7 | import { useTranslation } from 'react-i18next' 8 | import { useCopyToClipboard } from 'react-use' 9 | 10 | import useNotionKeyValue from '@/ui/hooks/useNotionKeyValue' 11 | 12 | import type { NotionKeyValue } from '@/types/common' 13 | import type { ApplyKeyValueHandler, NotifyHandler } from '@/types/eventHandler' 14 | 15 | type CopyButtonProps = { 16 | type: 'key' | 'value' 17 | title: string 18 | keyValue: NotionKeyValue 19 | selected: boolean 20 | className?: string 21 | } 22 | type RowProps = { 23 | keyValue: NotionKeyValue 24 | onClick: (id: string) => void 25 | selected: boolean 26 | } 27 | 28 | function CopyButton({ 29 | type, 30 | title, 31 | keyValue, 32 | selected, 33 | className, 34 | }: CopyButtonProps) { 35 | const { t } = useTranslation() 36 | const [state, copyToClipboard] = useCopyToClipboard() 37 | const { getKeyWithQueryStrings } = useNotionKeyValue() 38 | 39 | function handleClick(event: JSX.TargetedMouseEvent) { 40 | // 親要素へのバブリングを止める 41 | event.stopPropagation() 42 | 43 | let copyValue = '' 44 | 45 | // typeがkeyの場合 46 | if (type === 'key') { 47 | copyValue = getKeyWithQueryStrings(keyValue) 48 | } 49 | // typeがvalueの場合 50 | else if (type === 'value') { 51 | copyValue = keyValue.value 52 | } 53 | 54 | copyToClipboard(copyValue) 55 | console.log('copied', copyValue) 56 | 57 | emit('NOTIFY', { 58 | message: t('notifications.KeyValueRow.copy', { title }), 59 | }) 60 | } 61 | 62 | return ( 63 |
64 | 75 |
76 | ) 77 | } 78 | 79 | export default function KeyValueRow({ keyValue, onClick, selected }: RowProps) { 80 | const { t } = useTranslation() 81 | const { getKeyWithQueryStrings } = useNotionKeyValue() 82 | 83 | function handleApplyClick(event: JSX.TargetedMouseEvent) { 84 | // 親要素へのバブリングを止める 85 | event.stopPropagation() 86 | 87 | const applyKeyValue: NotionKeyValue = { 88 | ...keyValue, 89 | key: getKeyWithQueryStrings(keyValue), 90 | } 91 | 92 | console.log('handleApplyClick', applyKeyValue) 93 | emit('APPLY_KEY_VALUE', applyKeyValue) 94 | } 95 | 96 | function handleOpenClick(event: JSX.TargetedMouseEvent) { 97 | // 親要素へのバブリングを止める 98 | event.stopPropagation() 99 | 100 | // ブラウザでkeyValue.urlを開く 101 | window.open(keyValue.url, '_blank', 'noopener, noreferrer') 102 | } 103 | 104 | return ( 105 |
  • onClick(keyValue.id)} 111 | > 112 | {/* key property */} 113 |
    114 |
    {t('KeyValueRow.key')}
    115 |
    121 |
    122 | {keyValue.key} 123 | 130 |
    131 |
    132 |
    133 | 134 | {/* value property */} 135 |
    136 |
    {t('KeyValueRow.value')}
    137 |
    143 |
    144 | {keyValue.value} 145 | 152 |
    153 |
    154 |
    155 | 156 | {selected && ( 157 |
    158 | 161 | 164 |
    165 | )} 166 |
  • 167 | ) 168 | } 169 | -------------------------------------------------------------------------------- /src/main/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | emit, 3 | loadSettingsAsync, 4 | on, 5 | saveSettingsAsync, 6 | setRelaunchButton, 7 | showUI, 8 | } from '@create-figma-plugin/utilities' 9 | 10 | import { 11 | CACHE_KEY, 12 | DEFAULT_CLIENT_STORAGE_OPTIONS, 13 | DEFAULT_DOCUMENT_OPTIONS, 14 | DEFAULT_WIDTH, 15 | SETTINGS_KEY, 16 | } from '@/constants' 17 | import i18n from '@/i18n/main' 18 | import applyKeyValue from '@/main/applyKeyValue' 19 | import applyValue from '@/main/applyValue' 20 | import highlightText from '@/main/highlightText' 21 | import renameLayer from '@/main/renameLayer' 22 | 23 | import type { 24 | ClientStorageOptions, 25 | DocumentOptions, 26 | NotionKeyValue, 27 | Options, 28 | } from '@/types/common' 29 | import type { 30 | ApplyKeyValueHandler, 31 | ApplyValueHandler, 32 | ChangeLanguageHandler, 33 | HighlightTextHandler, 34 | LoadCacheFromMainHandler, 35 | LoadCacheFromUIHandler, 36 | LoadOptionsFromMainHandler, 37 | LoadOptionsFromUIHandler, 38 | NotifyHandler, 39 | RenameLayerHandler, 40 | ResizeWindowHandler, 41 | SaveCacheHandler, 42 | SaveOptionsHandler, 43 | } from '@/types/eventHandler' 44 | 45 | export default async function () { 46 | // set relaunch button 47 | setRelaunchButton(figma.root, 'open') 48 | 49 | // show ui 50 | showUI({ 51 | width: DEFAULT_WIDTH, 52 | height: 0, 53 | }) 54 | 55 | // setPluginDataで保存したキャッシュを削除(clientStorageに移行したため) 56 | figma.root.setPluginData(CACHE_KEY, '') 57 | 58 | // register event handlers 59 | on('LOAD_OPTIONS_FROM_UI', async () => { 60 | // documentOptionsを取得 61 | let documentOptions: DocumentOptions = DEFAULT_DOCUMENT_OPTIONS 62 | const pluginData = figma.root.getPluginData(SETTINGS_KEY) 63 | if (pluginData) { 64 | documentOptions = JSON.parse(pluginData) 65 | } 66 | console.log('documentOptions', documentOptions) 67 | 68 | // clientStorageOptionsを取得 69 | const clientStorageOptions = await loadSettingsAsync( 70 | DEFAULT_CLIENT_STORAGE_OPTIONS, 71 | SETTINGS_KEY, 72 | ) 73 | console.log('clientStorageOptions', clientStorageOptions) 74 | 75 | // documentOptionsとclientStorageをマージ 76 | const options: Options = { ...documentOptions, ...clientStorageOptions } 77 | 78 | // main側の言語を切り替え 79 | await i18n.changeLanguage(options.pluginLanguage) 80 | console.log('language in main updated.', options.pluginLanguage, i18n) 81 | 82 | // uiにoptionsを送る 83 | emit('LOAD_OPTIONS_FROM_MAIN', options) 84 | }) 85 | 86 | on('SAVE_OPTIONS', async options => { 87 | const newDocumentOptions: DocumentOptions = { 88 | databaseId: options.databaseId, 89 | integrationToken: options.integrationToken, 90 | keyPropertyName: options.keyPropertyName, 91 | valuePropertyName: options.valuePropertyName, 92 | } 93 | const newClientStorageOptions: ClientStorageOptions = { 94 | selectedTabKey: options.selectedTabKey, 95 | filterString: options.filterString, 96 | sortValue: options.sortValue, 97 | sortOrder: options.sortOrder, 98 | selectedRowId: options.selectedRowId, 99 | scrollPosition: options.scrollPosition, 100 | targetTextRange: options.targetTextRange, 101 | includeComponents: options.includeComponents, 102 | includeInstances: options.includeInstances, 103 | pluginLanguage: options.pluginLanguage, 104 | } 105 | console.log('newDocumentOptions', newDocumentOptions) 106 | console.log('newClientStorageOptions', newClientStorageOptions) 107 | 108 | // 新しいオプションをDocumentに保存 109 | figma.root.setPluginData(SETTINGS_KEY, JSON.stringify(newDocumentOptions)) 110 | 111 | // 新しいオプションをclientStorageに保存 112 | await saveSettingsAsync( 113 | newClientStorageOptions, 114 | SETTINGS_KEY, 115 | ) 116 | }) 117 | 118 | on('NOTIFY', options => { 119 | figma.notify(options.message, options.options) 120 | }) 121 | 122 | on('RESIZE_WINDOW', windowSize => { 123 | figma.ui.resize(windowSize.width, windowSize.height) 124 | }) 125 | 126 | on('LOAD_CACHE_FROM_UI', async () => { 127 | // キャッシュのデータをclientStorageから取得 128 | const data: NotionKeyValue[] = 129 | (await figma.clientStorage.getAsync(CACHE_KEY)) || [] 130 | console.log('cache data', data) 131 | 132 | // UIに送る 133 | emit('LOAD_CACHE_FROM_MAIN', data) 134 | }) 135 | 136 | on('SAVE_CACHE', async keyValues => { 137 | // キャッシュをclientStorageに保存 138 | await figma.clientStorage.setAsync(CACHE_KEY, keyValues) 139 | 140 | console.log('save cache success', keyValues) 141 | }) 142 | 143 | on('APPLY_KEY_VALUE', keyValue => { 144 | applyKeyValue(keyValue) 145 | }) 146 | 147 | on('APPLY_VALUE', (keyValues, options) => { 148 | applyValue(keyValues, options) 149 | }) 150 | 151 | on('RENAME_LAYER', (keyValues, options) => { 152 | renameLayer(keyValues, options) 153 | }) 154 | 155 | on('HIGHLIGHT_TEXT', (keyValues, options) => { 156 | highlightText(keyValues, options) 157 | }) 158 | 159 | on('CHANGE_LANGUAGE', async (language, options) => { 160 | // main側の言語を切り替え 161 | await i18n.changeLanguage(language) 162 | console.log('language in main updated.', language, i18n) 163 | 164 | // options.notifyがtrueの場合は完了通知 165 | if (options?.notify) { 166 | figma.notify(i18n.t('notifications.Settings.updateLanguage')) 167 | } 168 | }) 169 | } 170 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sync Text with Notion Figma Plugin 2 | 3 | ![](./cover.png) 4 | 5 | Sync text from Notion database to Figma document. 6 | Useful when managing app text in Notion or for multilingual support. 7 | \- 8 | NotionのデータベースからFigmaのドキュメントにテキストを同期するプラグインです。 9 | アプリケーションのテキストをNotionで管理したい場合や多言語対応をしたい場合に便利です。 10 | 11 | 12 | ## 🔥 How to use / 使い方 13 | 14 | ### Fetch tab / 「取得」タブ 15 | Fetches text from a database in Notion. The data is cached to the ClientStorage and restored at next time it is launched. If you have updated Notion database, fetch again. 16 | \- 17 | Notionのデータベースからテキストを取得します。取得したテキストはClientStorageにキャッシュされ、次回起動時に復元されます。もしNotionのデータベースを更新した場合は、再度取得してください。 18 | 19 | #### 1. Database ID / データベースID 20 | Specify the Notion database ID ([Reference](https://developers.notion.com/reference/retrieve-a-database)). 21 | \- 22 | NotionのデータベースIDを指定します ([参考](https://developers.notion.com/reference/retrieve-a-database))。 23 | 24 | #### 2. Integration token / インテグレーショントークン 25 | First, create a new integration in Notion ([Reference](https://developers.notion.com/docs/create-a-notion-integration#create-your-integration-in-notion)). 26 | Next, give your integration page permissions ([Reference](https://developers.notion.com/docs/create-a-notion-integration#give-your-integration-page-permissions)). 27 | Input the copied token. 28 | \- 29 | まず、Notionで新しくインテグレーションを作成します ([参考](https://developers.notion.com/docs/create-a-notion-integration#create-your-integration-in-notion))。 30 | 次に、作成したインテグレーションにデータベースへのアクセス権限を与えます ([参考](https://developers.notion.com/docs/create-a-notion-integration#give-your-integration-page-permissions))。 31 | コピーしたトークンを入力してください。 32 | 33 | #### 3. Key property name / キーのプロパティ名 34 | Specify the name of the property that is the key of the data to be fetched (e.g. Name, Key, Title, etc.). 35 | Currently, title, formula and text properties are supported. 36 | \- 37 | 取得するデータのキーとなるプロパティ名を指定します (例: Name、Key、Titleなど)。 38 | 現在、タイトル、フォーミュラ、テキストプロパティが対応しています。 39 | 40 | #### 4. Value property name / 値のプロパティ名 41 | Specify the name of the property that is the value of the data to be fetched (e.g. Value, en, ja, etc.). 42 | Currently, title, formula and text properties are supported. 43 | \- 44 | 取得するデータの値となるプロパティ名を指定します (例: Value、en、jaなど)。 45 | 現在、タイトル、フォーミュラ、テキストプロパティが対応しています。 46 | 47 | After entering the information for steps 1-4, click the "Fetch text from Notion" button to retrieve the text. Depending on the number of items in the database, this process may take some time. 48 | To delete the cache, click the "Clear cache" button. 49 | \- 50 | 1-4を入力したら、「Notionからテキストを取得」ボタンをクリックし、テキストを取得します。データベースの項目数によっては、しばらく待つ必要があります。 51 | キャッシュを削除する場合は、「キャッシュを削除」ボタンをクリックしてください。 52 | 53 | ### List tab / 「リスト」タブ 54 | Text retrieved from Notion will be displayed. Keys and values can be copied. 55 | You can also filter by key or value and change the sort order. 56 | Click on a row to display the following two buttons. 57 | \- 58 | Notionから取得したテキストが表示されます。キーと値をコピーすることができます。 59 | また、キーや値で絞り込むことができ、並び順も変更することができます。 60 | 行をクリックすると、以下の2つのボタンが表示されます。 61 | 62 | #### "Apply key & value to selected text" button / 「選択したテキストにキーと値を適用」ボタン 63 | Select text in Figma and click the button to apply the key and value to the text. 64 | \- 65 | Figmaのテキストを選択し、ボタンをクリックすると、テキストにキーと値が適用されます。 66 | 67 | #### "Open in browser" button / 「ブラウザで開く」ボタン 68 | Open the page of text in Notion in browser. 69 | \- 70 | Notionの該当するテキストのページをブラウザで開きます。 71 | 72 | ### Utilities tab / 「ユーティリティ」タブ 73 | Several actions are available to help synchronize Notion text with Figma. 74 | \- 75 | NotionのテキストをFigmaと同期するのに便利な、いくつかのアクションが用意されています。 76 | 77 | #### Target text range / 対象テキストの範囲 78 | Select the range of text to be the target of the action. You can choose from the selected elements, the current page, or all pages. 79 | You can also choose to include text in components or instances. 80 | \- 81 | アクションの対象にするテキストの範囲を選択します。選択した要素、現在のページ、すべてのページから選択することができます。 82 | また、コンポーネント内のテキストやインスタンス内のテキストを対象にすることもできます。 83 | 84 | #### "Apply value to text" button / 「テキストに値を適用」ボタン 85 | If the name of the text layer is #\ (e.g. #userInfo), the text content will be replaced. If the value has an embedded variable, a parameter can be passed to the layer name (e.g. #userInfo?name=John&age=24). If the value is "Name: {name} / Age: {age}", the text content will be "Name: John / Age: 24". 86 | \- 87 | テキストレイヤーの名前が#<キー>になっている場合(例: #userInfo)、テキストの内容を置き換えます。値に変数が埋め込まれている場合、レイヤー名にパラメータを渡すことができます(例: #userInfo?name=ジョン&age=24)。値が「名前: {name} / 年齢: {age}」の場合、テキストの内容は「名前: ジョン / 年齢: 24」となります。 88 | 89 | #### "Rename layer" button / 「レイヤー名を変更」ボタン 90 | If the text content has the same value in the database, rename the layer to #\. This is useful when renaming layers is troublesome. 91 | \- 92 | テキストの内容がデータベースの値と一致している場合、レイヤー名を#<キー>に変更します。レイヤーのリネームが面倒な場合に便利です。 93 | 94 | #### "Highlight text" button / 「テキストをハイライト」ボタン 95 | Highlight the text layer with the name #\. If the key exists in the database, it is highlighted in blue; if it does not exist or the key is incorrect, it is highlighted in red. This is useful to verify that layer names are formatted correctly or that there are no typos. 96 | \- 97 | テキストレイヤーの名前が#<キー>になっているものをハイライトします。キーがデータベースに存在する場合は青色、存在しない場合またはキーが間違っている場合は赤色でハイライトされます。レイヤー名が正しくフォーマットされているか、またはタイプミスがないかを確認するのに便利です。 98 | 99 | ### Settings tab / 「設定」タブ 100 | #### Language / 言語 101 | Select the language of this plugin. Currently, English and Japanese are available. 102 | \- 103 | このプラグインの言語を選択します。現在、英語と日本語が選択できます。 104 | 105 | 106 | ## 📮 Support 107 | 108 | If you have any plobrem or feedback, please use the [GitHub Issues](https://github.com/ryonakae/figma-plugin-sync-notion/issues). 109 | 110 | --- 111 | 112 | This plugin is made by Ryo Nakae 🙎‍♂️. 113 | 114 | - https://brdr.jp 115 | - https://x.com/ryo_dg 116 | - https://github.com/ryonakae 117 | -------------------------------------------------------------------------------- /src/main/util.ts: -------------------------------------------------------------------------------- 1 | import times from 'lodash/times' 2 | import uniqBy from 'lodash/uniqBy' 3 | 4 | import i18n from '@/i18n/main' 5 | 6 | import type { TargetTextRange } from '@/types/common' 7 | 8 | // nodeの親がコンポーネント or Variantsかどうかを返す再帰関数 9 | export function getIsNodeParentComponentOrVariants(node: SceneNode) { 10 | // 親要素がなかったらfalseを返す 11 | if (!node.parent) { 12 | return false 13 | } 14 | // 親がpage or documentならfalseを返す 15 | if (node.parent.type === 'PAGE' || node.parent.type === 'DOCUMENT') { 16 | return false 17 | } 18 | // 親要素のtypeがCOMPONENT || COMPONENT_SETならtrueを返す 19 | if ( 20 | node.parent.type === 'COMPONENT' || 21 | node.parent.type === 'COMPONENT_SET' 22 | ) { 23 | return true 24 | } 25 | // ↑のどれにも当てはまらなかったら親要素を対象にして関数実行 26 | return getIsNodeParentComponentOrVariants(node.parent) 27 | } 28 | 29 | // nodeのidから先祖インスタンスの配列を返す関数(自分は含まない) 30 | export function getAncestorInstances(node: SceneNode) { 31 | const instanceArray: InstanceNode[] = [] 32 | 33 | // idをセミコロンで区切って配列にしたもの 34 | const idArray = node.id.split(';') 35 | 36 | idArray.map((id, i) => { 37 | if (i === idArray.length - 1) { 38 | return 39 | } 40 | 41 | // indexに応じてidを加工 42 | let targetId = '' 43 | if (i === 0) { 44 | targetId = idArray[i] 45 | } else { 46 | const arr: string[] = [] 47 | times(i + 1).map(j => arr.push(idArray[j])) 48 | targetId = arr.join(';') 49 | } 50 | 51 | // 加工したidを元にインスタンスを検索 52 | const instance = figma.getNodeById(targetId) as InstanceNode 53 | 54 | instanceArray.push(instance) 55 | }) 56 | 57 | return instanceArray 58 | } 59 | 60 | // textNodesをフィルタリングする関数 61 | function filterTextNodes( 62 | textNodes: TextNode[], 63 | options: { 64 | includeComponents: boolean 65 | includeInstances: boolean 66 | }, 67 | ) { 68 | // 削除予定のtextNodeを格納する配列 69 | let textNodesToRemove: TextNode[] = [] 70 | 71 | // includeComponentsがfalse 72 | // → コンポーネントまたはバリアントの子要素をtextNodesToRemoveに追加 73 | if (!options.includeComponents) { 74 | textNodes.forEach(textNode => { 75 | console.log( 76 | 'Checking textNode is component/variant child:', 77 | textNode.characters, 78 | ) 79 | 80 | if (getIsNodeParentComponentOrVariants(textNode)) { 81 | console.log( 82 | 'Removing textNode is component/variant child:', 83 | textNode.characters, 84 | ) 85 | textNodesToRemove.push(textNode) 86 | } 87 | }) 88 | } 89 | 90 | // includeInstances 91 | // → インスタンスの子要素をtextNodesToRemoveに追加 92 | if (!options.includeInstances) { 93 | console.log('textNodes', textNodes) 94 | textNodes.forEach(textNode => { 95 | console.log('Checking textNode is instance child:', textNode.characters) 96 | 97 | const ancestorInstances = getAncestorInstances(textNode) 98 | if (ancestorInstances.length > 0) { 99 | console.log('Removing textNode is instance child:', textNode.characters) 100 | textNodesToRemove.push(textNode) 101 | } 102 | }) 103 | } 104 | 105 | // textNodesToRemoveから重複を削除 106 | textNodesToRemove = uniqBy(textNodesToRemove, 'id') 107 | 108 | console.log('textNodesToRemove', textNodesToRemove) 109 | 110 | // textNodesからtextNodesToRemoveにある要素を削除 111 | const filteredTextNodes = textNodes.filter(textNode => { 112 | return !textNodesToRemove.some( 113 | textNodeToRemove => textNodeToRemove.id === textNode.id, 114 | ) 115 | }) 116 | 117 | // filteredTextNodesを返す 118 | return filteredTextNodes 119 | } 120 | 121 | // targetTextRangeに応じてtextNodeを取得する関数 122 | export async function getTextNodes(options: { 123 | targetTextRange: TargetTextRange 124 | includeComponents: boolean 125 | includeInstances: boolean 126 | }) { 127 | console.log('getTextNodes', options.targetTextRange, options) 128 | 129 | // textNodeを格納する配列を用意 130 | let textNodes: TextNode[] = [] 131 | 132 | if (options.targetTextRange === 'currentPage') { 133 | // ページを読み込む 134 | await figma.currentPage.loadAsync() 135 | 136 | // targetTextRangeに応じてtextNodeを検索、配列に追加 137 | textNodes = figma.currentPage.findAllWithCriteria({ types: ['TEXT'] }) 138 | } else if (options.targetTextRange === 'allPages') { 139 | // 各ページごとに処理を実行 140 | for (const page of figma.root.children) { 141 | // ページを読み込む 142 | await page.loadAsync() 143 | 144 | // ページは以下にあるすべてのテキストをtextNodesに追加 145 | textNodes = [ 146 | ...textNodes, 147 | ...page.findAllWithCriteria({ types: ['TEXT'] }), 148 | ] 149 | } 150 | } else if (options.targetTextRange === 'selection') { 151 | // 何も選択していない場合は処理を終了 152 | if (figma.currentPage.selection.length === 0) { 153 | figma.notify(i18n.t('notifications.main.noSelections')) 154 | return textNodes 155 | } 156 | 157 | figma.currentPage.selection.forEach(node => { 158 | // 要素がテキストの場合、textNodesに追加 159 | if (node.type === 'TEXT') { 160 | textNodes.push(node) 161 | } 162 | 163 | // 要素がグループ、フレーム、コンポーネント、インスタンスなら、要素内のすべてのテキストをtextNodesに追加 164 | else if ( 165 | node.type === 'GROUP' || 166 | node.type === 'FRAME' || 167 | node.type === 'COMPONENT' || 168 | node.type === 'COMPONENT_SET' || 169 | node.type === 'INSTANCE' 170 | ) { 171 | textNodes = [ 172 | ...textNodes, 173 | ...node.findAllWithCriteria({ types: ['TEXT'] }), 174 | ] 175 | } 176 | 177 | // それ以外の場合は何もしない 178 | // else {} 179 | }) 180 | } 181 | 182 | // includeComponentsがfalse、またはincludeInstancesがfalseの場合、filterTextNodesを実行 183 | if (!options.includeComponents || !options.includeInstances) { 184 | textNodes = filterTextNodes(textNodes, { 185 | includeComponents: options.includeComponents, 186 | includeInstances: options.includeInstances, 187 | }) 188 | } 189 | 190 | return textNodes 191 | } 192 | -------------------------------------------------------------------------------- /src/ui/tabs/Utilities.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx h */ 2 | import { type JSX, h } from 'preact' 3 | 4 | import { 5 | Button, 6 | Checkbox, 7 | Container, 8 | Dropdown, 9 | type DropdownOption, 10 | Stack, 11 | Text, 12 | VerticalSpace, 13 | } from '@create-figma-plugin/ui' 14 | import { emit } from '@create-figma-plugin/utilities' 15 | import { useTranslation } from 'react-i18next' 16 | import { useMount, useUnmount } from 'react-use' 17 | 18 | import { useKeyValuesStore, useStore } from '@/ui/Store' 19 | import useOptions from '@/ui/hooks/useOptions' 20 | import useResizeWindow from '@/ui/hooks/useResizeWindow' 21 | 22 | import type { TargetTextRange } from '@/types/common' 23 | import type { 24 | ApplyValueHandler, 25 | HighlightTextHandler, 26 | RenameLayerHandler, 27 | } from '@/types/eventHandler' 28 | 29 | export default function Utilities() { 30 | const { t } = useTranslation() 31 | const options = useStore() 32 | const { keyValues } = useKeyValuesStore() 33 | const { updateOptions } = useOptions() 34 | const { resizeWindow } = useResizeWindow() 35 | 36 | const targetTextRangeDropdownOptions: DropdownOption[] & 37 | { 38 | value?: TargetTextRange 39 | }[] = [ 40 | { 41 | text: t('Utilities.targetTextRange.options.selection'), 42 | value: 'selection', 43 | }, 44 | { 45 | text: t('Utilities.targetTextRange.options.currentPage'), 46 | value: 'currentPage', 47 | }, 48 | { 49 | text: t('Utilities.targetTextRange.options.allPages'), 50 | value: 'allPages', 51 | }, 52 | ] 53 | 54 | function handleTextRangeDropdownChange( 55 | event: JSX.TargetedEvent, 56 | ) { 57 | updateOptions({ 58 | targetTextRange: event.currentTarget.value as TargetTextRange, 59 | }) 60 | } 61 | 62 | function handleCheckboxClick( 63 | option: 'includeComponents' | 'includeInstances', 64 | ) { 65 | return (event: JSX.TargetedEvent) => { 66 | console.log('handleCheckboxClick', option) 67 | 68 | if (option === 'includeComponents') { 69 | updateOptions({ 70 | includeComponents: event.currentTarget.checked, 71 | }) 72 | } else if (option === 'includeInstances') { 73 | updateOptions({ 74 | includeInstances: event.currentTarget.checked, 75 | }) 76 | } 77 | } 78 | } 79 | 80 | function handleActionClick( 81 | action: 'applyValue' | 'renameLayer' | 'highlightText', 82 | ) { 83 | return (event: JSX.TargetedEvent) => { 84 | console.log('handleActionClick', action) 85 | 86 | const actionOptions = { 87 | targetTextRange: options.targetTextRange, 88 | includeComponents: options.includeComponents, 89 | includeInstances: options.includeInstances, 90 | } 91 | 92 | if (action === 'applyValue') { 93 | emit('APPLY_VALUE', keyValues, actionOptions) 94 | } else if (action === 'renameLayer') { 95 | emit('RENAME_LAYER', keyValues, actionOptions) 96 | } else if (action === 'highlightText') { 97 | emit('HIGHLIGHT_TEXT', keyValues, actionOptions) 98 | } 99 | } 100 | } 101 | 102 | useMount(() => { 103 | console.log('Utilities mounted') 104 | resizeWindow() 105 | }) 106 | 107 | useUnmount(() => { 108 | console.log('Utilities unmounted') 109 | }) 110 | 111 | return ( 112 | 113 | 114 | 115 | 116 |
    117 | {t('Utilities.targetTextRange.label')} 118 | 124 |
    125 | 126 | 130 | {t('Utilities.includeComponents')} 131 | 132 | 133 | 137 | {t('Utilities.includeInstances')} 138 | 139 |
    140 | 141 | 142 | 143 | 144 |
    145 | 152 |

    153 | {t('Utilities.applyValueButton.description')} 154 |

    155 |
    156 | 157 |
    158 | 165 |

    166 | {t('Utilities.renameLayerButton.description')} 167 |

    168 |
    169 | 170 |
    171 | 178 |

    179 | {t('Utilities.highlightTextButton.description')} 180 |

    181 |
    182 |
    183 | 184 | 185 |
    186 | ) 187 | } 188 | -------------------------------------------------------------------------------- /src/ui/tabs/Fetch.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx h */ 2 | import { type JSX, h } from 'preact' 3 | import { useRef, useState } from 'preact/hooks' 4 | 5 | import { 6 | Button, 7 | Container, 8 | Stack, 9 | Textbox, 10 | VerticalSpace, 11 | } from '@create-figma-plugin/ui' 12 | import { emit } from '@create-figma-plugin/utilities' 13 | import { useTranslation } from 'react-i18next' 14 | import { useMount, useUnmount } from 'react-use' 15 | 16 | import { useKeyValuesStore, useStore } from '@/ui/Store' 17 | import useCache from '@/ui/hooks/useCache' 18 | import useNotion from '@/ui/hooks/useNotion' 19 | import useOptions from '@/ui/hooks/useOptions' 20 | import useResizeWindow from '@/ui/hooks/useResizeWindow' 21 | 22 | import type { NotionKeyValue, Options } from '@/types/common' 23 | import type { NotifyHandler } from '@/types/eventHandler' 24 | 25 | export default function Fetch() { 26 | const { t } = useTranslation() 27 | const options = useStore() 28 | const { keyValues } = useKeyValuesStore() 29 | const { updateOptions } = useOptions() 30 | const { resizeWindow } = useResizeWindow() 31 | const { fetchNotion } = useNotion() 32 | const { saveCacheToClientStorage } = useCache() 33 | const [fetching, setFetching] = useState(false) 34 | const keyValuesRef = useRef([]) 35 | 36 | function handleInput(key: keyof Options) { 37 | return (event: JSX.TargetedEvent) => { 38 | updateOptions({ 39 | [key]: event.currentTarget.value, 40 | }) 41 | } 42 | } 43 | 44 | async function handleFetchClick() { 45 | setFetching(true) 46 | 47 | emit('NOTIFY', { 48 | message: t('notifications.Fetch.loading'), 49 | }) 50 | 51 | // keyValuesRefをクリア 52 | keyValuesRef.current = [] 53 | 54 | await fetchNotion({ 55 | proxyUrl: process.env.PROXY_URL as string, 56 | integrationToken: options.integrationToken, 57 | databaseId: options.databaseId, 58 | keyPropertyName: options.keyPropertyName, 59 | valuePropertyName: options.valuePropertyName, 60 | keyValuesArray: keyValuesRef.current, 61 | }).catch((error: Error) => { 62 | emit('NOTIFY', { 63 | message: error.message, 64 | options: { 65 | error: true, 66 | }, 67 | }) 68 | setFetching(false) 69 | throw new Error(error.message) 70 | }) 71 | 72 | console.log('fetch done', keyValuesRef.current) 73 | 74 | // keyValuesをkeyValuesStoreに保存 75 | useKeyValuesStore.setState({ keyValues: keyValuesRef.current }) 76 | 77 | // keyValuesをclientStorageにキャッシュ 78 | saveCacheToClientStorage(keyValuesRef.current) 79 | 80 | setFetching(false) 81 | 82 | emit('NOTIFY', { 83 | message: t('notifications.Fetch.finish'), 84 | }) 85 | } 86 | 87 | function handleClearClick() { 88 | console.log('handleClearClick') 89 | 90 | // keyValuesStoreに空配列を入れる 91 | useKeyValuesStore.setState({ keyValues: [] }) 92 | 93 | // 空配列をclientStorageにキャッシュ 94 | saveCacheToClientStorage([]) 95 | 96 | emit('NOTIFY', { 97 | message: t('notifications.Fetch.clearCache'), 98 | }) 99 | } 100 | 101 | useMount(() => { 102 | console.log('Fetch mounted') 103 | resizeWindow() 104 | }) 105 | 106 | useUnmount(() => { 107 | console.log('Fetch unmounted') 108 | }) 109 | 110 | return ( 111 | 112 | 113 | 114 | 115 |
    116 |
    {t('Fetch.databaseId')}
    117 | 123 |
    124 | 125 |
    126 |
    {t('Fetch.integrationToken')}
    127 | 133 |
    134 | 135 |
    136 |
    {t('Fetch.keyPropertyName')}
    137 | 143 |
    144 | 145 |
    146 |
    {t('Fetch.valuePropertyName')}
    147 | 153 |
    154 |
    155 | 156 | 157 | 158 | 159 |
    160 | 174 |

    {t('Fetch.fetchButton.description')}

    175 |
    176 | 177 | 186 |
    187 | 188 | 189 |
    190 | ) 191 | } 192 | -------------------------------------------------------------------------------- /src/ui/tabs/List.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx h */ 2 | import { Fragment, type JSX, h } from 'preact' 3 | 4 | import { 5 | Button, 6 | Divider, 7 | Dropdown, 8 | type DropdownOption, 9 | Textbox, 10 | } from '@create-figma-plugin/ui' 11 | import { useTranslation } from 'react-i18next' 12 | import { useDebounce, useList, useMount, useUnmount } from 'react-use' 13 | 14 | import { useKeyValuesStore, useStore } from '@/ui/Store' 15 | import useOptions from '@/ui/hooks/useOptions' 16 | import useResizeWindow from '@/ui/hooks/useResizeWindow' 17 | 18 | import type { NotionKeyValue, SortOrder, SortValue } from '@/types/common' 19 | import KeyValueList from '@/ui/components/KeyValueList' 20 | 21 | export default function List() { 22 | const { t } = useTranslation() 23 | const options = useStore() 24 | const { keyValues } = useKeyValuesStore() 25 | const { updateOptions } = useOptions() 26 | const { resizeWindow } = useResizeWindow() 27 | const [rows, { filter, sort, reset }] = useList(keyValues) 28 | 29 | const sortValueOptions: DropdownOption[] & 30 | { 31 | value?: SortValue 32 | }[] = [ 33 | { 34 | text: t('List.sortValue.key'), 35 | value: 'key', 36 | }, 37 | { 38 | text: t('List.sortValue.value'), 39 | value: 'value', 40 | }, 41 | { 42 | text: t('List.sortValue.created_time'), 43 | value: 'created_time', 44 | }, 45 | { 46 | text: t('List.sortValue.last_edited_time'), 47 | value: 'last_edited_time', 48 | }, 49 | ] 50 | const sortOrderOptions: DropdownOption[] & 51 | { 52 | value?: SortOrder 53 | }[] = [ 54 | { 55 | text: t('List.sortOrder.ascending'), 56 | value: 'ascending', 57 | }, 58 | { 59 | text: t('List.sortOrder.descending'), 60 | value: 'descending', 61 | }, 62 | ] 63 | 64 | function filterList(filterString: string) { 65 | console.log('filterList', filterString) 66 | 67 | // リストをリセット 68 | reset() 69 | 70 | // filterStringがkeyもしくはvalue propertyを含んでいたらそれに絞り込む 71 | filter(rows => { 72 | const keyProperty = rows.key.toLowerCase() 73 | const valueProperty = rows.value.toLowerCase() 74 | return ( 75 | keyProperty.includes(filterString.toLowerCase()) || 76 | valueProperty.includes(filterString.toLowerCase()) 77 | ) 78 | }) 79 | } 80 | 81 | function sortList(sortValue: SortValue, sortOrder: SortOrder) { 82 | console.log('sortList', sortValue, sortOrder) 83 | 84 | sort((a, b) => { 85 | let comparison: number 86 | 87 | if (sortValue === 'created_time' || sortValue === 'last_edited_time') { 88 | // 日付フィールドの場合、Date オブジェクトに変換して比較 89 | const aDate = new Date(a[sortValue]).getTime() 90 | const bDate = new Date(b[sortValue]).getTime() 91 | comparison = aDate - bDate 92 | } else { 93 | // 文字列の場合は localeCompare を使用 94 | comparison = a[sortValue].localeCompare(b[sortValue]) 95 | } 96 | 97 | // ソート順に応じて結果を反転 98 | return sortOrder === 'ascending' ? comparison : -comparison 99 | }) 100 | } 101 | 102 | function handleFilterInput(event: JSX.TargetedEvent) { 103 | const inputValue = event.currentTarget.value 104 | console.log('handleFilterInput', inputValue) 105 | 106 | // stateを更新 107 | updateOptions({ 108 | filterString: inputValue, 109 | }) 110 | } 111 | 112 | function handleClearClick() { 113 | console.log('handleClearClick') 114 | updateOptions({ 115 | filterString: '', 116 | }) 117 | } 118 | 119 | function handleSortChange(key: 'sortValue' | 'sortOrder') { 120 | return (event: JSX.TargetedEvent) => { 121 | const inputValue = event.currentTarget.value 122 | console.log('handleSortChange', key, inputValue) 123 | 124 | // stateを更新 125 | updateOptions({ 126 | [key]: inputValue, 127 | }) 128 | } 129 | } 130 | 131 | function handleFetchClick() { 132 | console.log('handleFetchClick') 133 | updateOptions({ selectedTabKey: 'fetch' }) 134 | } 135 | 136 | useMount(() => { 137 | console.log('List mounted') 138 | resizeWindow() 139 | }) 140 | 141 | useUnmount(() => { 142 | console.log('List unmounted') 143 | }) 144 | 145 | // filterString | sortValue | sortOrderがアップデートされたらdebounceさせてから配列をフィルター&ソート 146 | useDebounce( 147 | () => { 148 | console.log( 149 | 'filterString or sortValue or sortOrder update (debounced)', 150 | options.filterString, 151 | options.sortValue, 152 | options.sortOrder, 153 | ) 154 | if (options.filterString) { 155 | filterList(options.filterString) 156 | } 157 | sortList(options.sortValue, options.sortOrder) 158 | }, 159 | 100, 160 | [options.filterString, options.sortValue, options.sortOrder], 161 | ) 162 | 163 | return ( 164 |
    165 | {keyValues.length > 0 ? ( 166 | 167 | {/* filter & sort */} 168 |
    169 | {/* filter */} 170 |
    171 | filter_list 172 | 173 |
    174 | 180 |
    181 | 182 | {/* clear button */} 183 | {options.filterString.length > 0 && ( 184 | 191 | )} 192 |
    193 | 194 | {/* sort */} 195 |
    196 | swap_vert 197 | 198 | 204 | 205 | 211 |
    212 |
    213 | 214 | 215 | 216 | {/* list */} 217 | 218 | 219 | 220 | 221 | {/* status bar */} 222 |
    223 |
    224 | highlight_mouse_cursor 225 | {t('List.statusBar.text')} 226 |
    227 | {t('List.statusBar.count', { length: rows.length })} 228 |
    229 |
    230 | ) : ( 231 | // empty 232 |
    233 | {t('List.empty.text')} 234 | 235 | 238 |
    239 | )} 240 |
    241 | ) 242 | } 243 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@create-figma-plugin/ui': 12 | specifier: 3.2.0 13 | version: 3.2.0(preact@10.23.2) 14 | '@create-figma-plugin/utilities': 15 | specifier: 3.2.0 16 | version: 3.2.0 17 | clsx: 18 | specifier: 2.1.1 19 | version: 2.1.1 20 | i18next: 21 | specifier: 23.14.0 22 | version: 23.14.0 23 | lodash: 24 | specifier: 4.17.21 25 | version: 4.17.21 26 | preact: 27 | specifier: 10.23.2 28 | version: 10.23.2 29 | query-string: 30 | specifier: 9.1.0 31 | version: 9.1.0 32 | react-i18next: 33 | specifier: 15.0.1 34 | version: 15.0.1(i18next@23.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 35 | react-use: 36 | specifier: 17.5.1 37 | version: 17.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 38 | zustand: 39 | specifier: 4.5.5 40 | version: 4.5.5(react@18.3.1) 41 | devDependencies: 42 | '@biomejs/biome': 43 | specifier: 1.8.3 44 | version: 1.8.3 45 | '@create-figma-plugin/build': 46 | specifier: 3.2.0 47 | version: 3.2.0(@figma/plugin-typings@1.98.0)(typescript@5.5.4) 48 | '@create-figma-plugin/tsconfig': 49 | specifier: 3.2.0 50 | version: 3.2.0 51 | '@figma/plugin-typings': 52 | specifier: 1.98.0 53 | version: 1.98.0 54 | '@types/lodash': 55 | specifier: 4.17.7 56 | version: 4.17.7 57 | '@types/node': 58 | specifier: 22.5.0 59 | version: 22.5.0 60 | concurrently: 61 | specifier: 8.2.2 62 | version: 8.2.2 63 | dotenv: 64 | specifier: 16.4.5 65 | version: 16.4.5 66 | husky: 67 | specifier: 9.1.5 68 | version: 9.1.5 69 | lint-staged: 70 | specifier: 15.2.9 71 | version: 15.2.9 72 | tailwindcss: 73 | specifier: 3.4.10 74 | version: 3.4.10 75 | typescript: 76 | specifier: 5.5.4 77 | version: 5.5.4 78 | 79 | packages: 80 | 81 | '@alloc/quick-lru@5.2.0': 82 | resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} 83 | engines: {node: '>=10'} 84 | 85 | '@babel/runtime@7.25.0': 86 | resolution: {integrity: sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==} 87 | engines: {node: '>=6.9.0'} 88 | 89 | '@biomejs/biome@1.8.3': 90 | resolution: {integrity: sha512-/uUV3MV+vyAczO+vKrPdOW0Iaet7UnJMU4bNMinggGJTAnBPjCoLEYcyYtYHNnUNYlv4xZMH6hVIQCAozq8d5w==} 91 | engines: {node: '>=14.21.3'} 92 | hasBin: true 93 | 94 | '@biomejs/cli-darwin-arm64@1.8.3': 95 | resolution: {integrity: sha512-9DYOjclFpKrH/m1Oz75SSExR8VKvNSSsLnVIqdnKexj6NwmiMlKk94Wa1kZEdv6MCOHGHgyyoV57Cw8WzL5n3A==} 96 | engines: {node: '>=14.21.3'} 97 | cpu: [arm64] 98 | os: [darwin] 99 | 100 | '@biomejs/cli-darwin-x64@1.8.3': 101 | resolution: {integrity: sha512-UeW44L/AtbmOF7KXLCoM+9PSgPo0IDcyEUfIoOXYeANaNXXf9mLUwV1GeF2OWjyic5zj6CnAJ9uzk2LT3v/wAw==} 102 | engines: {node: '>=14.21.3'} 103 | cpu: [x64] 104 | os: [darwin] 105 | 106 | '@biomejs/cli-linux-arm64-musl@1.8.3': 107 | resolution: {integrity: sha512-9yjUfOFN7wrYsXt/T/gEWfvVxKlnh3yBpnScw98IF+oOeCYb5/b/+K7YNqKROV2i1DlMjg9g/EcN9wvj+NkMuQ==} 108 | engines: {node: '>=14.21.3'} 109 | cpu: [arm64] 110 | os: [linux] 111 | 112 | '@biomejs/cli-linux-arm64@1.8.3': 113 | resolution: {integrity: sha512-fed2ji8s+I/m8upWpTJGanqiJ0rnlHOK3DdxsyVLZQ8ClY6qLuPc9uehCREBifRJLl/iJyQpHIRufLDeotsPtw==} 114 | engines: {node: '>=14.21.3'} 115 | cpu: [arm64] 116 | os: [linux] 117 | 118 | '@biomejs/cli-linux-x64-musl@1.8.3': 119 | resolution: {integrity: sha512-UHrGJX7PrKMKzPGoEsooKC9jXJMa28TUSMjcIlbDnIO4EAavCoVmNQaIuUSH0Ls2mpGMwUIf+aZJv657zfWWjA==} 120 | engines: {node: '>=14.21.3'} 121 | cpu: [x64] 122 | os: [linux] 123 | 124 | '@biomejs/cli-linux-x64@1.8.3': 125 | resolution: {integrity: sha512-I8G2QmuE1teISyT8ie1HXsjFRz9L1m5n83U1O6m30Kw+kPMPSKjag6QGUn+sXT8V+XWIZxFFBoTDEDZW2KPDDw==} 126 | engines: {node: '>=14.21.3'} 127 | cpu: [x64] 128 | os: [linux] 129 | 130 | '@biomejs/cli-win32-arm64@1.8.3': 131 | resolution: {integrity: sha512-J+Hu9WvrBevfy06eU1Na0lpc7uR9tibm9maHynLIoAjLZpQU3IW+OKHUtyL8p6/3pT2Ju5t5emReeIS2SAxhkQ==} 132 | engines: {node: '>=14.21.3'} 133 | cpu: [arm64] 134 | os: [win32] 135 | 136 | '@biomejs/cli-win32-x64@1.8.3': 137 | resolution: {integrity: sha512-/PJ59vA1pnQeKahemaQf4Nyj7IKUvGQSc3Ze1uIGi+Wvr1xF7rGobSrAAG01T/gUDG21vkDsZYM03NAmPiVkqg==} 138 | engines: {node: '>=14.21.3'} 139 | cpu: [x64] 140 | os: [win32] 141 | 142 | '@create-figma-plugin/build@3.2.0': 143 | resolution: {integrity: sha512-OX2XImYDhSMgtJ5Rqr077miQqautDUNEm2E3nLof8l4wxEo5QaMC4RBewnBe4yPt6pWIbJjuzhjyhLytH/aLiw==} 144 | engines: {node: '>=20'} 145 | hasBin: true 146 | peerDependencies: 147 | '@figma/plugin-typings': '>=1' 148 | typescript: '>=4' 149 | 150 | '@create-figma-plugin/common@3.2.0': 151 | resolution: {integrity: sha512-cT0eK9j3aRH0w8Nh3Ii0WM68moLyLd+erg0SzseL+RQef/DIKl+E9ReRRVlBjYrHH5t31LlGO9Pi2rU8C/ck1g==} 152 | engines: {node: '>=20'} 153 | 154 | '@create-figma-plugin/tsconfig@3.2.0': 155 | resolution: {integrity: sha512-gdhq2uzDbzu/PMoWwKtA3EmVucKkqkCiQg1esJeyF3u/EtzYXoucAHazB9kXmzvf8UlZW1FS9QI3MQuuIzy8jg==} 156 | 157 | '@create-figma-plugin/ui@3.2.0': 158 | resolution: {integrity: sha512-E0is67aaoVECDgSMAlkp584wI24yglENH6M4OIJwDMbHBXidIbtAk9RIbUDr0gC+2NWNRLpIyFQH6LnZcqO+0A==} 159 | engines: {node: '>=20'} 160 | peerDependencies: 161 | preact: '>=10' 162 | 163 | '@create-figma-plugin/utilities@3.2.0': 164 | resolution: {integrity: sha512-+5JddDqI6XjVJs0EqgLd+6B9ukjhHFEBesqPI/e0okqS+Ay7Gt3xsD0vMrZjVYfJcmRAprgJhdGBGJZjOZiQIA==} 165 | engines: {node: '>=20'} 166 | 167 | '@esbuild/aix-ppc64@0.20.2': 168 | resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} 169 | engines: {node: '>=12'} 170 | cpu: [ppc64] 171 | os: [aix] 172 | 173 | '@esbuild/android-arm64@0.20.2': 174 | resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} 175 | engines: {node: '>=12'} 176 | cpu: [arm64] 177 | os: [android] 178 | 179 | '@esbuild/android-arm@0.20.2': 180 | resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} 181 | engines: {node: '>=12'} 182 | cpu: [arm] 183 | os: [android] 184 | 185 | '@esbuild/android-x64@0.20.2': 186 | resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} 187 | engines: {node: '>=12'} 188 | cpu: [x64] 189 | os: [android] 190 | 191 | '@esbuild/darwin-arm64@0.20.2': 192 | resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} 193 | engines: {node: '>=12'} 194 | cpu: [arm64] 195 | os: [darwin] 196 | 197 | '@esbuild/darwin-x64@0.20.2': 198 | resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} 199 | engines: {node: '>=12'} 200 | cpu: [x64] 201 | os: [darwin] 202 | 203 | '@esbuild/freebsd-arm64@0.20.2': 204 | resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} 205 | engines: {node: '>=12'} 206 | cpu: [arm64] 207 | os: [freebsd] 208 | 209 | '@esbuild/freebsd-x64@0.20.2': 210 | resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} 211 | engines: {node: '>=12'} 212 | cpu: [x64] 213 | os: [freebsd] 214 | 215 | '@esbuild/linux-arm64@0.20.2': 216 | resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} 217 | engines: {node: '>=12'} 218 | cpu: [arm64] 219 | os: [linux] 220 | 221 | '@esbuild/linux-arm@0.20.2': 222 | resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} 223 | engines: {node: '>=12'} 224 | cpu: [arm] 225 | os: [linux] 226 | 227 | '@esbuild/linux-ia32@0.20.2': 228 | resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} 229 | engines: {node: '>=12'} 230 | cpu: [ia32] 231 | os: [linux] 232 | 233 | '@esbuild/linux-loong64@0.20.2': 234 | resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} 235 | engines: {node: '>=12'} 236 | cpu: [loong64] 237 | os: [linux] 238 | 239 | '@esbuild/linux-mips64el@0.20.2': 240 | resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} 241 | engines: {node: '>=12'} 242 | cpu: [mips64el] 243 | os: [linux] 244 | 245 | '@esbuild/linux-ppc64@0.20.2': 246 | resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} 247 | engines: {node: '>=12'} 248 | cpu: [ppc64] 249 | os: [linux] 250 | 251 | '@esbuild/linux-riscv64@0.20.2': 252 | resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} 253 | engines: {node: '>=12'} 254 | cpu: [riscv64] 255 | os: [linux] 256 | 257 | '@esbuild/linux-s390x@0.20.2': 258 | resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} 259 | engines: {node: '>=12'} 260 | cpu: [s390x] 261 | os: [linux] 262 | 263 | '@esbuild/linux-x64@0.20.2': 264 | resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} 265 | engines: {node: '>=12'} 266 | cpu: [x64] 267 | os: [linux] 268 | 269 | '@esbuild/netbsd-x64@0.20.2': 270 | resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} 271 | engines: {node: '>=12'} 272 | cpu: [x64] 273 | os: [netbsd] 274 | 275 | '@esbuild/openbsd-x64@0.20.2': 276 | resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} 277 | engines: {node: '>=12'} 278 | cpu: [x64] 279 | os: [openbsd] 280 | 281 | '@esbuild/sunos-x64@0.20.2': 282 | resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} 283 | engines: {node: '>=12'} 284 | cpu: [x64] 285 | os: [sunos] 286 | 287 | '@esbuild/win32-arm64@0.20.2': 288 | resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} 289 | engines: {node: '>=12'} 290 | cpu: [arm64] 291 | os: [win32] 292 | 293 | '@esbuild/win32-ia32@0.20.2': 294 | resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} 295 | engines: {node: '>=12'} 296 | cpu: [ia32] 297 | os: [win32] 298 | 299 | '@esbuild/win32-x64@0.20.2': 300 | resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} 301 | engines: {node: '>=12'} 302 | cpu: [x64] 303 | os: [win32] 304 | 305 | '@figma/plugin-typings@1.98.0': 306 | resolution: {integrity: sha512-Bx5pBRf8XWeAFbDtKfdiUEM/UkCHyl4baOGb+7OPM0ozrzg5JNDH/BsrGA4EpuJKkS4uRmV525q3g067OX6VHg==} 307 | 308 | '@isaacs/cliui@8.0.2': 309 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 310 | engines: {node: '>=12'} 311 | 312 | '@jridgewell/gen-mapping@0.3.5': 313 | resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} 314 | engines: {node: '>=6.0.0'} 315 | 316 | '@jridgewell/resolve-uri@3.1.2': 317 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 318 | engines: {node: '>=6.0.0'} 319 | 320 | '@jridgewell/set-array@1.2.1': 321 | resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} 322 | engines: {node: '>=6.0.0'} 323 | 324 | '@jridgewell/sourcemap-codec@1.5.0': 325 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 326 | 327 | '@jridgewell/trace-mapping@0.3.25': 328 | resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} 329 | 330 | '@nodelib/fs.scandir@2.1.5': 331 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 332 | engines: {node: '>= 8'} 333 | 334 | '@nodelib/fs.stat@2.0.5': 335 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 336 | engines: {node: '>= 8'} 337 | 338 | '@nodelib/fs.walk@1.2.8': 339 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 340 | engines: {node: '>= 8'} 341 | 342 | '@pkgjs/parseargs@0.11.0': 343 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} 344 | engines: {node: '>=14'} 345 | 346 | '@sindresorhus/merge-streams@2.3.0': 347 | resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} 348 | engines: {node: '>=18'} 349 | 350 | '@sindresorhus/slugify@2.2.1': 351 | resolution: {integrity: sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==} 352 | engines: {node: '>=12'} 353 | 354 | '@sindresorhus/transliterate@1.6.0': 355 | resolution: {integrity: sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ==} 356 | engines: {node: '>=12'} 357 | 358 | '@trysound/sax@0.2.0': 359 | resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} 360 | engines: {node: '>=10.13.0'} 361 | 362 | '@types/js-cookie@2.2.7': 363 | resolution: {integrity: sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==} 364 | 365 | '@types/lodash@4.17.7': 366 | resolution: {integrity: sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==} 367 | 368 | '@types/node@22.5.0': 369 | resolution: {integrity: sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==} 370 | 371 | '@xobotyi/scrollbar-width@1.9.5': 372 | resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==} 373 | 374 | ansi-escapes@7.0.0: 375 | resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} 376 | engines: {node: '>=18'} 377 | 378 | ansi-regex@5.0.1: 379 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 380 | engines: {node: '>=8'} 381 | 382 | ansi-regex@6.0.1: 383 | resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} 384 | engines: {node: '>=12'} 385 | 386 | ansi-styles@4.3.0: 387 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 388 | engines: {node: '>=8'} 389 | 390 | ansi-styles@6.2.1: 391 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} 392 | engines: {node: '>=12'} 393 | 394 | any-promise@1.3.0: 395 | resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} 396 | 397 | anymatch@3.1.3: 398 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 399 | engines: {node: '>= 8'} 400 | 401 | arg@5.0.2: 402 | resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} 403 | 404 | balanced-match@1.0.2: 405 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 406 | 407 | binary-extensions@2.3.0: 408 | resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} 409 | engines: {node: '>=8'} 410 | 411 | boolbase@1.0.0: 412 | resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} 413 | 414 | brace-expansion@2.0.1: 415 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 416 | 417 | braces@3.0.3: 418 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 419 | engines: {node: '>=8'} 420 | 421 | browserslist@4.23.3: 422 | resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==} 423 | engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} 424 | hasBin: true 425 | 426 | camelcase-css@2.0.1: 427 | resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} 428 | engines: {node: '>= 6'} 429 | 430 | camelcase@6.3.0: 431 | resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} 432 | engines: {node: '>=10'} 433 | 434 | caniuse-api@3.0.0: 435 | resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} 436 | 437 | caniuse-lite@1.0.30001651: 438 | resolution: {integrity: sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==} 439 | 440 | chalk@4.1.2: 441 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 442 | engines: {node: '>=10'} 443 | 444 | chalk@5.3.0: 445 | resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} 446 | engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} 447 | 448 | chokidar@3.6.0: 449 | resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} 450 | engines: {node: '>= 8.10.0'} 451 | 452 | cli-cursor@5.0.0: 453 | resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} 454 | engines: {node: '>=18'} 455 | 456 | cli-truncate@4.0.0: 457 | resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} 458 | engines: {node: '>=18'} 459 | 460 | cliui@8.0.1: 461 | resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} 462 | engines: {node: '>=12'} 463 | 464 | clsx@2.1.1: 465 | resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} 466 | engines: {node: '>=6'} 467 | 468 | color-convert@2.0.1: 469 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 470 | engines: {node: '>=7.0.0'} 471 | 472 | color-name@1.1.4: 473 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 474 | 475 | colord@2.9.3: 476 | resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} 477 | 478 | colorette@2.0.20: 479 | resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} 480 | 481 | commander@12.1.0: 482 | resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} 483 | engines: {node: '>=18'} 484 | 485 | commander@4.1.1: 486 | resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} 487 | engines: {node: '>= 6'} 488 | 489 | commander@7.2.0: 490 | resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} 491 | engines: {node: '>= 10'} 492 | 493 | concurrently@8.2.2: 494 | resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} 495 | engines: {node: ^14.13.0 || >=16.0.0} 496 | hasBin: true 497 | 498 | copy-to-clipboard@3.3.3: 499 | resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} 500 | 501 | cross-spawn@7.0.3: 502 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} 503 | engines: {node: '>= 8'} 504 | 505 | crypto-random-string@4.0.0: 506 | resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} 507 | engines: {node: '>=12'} 508 | 509 | css-declaration-sorter@7.2.0: 510 | resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==} 511 | engines: {node: ^14 || ^16 || >=18} 512 | peerDependencies: 513 | postcss: ^8.0.9 514 | 515 | css-in-js-utils@3.1.0: 516 | resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==} 517 | 518 | css-select@5.1.0: 519 | resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} 520 | 521 | css-tree@1.1.3: 522 | resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} 523 | engines: {node: '>=8.0.0'} 524 | 525 | css-tree@2.2.1: 526 | resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} 527 | engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} 528 | 529 | css-tree@2.3.1: 530 | resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} 531 | engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} 532 | 533 | css-what@6.1.0: 534 | resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} 535 | engines: {node: '>= 6'} 536 | 537 | cssesc@3.0.0: 538 | resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} 539 | engines: {node: '>=4'} 540 | hasBin: true 541 | 542 | cssnano-preset-default@6.1.2: 543 | resolution: {integrity: sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==} 544 | engines: {node: ^14 || ^16 || >=18.0} 545 | peerDependencies: 546 | postcss: ^8.4.31 547 | 548 | cssnano-utils@4.0.2: 549 | resolution: {integrity: sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==} 550 | engines: {node: ^14 || ^16 || >=18.0} 551 | peerDependencies: 552 | postcss: ^8.4.31 553 | 554 | cssnano@6.1.2: 555 | resolution: {integrity: sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==} 556 | engines: {node: ^14 || ^16 || >=18.0} 557 | peerDependencies: 558 | postcss: ^8.4.31 559 | 560 | csso@5.0.5: 561 | resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} 562 | engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} 563 | 564 | csstype@3.1.3: 565 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 566 | 567 | date-fns@2.30.0: 568 | resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} 569 | engines: {node: '>=0.11'} 570 | 571 | debug@4.3.6: 572 | resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} 573 | engines: {node: '>=6.0'} 574 | peerDependencies: 575 | supports-color: '*' 576 | peerDependenciesMeta: 577 | supports-color: 578 | optional: true 579 | 580 | decode-uri-component@0.4.1: 581 | resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==} 582 | engines: {node: '>=14.16'} 583 | 584 | didyoumean@1.2.2: 585 | resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} 586 | 587 | dlv@1.1.3: 588 | resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} 589 | 590 | dom-serializer@2.0.0: 591 | resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} 592 | 593 | domelementtype@2.3.0: 594 | resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} 595 | 596 | domhandler@5.0.3: 597 | resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} 598 | engines: {node: '>= 4'} 599 | 600 | domutils@3.1.0: 601 | resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} 602 | 603 | dotenv@16.4.5: 604 | resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} 605 | engines: {node: '>=12'} 606 | 607 | eastasianwidth@0.2.0: 608 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 609 | 610 | electron-to-chromium@1.5.8: 611 | resolution: {integrity: sha512-4Nx0gP2tPNBLTrFxBMHpkQbtn2hidPVr/+/FTtcCiBYTucqc70zRyVZiOLj17Ui3wTO7SQ1/N+hkHYzJjBzt6A==} 612 | 613 | emoji-regex@10.3.0: 614 | resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} 615 | 616 | emoji-regex@8.0.0: 617 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 618 | 619 | emoji-regex@9.2.2: 620 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 621 | 622 | entities@4.5.0: 623 | resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} 624 | engines: {node: '>=0.12'} 625 | 626 | environment@1.1.0: 627 | resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} 628 | engines: {node: '>=18'} 629 | 630 | error-stack-parser@2.1.4: 631 | resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} 632 | 633 | esbuild@0.20.2: 634 | resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} 635 | engines: {node: '>=12'} 636 | hasBin: true 637 | 638 | escalade@3.1.2: 639 | resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} 640 | engines: {node: '>=6'} 641 | 642 | escape-string-regexp@5.0.0: 643 | resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} 644 | engines: {node: '>=12'} 645 | 646 | eventemitter3@5.0.1: 647 | resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} 648 | 649 | execa@8.0.1: 650 | resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} 651 | engines: {node: '>=16.17'} 652 | 653 | fast-deep-equal@3.1.3: 654 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 655 | 656 | fast-glob@3.3.2: 657 | resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} 658 | engines: {node: '>=8.6.0'} 659 | 660 | fast-shallow-equal@1.0.0: 661 | resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==} 662 | 663 | fastest-stable-stringify@2.0.2: 664 | resolution: {integrity: sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==} 665 | 666 | fastq@1.17.1: 667 | resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} 668 | 669 | fill-range@7.1.1: 670 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 671 | engines: {node: '>=8'} 672 | 673 | filter-obj@5.1.0: 674 | resolution: {integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==} 675 | engines: {node: '>=14.16'} 676 | 677 | find-up@7.0.0: 678 | resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} 679 | engines: {node: '>=18'} 680 | 681 | foreground-child@3.3.0: 682 | resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} 683 | engines: {node: '>=14'} 684 | 685 | fsevents@2.3.3: 686 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 687 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 688 | os: [darwin] 689 | 690 | function-bind@1.1.2: 691 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 692 | 693 | generic-names@4.0.0: 694 | resolution: {integrity: sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==} 695 | 696 | get-caller-file@2.0.5: 697 | resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} 698 | engines: {node: 6.* || 8.* || >= 10.*} 699 | 700 | get-east-asian-width@1.2.0: 701 | resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} 702 | engines: {node: '>=18'} 703 | 704 | get-stream@8.0.1: 705 | resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} 706 | engines: {node: '>=16'} 707 | 708 | glob-parent@5.1.2: 709 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 710 | engines: {node: '>= 6'} 711 | 712 | glob-parent@6.0.2: 713 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 714 | engines: {node: '>=10.13.0'} 715 | 716 | glob@10.4.5: 717 | resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} 718 | hasBin: true 719 | 720 | globby@14.0.2: 721 | resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==} 722 | engines: {node: '>=18'} 723 | 724 | graceful-fs@4.2.11: 725 | resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 726 | 727 | has-flag@4.0.0: 728 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 729 | engines: {node: '>=8'} 730 | 731 | hasown@2.0.2: 732 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 733 | engines: {node: '>= 0.4'} 734 | 735 | hex-rgb@5.0.0: 736 | resolution: {integrity: sha512-NQO+lgVUCtHxZ792FodgW0zflK+ozS9X9dwGp9XvvmPlH7pyxd588cn24TD3rmPm/N0AIRXF10Otah8yKqGw4w==} 737 | engines: {node: '>=12'} 738 | 739 | html-parse-stringify@3.0.1: 740 | resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} 741 | 742 | human-signals@5.0.0: 743 | resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} 744 | engines: {node: '>=16.17.0'} 745 | 746 | husky@9.1.5: 747 | resolution: {integrity: sha512-rowAVRUBfI0b4+niA4SJMhfQwc107VLkBUgEYYAOQAbqDCnra1nYh83hF/MDmhYs9t9n1E3DuKOrs2LYNC+0Ag==} 748 | engines: {node: '>=18'} 749 | hasBin: true 750 | 751 | hyphenate-style-name@1.1.0: 752 | resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} 753 | 754 | i18next@23.14.0: 755 | resolution: {integrity: sha512-Y5GL4OdA8IU2geRrt2+Uc1iIhsjICdHZzT9tNwQ3TVqdNzgxHToGCKf/TPRP80vTCAP6svg2WbbJL+Gx5MFQVA==} 756 | 757 | icss-replace-symbols@1.1.0: 758 | resolution: {integrity: sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==} 759 | 760 | icss-utils@5.1.0: 761 | resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} 762 | engines: {node: ^10 || ^12 || >= 14} 763 | peerDependencies: 764 | postcss: ^8.1.0 765 | 766 | ignore@5.3.2: 767 | resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} 768 | engines: {node: '>= 4'} 769 | 770 | indent-string@5.0.0: 771 | resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} 772 | engines: {node: '>=12'} 773 | 774 | inline-style-prefixer@7.0.1: 775 | resolution: {integrity: sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==} 776 | 777 | is-binary-path@2.1.0: 778 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 779 | engines: {node: '>=8'} 780 | 781 | is-core-module@2.15.0: 782 | resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==} 783 | engines: {node: '>= 0.4'} 784 | 785 | is-extglob@2.1.1: 786 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 787 | engines: {node: '>=0.10.0'} 788 | 789 | is-fullwidth-code-point@3.0.0: 790 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 791 | engines: {node: '>=8'} 792 | 793 | is-fullwidth-code-point@4.0.0: 794 | resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} 795 | engines: {node: '>=12'} 796 | 797 | is-fullwidth-code-point@5.0.0: 798 | resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} 799 | engines: {node: '>=18'} 800 | 801 | is-glob@4.0.3: 802 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 803 | engines: {node: '>=0.10.0'} 804 | 805 | is-number@7.0.0: 806 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 807 | engines: {node: '>=0.12.0'} 808 | 809 | is-stream@2.0.1: 810 | resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} 811 | engines: {node: '>=8'} 812 | 813 | is-stream@3.0.0: 814 | resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} 815 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 816 | 817 | is-there@4.5.1: 818 | resolution: {integrity: sha512-vIZ7HTXAoRoIwYSsTnxb0sg9L6rth+JOulNcavsbskQkCIWoSM2cjFOWZs4wGziGZER+Xgs/HXiCQZgiL8ppxQ==} 819 | 820 | isexe@2.0.0: 821 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 822 | 823 | jackspeak@3.4.3: 824 | resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} 825 | 826 | jiti@1.21.6: 827 | resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} 828 | hasBin: true 829 | 830 | js-cookie@2.2.1: 831 | resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==} 832 | 833 | js-tokens@4.0.0: 834 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 835 | 836 | kleur@4.1.5: 837 | resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} 838 | engines: {node: '>=6'} 839 | 840 | lilconfig@2.1.0: 841 | resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} 842 | engines: {node: '>=10'} 843 | 844 | lilconfig@3.1.2: 845 | resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} 846 | engines: {node: '>=14'} 847 | 848 | lines-and-columns@1.2.4: 849 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} 850 | 851 | lint-staged@15.2.9: 852 | resolution: {integrity: sha512-BZAt8Lk3sEnxw7tfxM7jeZlPRuT4M68O0/CwZhhaw6eeWu0Lz5eERE3m386InivXB64fp/mDID452h48tvKlRQ==} 853 | engines: {node: '>=18.12.0'} 854 | hasBin: true 855 | 856 | listr2@8.2.4: 857 | resolution: {integrity: sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==} 858 | engines: {node: '>=18.0.0'} 859 | 860 | loader-utils@3.3.1: 861 | resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} 862 | engines: {node: '>= 12.13.0'} 863 | 864 | locate-path@7.2.0: 865 | resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} 866 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 867 | 868 | lodash.camelcase@4.3.0: 869 | resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} 870 | 871 | lodash.memoize@4.1.2: 872 | resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} 873 | 874 | lodash.uniq@4.5.0: 875 | resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} 876 | 877 | lodash@4.17.21: 878 | resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} 879 | 880 | log-update@6.1.0: 881 | resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} 882 | engines: {node: '>=18'} 883 | 884 | loose-envify@1.4.0: 885 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} 886 | hasBin: true 887 | 888 | lru-cache@10.4.3: 889 | resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} 890 | 891 | mdn-data@2.0.14: 892 | resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} 893 | 894 | mdn-data@2.0.28: 895 | resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} 896 | 897 | mdn-data@2.0.30: 898 | resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} 899 | 900 | merge-stream@2.0.0: 901 | resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} 902 | 903 | merge2@1.4.1: 904 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 905 | engines: {node: '>= 8'} 906 | 907 | micromatch@4.0.7: 908 | resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} 909 | engines: {node: '>=8.6'} 910 | 911 | mimic-fn@4.0.0: 912 | resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} 913 | engines: {node: '>=12'} 914 | 915 | mimic-function@5.0.1: 916 | resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} 917 | engines: {node: '>=18'} 918 | 919 | minimatch@9.0.5: 920 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 921 | engines: {node: '>=16 || 14 >=14.17'} 922 | 923 | minipass@7.1.2: 924 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 925 | engines: {node: '>=16 || 14 >=14.17'} 926 | 927 | mkdirp@3.0.1: 928 | resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} 929 | engines: {node: '>=10'} 930 | hasBin: true 931 | 932 | mri@1.2.0: 933 | resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} 934 | engines: {node: '>=4'} 935 | 936 | ms@2.1.2: 937 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 938 | 939 | mz@2.7.0: 940 | resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} 941 | 942 | nano-css@5.6.2: 943 | resolution: {integrity: sha512-+6bHaC8dSDGALM1HJjOHVXpuastdu2xFoZlC77Jh4cg+33Zcgm+Gxd+1xsnpZK14eyHObSp82+ll5y3SX75liw==} 944 | peerDependencies: 945 | react: '*' 946 | react-dom: '*' 947 | 948 | nanoid@3.3.7: 949 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} 950 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 951 | hasBin: true 952 | 953 | natural-compare-lite@1.4.0: 954 | resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} 955 | 956 | node-releases@2.0.18: 957 | resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} 958 | 959 | normalize-path@3.0.0: 960 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 961 | engines: {node: '>=0.10.0'} 962 | 963 | npm-run-path@5.3.0: 964 | resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} 965 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 966 | 967 | nth-check@2.1.1: 968 | resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} 969 | 970 | object-assign@4.1.1: 971 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 972 | engines: {node: '>=0.10.0'} 973 | 974 | object-hash@3.0.0: 975 | resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} 976 | engines: {node: '>= 6'} 977 | 978 | onetime@6.0.0: 979 | resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} 980 | engines: {node: '>=12'} 981 | 982 | onetime@7.0.0: 983 | resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} 984 | engines: {node: '>=18'} 985 | 986 | p-limit@4.0.0: 987 | resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} 988 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 989 | 990 | p-locate@6.0.0: 991 | resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} 992 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 993 | 994 | package-json-from-dist@1.0.0: 995 | resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} 996 | 997 | path-exists@5.0.0: 998 | resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} 999 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 1000 | 1001 | path-key@3.1.1: 1002 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 1003 | engines: {node: '>=8'} 1004 | 1005 | path-key@4.0.0: 1006 | resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} 1007 | engines: {node: '>=12'} 1008 | 1009 | path-parse@1.0.7: 1010 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 1011 | 1012 | path-scurry@1.11.1: 1013 | resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} 1014 | engines: {node: '>=16 || 14 >=14.18'} 1015 | 1016 | path-type@5.0.0: 1017 | resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} 1018 | engines: {node: '>=12'} 1019 | 1020 | picocolors@1.0.1: 1021 | resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} 1022 | 1023 | picomatch@2.3.1: 1024 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 1025 | engines: {node: '>=8.6'} 1026 | 1027 | pidtree@0.6.0: 1028 | resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} 1029 | engines: {node: '>=0.10'} 1030 | hasBin: true 1031 | 1032 | pify@2.3.0: 1033 | resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} 1034 | engines: {node: '>=0.10.0'} 1035 | 1036 | pirates@4.0.6: 1037 | resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} 1038 | engines: {node: '>= 6'} 1039 | 1040 | postcss-calc@9.0.1: 1041 | resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==} 1042 | engines: {node: ^14 || ^16 || >=18.0} 1043 | peerDependencies: 1044 | postcss: ^8.2.2 1045 | 1046 | postcss-colormin@6.1.0: 1047 | resolution: {integrity: sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==} 1048 | engines: {node: ^14 || ^16 || >=18.0} 1049 | peerDependencies: 1050 | postcss: ^8.4.31 1051 | 1052 | postcss-convert-values@6.1.0: 1053 | resolution: {integrity: sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==} 1054 | engines: {node: ^14 || ^16 || >=18.0} 1055 | peerDependencies: 1056 | postcss: ^8.4.31 1057 | 1058 | postcss-discard-comments@6.0.2: 1059 | resolution: {integrity: sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==} 1060 | engines: {node: ^14 || ^16 || >=18.0} 1061 | peerDependencies: 1062 | postcss: ^8.4.31 1063 | 1064 | postcss-discard-duplicates@6.0.3: 1065 | resolution: {integrity: sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==} 1066 | engines: {node: ^14 || ^16 || >=18.0} 1067 | peerDependencies: 1068 | postcss: ^8.4.31 1069 | 1070 | postcss-discard-empty@6.0.3: 1071 | resolution: {integrity: sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==} 1072 | engines: {node: ^14 || ^16 || >=18.0} 1073 | peerDependencies: 1074 | postcss: ^8.4.31 1075 | 1076 | postcss-discard-overridden@6.0.2: 1077 | resolution: {integrity: sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==} 1078 | engines: {node: ^14 || ^16 || >=18.0} 1079 | peerDependencies: 1080 | postcss: ^8.4.31 1081 | 1082 | postcss-import@15.1.0: 1083 | resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} 1084 | engines: {node: '>=14.0.0'} 1085 | peerDependencies: 1086 | postcss: ^8.0.0 1087 | 1088 | postcss-js@4.0.1: 1089 | resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} 1090 | engines: {node: ^12 || ^14 || >= 16} 1091 | peerDependencies: 1092 | postcss: ^8.4.21 1093 | 1094 | postcss-load-config@4.0.2: 1095 | resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} 1096 | engines: {node: '>= 14'} 1097 | peerDependencies: 1098 | postcss: '>=8.0.9' 1099 | ts-node: '>=9.0.0' 1100 | peerDependenciesMeta: 1101 | postcss: 1102 | optional: true 1103 | ts-node: 1104 | optional: true 1105 | 1106 | postcss-merge-longhand@6.0.5: 1107 | resolution: {integrity: sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==} 1108 | engines: {node: ^14 || ^16 || >=18.0} 1109 | peerDependencies: 1110 | postcss: ^8.4.31 1111 | 1112 | postcss-merge-rules@6.1.1: 1113 | resolution: {integrity: sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==} 1114 | engines: {node: ^14 || ^16 || >=18.0} 1115 | peerDependencies: 1116 | postcss: ^8.4.31 1117 | 1118 | postcss-minify-font-values@6.1.0: 1119 | resolution: {integrity: sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==} 1120 | engines: {node: ^14 || ^16 || >=18.0} 1121 | peerDependencies: 1122 | postcss: ^8.4.31 1123 | 1124 | postcss-minify-gradients@6.0.3: 1125 | resolution: {integrity: sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==} 1126 | engines: {node: ^14 || ^16 || >=18.0} 1127 | peerDependencies: 1128 | postcss: ^8.4.31 1129 | 1130 | postcss-minify-params@6.1.0: 1131 | resolution: {integrity: sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==} 1132 | engines: {node: ^14 || ^16 || >=18.0} 1133 | peerDependencies: 1134 | postcss: ^8.4.31 1135 | 1136 | postcss-minify-selectors@6.0.4: 1137 | resolution: {integrity: sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==} 1138 | engines: {node: ^14 || ^16 || >=18.0} 1139 | peerDependencies: 1140 | postcss: ^8.4.31 1141 | 1142 | postcss-modules-extract-imports@3.1.0: 1143 | resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} 1144 | engines: {node: ^10 || ^12 || >= 14} 1145 | peerDependencies: 1146 | postcss: ^8.1.0 1147 | 1148 | postcss-modules-local-by-default@4.0.5: 1149 | resolution: {integrity: sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==} 1150 | engines: {node: ^10 || ^12 || >= 14} 1151 | peerDependencies: 1152 | postcss: ^8.1.0 1153 | 1154 | postcss-modules-scope@3.2.0: 1155 | resolution: {integrity: sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==} 1156 | engines: {node: ^10 || ^12 || >= 14} 1157 | peerDependencies: 1158 | postcss: ^8.1.0 1159 | 1160 | postcss-modules-values@4.0.0: 1161 | resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} 1162 | engines: {node: ^10 || ^12 || >= 14} 1163 | peerDependencies: 1164 | postcss: ^8.1.0 1165 | 1166 | postcss-modules@6.0.0: 1167 | resolution: {integrity: sha512-7DGfnlyi/ju82BRzTIjWS5C4Tafmzl3R79YP/PASiocj+aa6yYphHhhKUOEoXQToId5rgyFgJ88+ccOUydjBXQ==} 1168 | peerDependencies: 1169 | postcss: ^8.0.0 1170 | 1171 | postcss-nested@6.2.0: 1172 | resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} 1173 | engines: {node: '>=12.0'} 1174 | peerDependencies: 1175 | postcss: ^8.2.14 1176 | 1177 | postcss-normalize-charset@6.0.2: 1178 | resolution: {integrity: sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==} 1179 | engines: {node: ^14 || ^16 || >=18.0} 1180 | peerDependencies: 1181 | postcss: ^8.4.31 1182 | 1183 | postcss-normalize-display-values@6.0.2: 1184 | resolution: {integrity: sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==} 1185 | engines: {node: ^14 || ^16 || >=18.0} 1186 | peerDependencies: 1187 | postcss: ^8.4.31 1188 | 1189 | postcss-normalize-positions@6.0.2: 1190 | resolution: {integrity: sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==} 1191 | engines: {node: ^14 || ^16 || >=18.0} 1192 | peerDependencies: 1193 | postcss: ^8.4.31 1194 | 1195 | postcss-normalize-repeat-style@6.0.2: 1196 | resolution: {integrity: sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==} 1197 | engines: {node: ^14 || ^16 || >=18.0} 1198 | peerDependencies: 1199 | postcss: ^8.4.31 1200 | 1201 | postcss-normalize-string@6.0.2: 1202 | resolution: {integrity: sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==} 1203 | engines: {node: ^14 || ^16 || >=18.0} 1204 | peerDependencies: 1205 | postcss: ^8.4.31 1206 | 1207 | postcss-normalize-timing-functions@6.0.2: 1208 | resolution: {integrity: sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==} 1209 | engines: {node: ^14 || ^16 || >=18.0} 1210 | peerDependencies: 1211 | postcss: ^8.4.31 1212 | 1213 | postcss-normalize-unicode@6.1.0: 1214 | resolution: {integrity: sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==} 1215 | engines: {node: ^14 || ^16 || >=18.0} 1216 | peerDependencies: 1217 | postcss: ^8.4.31 1218 | 1219 | postcss-normalize-url@6.0.2: 1220 | resolution: {integrity: sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==} 1221 | engines: {node: ^14 || ^16 || >=18.0} 1222 | peerDependencies: 1223 | postcss: ^8.4.31 1224 | 1225 | postcss-normalize-whitespace@6.0.2: 1226 | resolution: {integrity: sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==} 1227 | engines: {node: ^14 || ^16 || >=18.0} 1228 | peerDependencies: 1229 | postcss: ^8.4.31 1230 | 1231 | postcss-ordered-values@6.0.2: 1232 | resolution: {integrity: sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==} 1233 | engines: {node: ^14 || ^16 || >=18.0} 1234 | peerDependencies: 1235 | postcss: ^8.4.31 1236 | 1237 | postcss-reduce-initial@6.1.0: 1238 | resolution: {integrity: sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==} 1239 | engines: {node: ^14 || ^16 || >=18.0} 1240 | peerDependencies: 1241 | postcss: ^8.4.31 1242 | 1243 | postcss-reduce-transforms@6.0.2: 1244 | resolution: {integrity: sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==} 1245 | engines: {node: ^14 || ^16 || >=18.0} 1246 | peerDependencies: 1247 | postcss: ^8.4.31 1248 | 1249 | postcss-selector-parser@6.1.2: 1250 | resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} 1251 | engines: {node: '>=4'} 1252 | 1253 | postcss-svgo@6.0.3: 1254 | resolution: {integrity: sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==} 1255 | engines: {node: ^14 || ^16 || >= 18} 1256 | peerDependencies: 1257 | postcss: ^8.4.31 1258 | 1259 | postcss-unique-selectors@6.0.4: 1260 | resolution: {integrity: sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==} 1261 | engines: {node: ^14 || ^16 || >=18.0} 1262 | peerDependencies: 1263 | postcss: ^8.4.31 1264 | 1265 | postcss-value-parser@4.2.0: 1266 | resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} 1267 | 1268 | postcss@8.4.41: 1269 | resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==} 1270 | engines: {node: ^10 || ^12 || >=14} 1271 | 1272 | preact@10.23.2: 1273 | resolution: {integrity: sha512-kKYfePf9rzKnxOAKDpsWhg/ysrHPqT+yQ7UW4JjdnqjFIeNUnNcEJvhuA8fDenxAGWzUqtd51DfVg7xp/8T9NA==} 1274 | 1275 | query-string@9.1.0: 1276 | resolution: {integrity: sha512-t6dqMECpCkqfyv2FfwVS1xcB6lgXW/0XZSaKdsCNGYkqMO76AFiJEg4vINzoDKcZa6MS7JX+OHIjwh06K5vczw==} 1277 | engines: {node: '>=18'} 1278 | 1279 | queue-microtask@1.2.3: 1280 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 1281 | 1282 | react-dom@18.3.1: 1283 | resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} 1284 | peerDependencies: 1285 | react: ^18.3.1 1286 | 1287 | react-i18next@15.0.1: 1288 | resolution: {integrity: sha512-NwxLqNM6CLbeGA9xPsjits0EnXdKgCRSS6cgkgOdNcPXqL+1fYNl8fBg1wmnnHvFy812Bt4IWTPE9zjoPmFj3w==} 1289 | peerDependencies: 1290 | i18next: '>= 23.2.3' 1291 | react: '>= 16.8.0' 1292 | react-dom: '*' 1293 | react-native: '*' 1294 | peerDependenciesMeta: 1295 | react-dom: 1296 | optional: true 1297 | react-native: 1298 | optional: true 1299 | 1300 | react-universal-interface@0.6.2: 1301 | resolution: {integrity: sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==} 1302 | peerDependencies: 1303 | react: '*' 1304 | tslib: '*' 1305 | 1306 | react-use@17.5.1: 1307 | resolution: {integrity: sha512-LG/uPEVRflLWMwi3j/sZqR00nF6JGqTTDblkXK2nzXsIvij06hXl1V/MZIlwj1OKIQUtlh1l9jK8gLsRyCQxMg==} 1308 | peerDependencies: 1309 | react: '*' 1310 | react-dom: '*' 1311 | 1312 | react@18.3.1: 1313 | resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} 1314 | engines: {node: '>=0.10.0'} 1315 | 1316 | read-cache@1.0.0: 1317 | resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} 1318 | 1319 | readdirp@3.6.0: 1320 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 1321 | engines: {node: '>=8.10.0'} 1322 | 1323 | regenerator-runtime@0.14.1: 1324 | resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} 1325 | 1326 | require-directory@2.1.1: 1327 | resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} 1328 | engines: {node: '>=0.10.0'} 1329 | 1330 | resize-observer-polyfill@1.5.1: 1331 | resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} 1332 | 1333 | resolve@1.22.8: 1334 | resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} 1335 | hasBin: true 1336 | 1337 | restore-cursor@5.1.0: 1338 | resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} 1339 | engines: {node: '>=18'} 1340 | 1341 | reusify@1.0.4: 1342 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} 1343 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 1344 | 1345 | rev-hash@4.1.0: 1346 | resolution: {integrity: sha512-e0EGnaveLY2IYpYwHNdh43WZ2M84KgW3Z/T4F6+Z/BlZI/T1ZbxTWj36xgYgUPOieGXYo2q225jTeUXn+LWYjw==} 1347 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 1348 | 1349 | rfdc@1.4.1: 1350 | resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} 1351 | 1352 | rgb-hex@4.1.0: 1353 | resolution: {integrity: sha512-UZLM57BW09Yi9J1R3OP8B1yCbbDK3NT8BDtihGZkGkGEs2b6EaV85rsfJ6yK4F+8UbxFFmfA+9xHT5ZWhN1gDQ==} 1354 | engines: {node: '>=12'} 1355 | 1356 | rtl-css-js@1.16.1: 1357 | resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} 1358 | 1359 | run-parallel@1.2.0: 1360 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 1361 | 1362 | rxjs@7.8.1: 1363 | resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} 1364 | 1365 | sade@1.8.1: 1366 | resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} 1367 | engines: {node: '>=6'} 1368 | 1369 | scheduler@0.23.2: 1370 | resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} 1371 | 1372 | screenfull@5.2.0: 1373 | resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} 1374 | engines: {node: '>=0.10.0'} 1375 | 1376 | set-harmonic-interval@1.0.1: 1377 | resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==} 1378 | engines: {node: '>=6.9'} 1379 | 1380 | shebang-command@2.0.0: 1381 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 1382 | engines: {node: '>=8'} 1383 | 1384 | shebang-regex@3.0.0: 1385 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 1386 | engines: {node: '>=8'} 1387 | 1388 | shell-quote@1.8.1: 1389 | resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} 1390 | 1391 | signal-exit@4.1.0: 1392 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 1393 | engines: {node: '>=14'} 1394 | 1395 | slash@5.1.0: 1396 | resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} 1397 | engines: {node: '>=14.16'} 1398 | 1399 | slice-ansi@5.0.0: 1400 | resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} 1401 | engines: {node: '>=12'} 1402 | 1403 | slice-ansi@7.1.0: 1404 | resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} 1405 | engines: {node: '>=18'} 1406 | 1407 | source-map-js@1.2.0: 1408 | resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} 1409 | engines: {node: '>=0.10.0'} 1410 | 1411 | source-map@0.5.6: 1412 | resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} 1413 | engines: {node: '>=0.10.0'} 1414 | 1415 | source-map@0.6.1: 1416 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 1417 | engines: {node: '>=0.10.0'} 1418 | 1419 | spawn-command@0.0.2: 1420 | resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} 1421 | 1422 | split-on-first@3.0.0: 1423 | resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==} 1424 | engines: {node: '>=12'} 1425 | 1426 | stack-generator@2.0.10: 1427 | resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} 1428 | 1429 | stackframe@1.3.4: 1430 | resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} 1431 | 1432 | stacktrace-gps@3.1.2: 1433 | resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==} 1434 | 1435 | stacktrace-js@2.0.2: 1436 | resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} 1437 | 1438 | string-argv@0.3.2: 1439 | resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} 1440 | engines: {node: '>=0.6.19'} 1441 | 1442 | string-hash@1.1.3: 1443 | resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==} 1444 | 1445 | string-width@4.2.3: 1446 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 1447 | engines: {node: '>=8'} 1448 | 1449 | string-width@5.1.2: 1450 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 1451 | engines: {node: '>=12'} 1452 | 1453 | string-width@7.2.0: 1454 | resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} 1455 | engines: {node: '>=18'} 1456 | 1457 | strip-ansi@6.0.1: 1458 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 1459 | engines: {node: '>=8'} 1460 | 1461 | strip-ansi@7.1.0: 1462 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 1463 | engines: {node: '>=12'} 1464 | 1465 | strip-final-newline@3.0.0: 1466 | resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} 1467 | engines: {node: '>=12'} 1468 | 1469 | stylehacks@6.1.1: 1470 | resolution: {integrity: sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==} 1471 | engines: {node: ^14 || ^16 || >=18.0} 1472 | peerDependencies: 1473 | postcss: ^8.4.31 1474 | 1475 | stylis@4.3.2: 1476 | resolution: {integrity: sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==} 1477 | 1478 | sucrase@3.35.0: 1479 | resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} 1480 | engines: {node: '>=16 || 14 >=14.17'} 1481 | hasBin: true 1482 | 1483 | supports-color@7.2.0: 1484 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 1485 | engines: {node: '>=8'} 1486 | 1487 | supports-color@8.1.1: 1488 | resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} 1489 | engines: {node: '>=10'} 1490 | 1491 | supports-preserve-symlinks-flag@1.0.0: 1492 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 1493 | engines: {node: '>= 0.4'} 1494 | 1495 | svgo@3.3.2: 1496 | resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} 1497 | engines: {node: '>=14.0.0'} 1498 | hasBin: true 1499 | 1500 | tailwindcss@3.4.10: 1501 | resolution: {integrity: sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==} 1502 | engines: {node: '>=14.0.0'} 1503 | hasBin: true 1504 | 1505 | temp-dir@2.0.0: 1506 | resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} 1507 | engines: {node: '>=8'} 1508 | 1509 | temp-dir@3.0.0: 1510 | resolution: {integrity: sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==} 1511 | engines: {node: '>=14.16'} 1512 | 1513 | temp-write@5.0.0: 1514 | resolution: {integrity: sha512-cJhnzBW7DjNox7VcZDXeNlQSkIh3mX/h+M0n0Fh+zgT7YAHwI9c+OngKx4MCiQCVx9iXxV104xYlJgDBCCtawA==} 1515 | engines: {node: '>=12'} 1516 | 1517 | tempy@3.1.0: 1518 | resolution: {integrity: sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==} 1519 | engines: {node: '>=14.16'} 1520 | 1521 | thenify-all@1.6.0: 1522 | resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} 1523 | engines: {node: '>=0.8'} 1524 | 1525 | thenify@3.3.1: 1526 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} 1527 | 1528 | throttle-debounce@3.0.1: 1529 | resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} 1530 | engines: {node: '>=10'} 1531 | 1532 | to-regex-range@5.0.1: 1533 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 1534 | engines: {node: '>=8.0'} 1535 | 1536 | toggle-selection@1.0.6: 1537 | resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} 1538 | 1539 | tree-kill@1.2.2: 1540 | resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} 1541 | hasBin: true 1542 | 1543 | ts-easing@0.2.0: 1544 | resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==} 1545 | 1546 | ts-interface-checker@0.1.13: 1547 | resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} 1548 | 1549 | tslib@2.6.3: 1550 | resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} 1551 | 1552 | type-fest@1.4.0: 1553 | resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} 1554 | engines: {node: '>=10'} 1555 | 1556 | type-fest@2.19.0: 1557 | resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} 1558 | engines: {node: '>=12.20'} 1559 | 1560 | typed-css-modules@0.9.1: 1561 | resolution: {integrity: sha512-W2HWKncdKd+bLWsnuWB2EyuQBzZ7KJ9Byr/67KLiiyGegcN52rOveun9JR8yAvuL5IXunRMxt0eORMtAUj5bmA==} 1562 | engines: {node: '>=18.0.0'} 1563 | hasBin: true 1564 | 1565 | typescript@5.5.4: 1566 | resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} 1567 | engines: {node: '>=14.17'} 1568 | hasBin: true 1569 | 1570 | undici-types@6.19.8: 1571 | resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} 1572 | 1573 | unicorn-magic@0.1.0: 1574 | resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} 1575 | engines: {node: '>=18'} 1576 | 1577 | unique-string@3.0.0: 1578 | resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} 1579 | engines: {node: '>=12'} 1580 | 1581 | update-browserslist-db@1.1.0: 1582 | resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} 1583 | hasBin: true 1584 | peerDependencies: 1585 | browserslist: '>= 4.21.0' 1586 | 1587 | use-sync-external-store@1.2.2: 1588 | resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} 1589 | peerDependencies: 1590 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 1591 | 1592 | util-deprecate@1.0.2: 1593 | resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 1594 | 1595 | uuid@8.3.2: 1596 | resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} 1597 | hasBin: true 1598 | 1599 | void-elements@3.1.0: 1600 | resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} 1601 | engines: {node: '>=0.10.0'} 1602 | 1603 | which@2.0.2: 1604 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1605 | engines: {node: '>= 8'} 1606 | hasBin: true 1607 | 1608 | wrap-ansi@7.0.0: 1609 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 1610 | engines: {node: '>=10'} 1611 | 1612 | wrap-ansi@8.1.0: 1613 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 1614 | engines: {node: '>=12'} 1615 | 1616 | wrap-ansi@9.0.0: 1617 | resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} 1618 | engines: {node: '>=18'} 1619 | 1620 | y18n@5.0.8: 1621 | resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} 1622 | engines: {node: '>=10'} 1623 | 1624 | yaml@2.5.0: 1625 | resolution: {integrity: sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==} 1626 | engines: {node: '>= 14'} 1627 | hasBin: true 1628 | 1629 | yargs-parser@21.1.1: 1630 | resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} 1631 | engines: {node: '>=12'} 1632 | 1633 | yargs@17.7.2: 1634 | resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} 1635 | engines: {node: '>=12'} 1636 | 1637 | yocto-queue@1.1.1: 1638 | resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} 1639 | engines: {node: '>=12.20'} 1640 | 1641 | zustand@4.5.5: 1642 | resolution: {integrity: sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==} 1643 | engines: {node: '>=12.7.0'} 1644 | peerDependencies: 1645 | '@types/react': '>=16.8' 1646 | immer: '>=9.0.6' 1647 | react: '>=16.8' 1648 | peerDependenciesMeta: 1649 | '@types/react': 1650 | optional: true 1651 | immer: 1652 | optional: true 1653 | react: 1654 | optional: true 1655 | 1656 | snapshots: 1657 | 1658 | '@alloc/quick-lru@5.2.0': {} 1659 | 1660 | '@babel/runtime@7.25.0': 1661 | dependencies: 1662 | regenerator-runtime: 0.14.1 1663 | 1664 | '@biomejs/biome@1.8.3': 1665 | optionalDependencies: 1666 | '@biomejs/cli-darwin-arm64': 1.8.3 1667 | '@biomejs/cli-darwin-x64': 1.8.3 1668 | '@biomejs/cli-linux-arm64': 1.8.3 1669 | '@biomejs/cli-linux-arm64-musl': 1.8.3 1670 | '@biomejs/cli-linux-x64': 1.8.3 1671 | '@biomejs/cli-linux-x64-musl': 1.8.3 1672 | '@biomejs/cli-win32-arm64': 1.8.3 1673 | '@biomejs/cli-win32-x64': 1.8.3 1674 | 1675 | '@biomejs/cli-darwin-arm64@1.8.3': 1676 | optional: true 1677 | 1678 | '@biomejs/cli-darwin-x64@1.8.3': 1679 | optional: true 1680 | 1681 | '@biomejs/cli-linux-arm64-musl@1.8.3': 1682 | optional: true 1683 | 1684 | '@biomejs/cli-linux-arm64@1.8.3': 1685 | optional: true 1686 | 1687 | '@biomejs/cli-linux-x64-musl@1.8.3': 1688 | optional: true 1689 | 1690 | '@biomejs/cli-linux-x64@1.8.3': 1691 | optional: true 1692 | 1693 | '@biomejs/cli-win32-arm64@1.8.3': 1694 | optional: true 1695 | 1696 | '@biomejs/cli-win32-x64@1.8.3': 1697 | optional: true 1698 | 1699 | '@create-figma-plugin/build@3.2.0(@figma/plugin-typings@1.98.0)(typescript@5.5.4)': 1700 | dependencies: 1701 | '@create-figma-plugin/common': 3.2.0 1702 | '@figma/plugin-typings': 1.98.0 1703 | chokidar: 3.6.0 1704 | cssnano: 6.1.2(postcss@8.4.41) 1705 | esbuild: 0.20.2 1706 | find-up: 7.0.0 1707 | globby: 14.0.2 1708 | indent-string: 5.0.0 1709 | kleur: 4.1.5 1710 | path-exists: 5.0.0 1711 | postcss: 8.4.41 1712 | postcss-modules: 6.0.0(postcss@8.4.41) 1713 | rev-hash: 4.1.0 1714 | sade: 1.8.1 1715 | temp-write: 5.0.0 1716 | tempy: 3.1.0 1717 | typed-css-modules: 0.9.1 1718 | typescript: 5.5.4 1719 | 1720 | '@create-figma-plugin/common@3.2.0': 1721 | dependencies: 1722 | '@sindresorhus/slugify': 2.2.1 1723 | kleur: 4.1.5 1724 | path-exists: 5.0.0 1725 | 1726 | '@create-figma-plugin/tsconfig@3.2.0': {} 1727 | 1728 | '@create-figma-plugin/ui@3.2.0(preact@10.23.2)': 1729 | dependencies: 1730 | '@create-figma-plugin/utilities': 3.2.0 1731 | preact: 10.23.2 1732 | 1733 | '@create-figma-plugin/utilities@3.2.0': 1734 | dependencies: 1735 | hex-rgb: 5.0.0 1736 | natural-compare-lite: 1.4.0 1737 | rgb-hex: 4.1.0 1738 | 1739 | '@esbuild/aix-ppc64@0.20.2': 1740 | optional: true 1741 | 1742 | '@esbuild/android-arm64@0.20.2': 1743 | optional: true 1744 | 1745 | '@esbuild/android-arm@0.20.2': 1746 | optional: true 1747 | 1748 | '@esbuild/android-x64@0.20.2': 1749 | optional: true 1750 | 1751 | '@esbuild/darwin-arm64@0.20.2': 1752 | optional: true 1753 | 1754 | '@esbuild/darwin-x64@0.20.2': 1755 | optional: true 1756 | 1757 | '@esbuild/freebsd-arm64@0.20.2': 1758 | optional: true 1759 | 1760 | '@esbuild/freebsd-x64@0.20.2': 1761 | optional: true 1762 | 1763 | '@esbuild/linux-arm64@0.20.2': 1764 | optional: true 1765 | 1766 | '@esbuild/linux-arm@0.20.2': 1767 | optional: true 1768 | 1769 | '@esbuild/linux-ia32@0.20.2': 1770 | optional: true 1771 | 1772 | '@esbuild/linux-loong64@0.20.2': 1773 | optional: true 1774 | 1775 | '@esbuild/linux-mips64el@0.20.2': 1776 | optional: true 1777 | 1778 | '@esbuild/linux-ppc64@0.20.2': 1779 | optional: true 1780 | 1781 | '@esbuild/linux-riscv64@0.20.2': 1782 | optional: true 1783 | 1784 | '@esbuild/linux-s390x@0.20.2': 1785 | optional: true 1786 | 1787 | '@esbuild/linux-x64@0.20.2': 1788 | optional: true 1789 | 1790 | '@esbuild/netbsd-x64@0.20.2': 1791 | optional: true 1792 | 1793 | '@esbuild/openbsd-x64@0.20.2': 1794 | optional: true 1795 | 1796 | '@esbuild/sunos-x64@0.20.2': 1797 | optional: true 1798 | 1799 | '@esbuild/win32-arm64@0.20.2': 1800 | optional: true 1801 | 1802 | '@esbuild/win32-ia32@0.20.2': 1803 | optional: true 1804 | 1805 | '@esbuild/win32-x64@0.20.2': 1806 | optional: true 1807 | 1808 | '@figma/plugin-typings@1.98.0': {} 1809 | 1810 | '@isaacs/cliui@8.0.2': 1811 | dependencies: 1812 | string-width: 5.1.2 1813 | string-width-cjs: string-width@4.2.3 1814 | strip-ansi: 7.1.0 1815 | strip-ansi-cjs: strip-ansi@6.0.1 1816 | wrap-ansi: 8.1.0 1817 | wrap-ansi-cjs: wrap-ansi@7.0.0 1818 | 1819 | '@jridgewell/gen-mapping@0.3.5': 1820 | dependencies: 1821 | '@jridgewell/set-array': 1.2.1 1822 | '@jridgewell/sourcemap-codec': 1.5.0 1823 | '@jridgewell/trace-mapping': 0.3.25 1824 | 1825 | '@jridgewell/resolve-uri@3.1.2': {} 1826 | 1827 | '@jridgewell/set-array@1.2.1': {} 1828 | 1829 | '@jridgewell/sourcemap-codec@1.5.0': {} 1830 | 1831 | '@jridgewell/trace-mapping@0.3.25': 1832 | dependencies: 1833 | '@jridgewell/resolve-uri': 3.1.2 1834 | '@jridgewell/sourcemap-codec': 1.5.0 1835 | 1836 | '@nodelib/fs.scandir@2.1.5': 1837 | dependencies: 1838 | '@nodelib/fs.stat': 2.0.5 1839 | run-parallel: 1.2.0 1840 | 1841 | '@nodelib/fs.stat@2.0.5': {} 1842 | 1843 | '@nodelib/fs.walk@1.2.8': 1844 | dependencies: 1845 | '@nodelib/fs.scandir': 2.1.5 1846 | fastq: 1.17.1 1847 | 1848 | '@pkgjs/parseargs@0.11.0': 1849 | optional: true 1850 | 1851 | '@sindresorhus/merge-streams@2.3.0': {} 1852 | 1853 | '@sindresorhus/slugify@2.2.1': 1854 | dependencies: 1855 | '@sindresorhus/transliterate': 1.6.0 1856 | escape-string-regexp: 5.0.0 1857 | 1858 | '@sindresorhus/transliterate@1.6.0': 1859 | dependencies: 1860 | escape-string-regexp: 5.0.0 1861 | 1862 | '@trysound/sax@0.2.0': {} 1863 | 1864 | '@types/js-cookie@2.2.7': {} 1865 | 1866 | '@types/lodash@4.17.7': {} 1867 | 1868 | '@types/node@22.5.0': 1869 | dependencies: 1870 | undici-types: 6.19.8 1871 | 1872 | '@xobotyi/scrollbar-width@1.9.5': {} 1873 | 1874 | ansi-escapes@7.0.0: 1875 | dependencies: 1876 | environment: 1.1.0 1877 | 1878 | ansi-regex@5.0.1: {} 1879 | 1880 | ansi-regex@6.0.1: {} 1881 | 1882 | ansi-styles@4.3.0: 1883 | dependencies: 1884 | color-convert: 2.0.1 1885 | 1886 | ansi-styles@6.2.1: {} 1887 | 1888 | any-promise@1.3.0: {} 1889 | 1890 | anymatch@3.1.3: 1891 | dependencies: 1892 | normalize-path: 3.0.0 1893 | picomatch: 2.3.1 1894 | 1895 | arg@5.0.2: {} 1896 | 1897 | balanced-match@1.0.2: {} 1898 | 1899 | binary-extensions@2.3.0: {} 1900 | 1901 | boolbase@1.0.0: {} 1902 | 1903 | brace-expansion@2.0.1: 1904 | dependencies: 1905 | balanced-match: 1.0.2 1906 | 1907 | braces@3.0.3: 1908 | dependencies: 1909 | fill-range: 7.1.1 1910 | 1911 | browserslist@4.23.3: 1912 | dependencies: 1913 | caniuse-lite: 1.0.30001651 1914 | electron-to-chromium: 1.5.8 1915 | node-releases: 2.0.18 1916 | update-browserslist-db: 1.1.0(browserslist@4.23.3) 1917 | 1918 | camelcase-css@2.0.1: {} 1919 | 1920 | camelcase@6.3.0: {} 1921 | 1922 | caniuse-api@3.0.0: 1923 | dependencies: 1924 | browserslist: 4.23.3 1925 | caniuse-lite: 1.0.30001651 1926 | lodash.memoize: 4.1.2 1927 | lodash.uniq: 4.5.0 1928 | 1929 | caniuse-lite@1.0.30001651: {} 1930 | 1931 | chalk@4.1.2: 1932 | dependencies: 1933 | ansi-styles: 4.3.0 1934 | supports-color: 7.2.0 1935 | 1936 | chalk@5.3.0: {} 1937 | 1938 | chokidar@3.6.0: 1939 | dependencies: 1940 | anymatch: 3.1.3 1941 | braces: 3.0.3 1942 | glob-parent: 5.1.2 1943 | is-binary-path: 2.1.0 1944 | is-glob: 4.0.3 1945 | normalize-path: 3.0.0 1946 | readdirp: 3.6.0 1947 | optionalDependencies: 1948 | fsevents: 2.3.3 1949 | 1950 | cli-cursor@5.0.0: 1951 | dependencies: 1952 | restore-cursor: 5.1.0 1953 | 1954 | cli-truncate@4.0.0: 1955 | dependencies: 1956 | slice-ansi: 5.0.0 1957 | string-width: 7.2.0 1958 | 1959 | cliui@8.0.1: 1960 | dependencies: 1961 | string-width: 4.2.3 1962 | strip-ansi: 6.0.1 1963 | wrap-ansi: 7.0.0 1964 | 1965 | clsx@2.1.1: {} 1966 | 1967 | color-convert@2.0.1: 1968 | dependencies: 1969 | color-name: 1.1.4 1970 | 1971 | color-name@1.1.4: {} 1972 | 1973 | colord@2.9.3: {} 1974 | 1975 | colorette@2.0.20: {} 1976 | 1977 | commander@12.1.0: {} 1978 | 1979 | commander@4.1.1: {} 1980 | 1981 | commander@7.2.0: {} 1982 | 1983 | concurrently@8.2.2: 1984 | dependencies: 1985 | chalk: 4.1.2 1986 | date-fns: 2.30.0 1987 | lodash: 4.17.21 1988 | rxjs: 7.8.1 1989 | shell-quote: 1.8.1 1990 | spawn-command: 0.0.2 1991 | supports-color: 8.1.1 1992 | tree-kill: 1.2.2 1993 | yargs: 17.7.2 1994 | 1995 | copy-to-clipboard@3.3.3: 1996 | dependencies: 1997 | toggle-selection: 1.0.6 1998 | 1999 | cross-spawn@7.0.3: 2000 | dependencies: 2001 | path-key: 3.1.1 2002 | shebang-command: 2.0.0 2003 | which: 2.0.2 2004 | 2005 | crypto-random-string@4.0.0: 2006 | dependencies: 2007 | type-fest: 1.4.0 2008 | 2009 | css-declaration-sorter@7.2.0(postcss@8.4.41): 2010 | dependencies: 2011 | postcss: 8.4.41 2012 | 2013 | css-in-js-utils@3.1.0: 2014 | dependencies: 2015 | hyphenate-style-name: 1.1.0 2016 | 2017 | css-select@5.1.0: 2018 | dependencies: 2019 | boolbase: 1.0.0 2020 | css-what: 6.1.0 2021 | domhandler: 5.0.3 2022 | domutils: 3.1.0 2023 | nth-check: 2.1.1 2024 | 2025 | css-tree@1.1.3: 2026 | dependencies: 2027 | mdn-data: 2.0.14 2028 | source-map: 0.6.1 2029 | 2030 | css-tree@2.2.1: 2031 | dependencies: 2032 | mdn-data: 2.0.28 2033 | source-map-js: 1.2.0 2034 | 2035 | css-tree@2.3.1: 2036 | dependencies: 2037 | mdn-data: 2.0.30 2038 | source-map-js: 1.2.0 2039 | 2040 | css-what@6.1.0: {} 2041 | 2042 | cssesc@3.0.0: {} 2043 | 2044 | cssnano-preset-default@6.1.2(postcss@8.4.41): 2045 | dependencies: 2046 | browserslist: 4.23.3 2047 | css-declaration-sorter: 7.2.0(postcss@8.4.41) 2048 | cssnano-utils: 4.0.2(postcss@8.4.41) 2049 | postcss: 8.4.41 2050 | postcss-calc: 9.0.1(postcss@8.4.41) 2051 | postcss-colormin: 6.1.0(postcss@8.4.41) 2052 | postcss-convert-values: 6.1.0(postcss@8.4.41) 2053 | postcss-discard-comments: 6.0.2(postcss@8.4.41) 2054 | postcss-discard-duplicates: 6.0.3(postcss@8.4.41) 2055 | postcss-discard-empty: 6.0.3(postcss@8.4.41) 2056 | postcss-discard-overridden: 6.0.2(postcss@8.4.41) 2057 | postcss-merge-longhand: 6.0.5(postcss@8.4.41) 2058 | postcss-merge-rules: 6.1.1(postcss@8.4.41) 2059 | postcss-minify-font-values: 6.1.0(postcss@8.4.41) 2060 | postcss-minify-gradients: 6.0.3(postcss@8.4.41) 2061 | postcss-minify-params: 6.1.0(postcss@8.4.41) 2062 | postcss-minify-selectors: 6.0.4(postcss@8.4.41) 2063 | postcss-normalize-charset: 6.0.2(postcss@8.4.41) 2064 | postcss-normalize-display-values: 6.0.2(postcss@8.4.41) 2065 | postcss-normalize-positions: 6.0.2(postcss@8.4.41) 2066 | postcss-normalize-repeat-style: 6.0.2(postcss@8.4.41) 2067 | postcss-normalize-string: 6.0.2(postcss@8.4.41) 2068 | postcss-normalize-timing-functions: 6.0.2(postcss@8.4.41) 2069 | postcss-normalize-unicode: 6.1.0(postcss@8.4.41) 2070 | postcss-normalize-url: 6.0.2(postcss@8.4.41) 2071 | postcss-normalize-whitespace: 6.0.2(postcss@8.4.41) 2072 | postcss-ordered-values: 6.0.2(postcss@8.4.41) 2073 | postcss-reduce-initial: 6.1.0(postcss@8.4.41) 2074 | postcss-reduce-transforms: 6.0.2(postcss@8.4.41) 2075 | postcss-svgo: 6.0.3(postcss@8.4.41) 2076 | postcss-unique-selectors: 6.0.4(postcss@8.4.41) 2077 | 2078 | cssnano-utils@4.0.2(postcss@8.4.41): 2079 | dependencies: 2080 | postcss: 8.4.41 2081 | 2082 | cssnano@6.1.2(postcss@8.4.41): 2083 | dependencies: 2084 | cssnano-preset-default: 6.1.2(postcss@8.4.41) 2085 | lilconfig: 3.1.2 2086 | postcss: 8.4.41 2087 | 2088 | csso@5.0.5: 2089 | dependencies: 2090 | css-tree: 2.2.1 2091 | 2092 | csstype@3.1.3: {} 2093 | 2094 | date-fns@2.30.0: 2095 | dependencies: 2096 | '@babel/runtime': 7.25.0 2097 | 2098 | debug@4.3.6: 2099 | dependencies: 2100 | ms: 2.1.2 2101 | 2102 | decode-uri-component@0.4.1: {} 2103 | 2104 | didyoumean@1.2.2: {} 2105 | 2106 | dlv@1.1.3: {} 2107 | 2108 | dom-serializer@2.0.0: 2109 | dependencies: 2110 | domelementtype: 2.3.0 2111 | domhandler: 5.0.3 2112 | entities: 4.5.0 2113 | 2114 | domelementtype@2.3.0: {} 2115 | 2116 | domhandler@5.0.3: 2117 | dependencies: 2118 | domelementtype: 2.3.0 2119 | 2120 | domutils@3.1.0: 2121 | dependencies: 2122 | dom-serializer: 2.0.0 2123 | domelementtype: 2.3.0 2124 | domhandler: 5.0.3 2125 | 2126 | dotenv@16.4.5: {} 2127 | 2128 | eastasianwidth@0.2.0: {} 2129 | 2130 | electron-to-chromium@1.5.8: {} 2131 | 2132 | emoji-regex@10.3.0: {} 2133 | 2134 | emoji-regex@8.0.0: {} 2135 | 2136 | emoji-regex@9.2.2: {} 2137 | 2138 | entities@4.5.0: {} 2139 | 2140 | environment@1.1.0: {} 2141 | 2142 | error-stack-parser@2.1.4: 2143 | dependencies: 2144 | stackframe: 1.3.4 2145 | 2146 | esbuild@0.20.2: 2147 | optionalDependencies: 2148 | '@esbuild/aix-ppc64': 0.20.2 2149 | '@esbuild/android-arm': 0.20.2 2150 | '@esbuild/android-arm64': 0.20.2 2151 | '@esbuild/android-x64': 0.20.2 2152 | '@esbuild/darwin-arm64': 0.20.2 2153 | '@esbuild/darwin-x64': 0.20.2 2154 | '@esbuild/freebsd-arm64': 0.20.2 2155 | '@esbuild/freebsd-x64': 0.20.2 2156 | '@esbuild/linux-arm': 0.20.2 2157 | '@esbuild/linux-arm64': 0.20.2 2158 | '@esbuild/linux-ia32': 0.20.2 2159 | '@esbuild/linux-loong64': 0.20.2 2160 | '@esbuild/linux-mips64el': 0.20.2 2161 | '@esbuild/linux-ppc64': 0.20.2 2162 | '@esbuild/linux-riscv64': 0.20.2 2163 | '@esbuild/linux-s390x': 0.20.2 2164 | '@esbuild/linux-x64': 0.20.2 2165 | '@esbuild/netbsd-x64': 0.20.2 2166 | '@esbuild/openbsd-x64': 0.20.2 2167 | '@esbuild/sunos-x64': 0.20.2 2168 | '@esbuild/win32-arm64': 0.20.2 2169 | '@esbuild/win32-ia32': 0.20.2 2170 | '@esbuild/win32-x64': 0.20.2 2171 | 2172 | escalade@3.1.2: {} 2173 | 2174 | escape-string-regexp@5.0.0: {} 2175 | 2176 | eventemitter3@5.0.1: {} 2177 | 2178 | execa@8.0.1: 2179 | dependencies: 2180 | cross-spawn: 7.0.3 2181 | get-stream: 8.0.1 2182 | human-signals: 5.0.0 2183 | is-stream: 3.0.0 2184 | merge-stream: 2.0.0 2185 | npm-run-path: 5.3.0 2186 | onetime: 6.0.0 2187 | signal-exit: 4.1.0 2188 | strip-final-newline: 3.0.0 2189 | 2190 | fast-deep-equal@3.1.3: {} 2191 | 2192 | fast-glob@3.3.2: 2193 | dependencies: 2194 | '@nodelib/fs.stat': 2.0.5 2195 | '@nodelib/fs.walk': 1.2.8 2196 | glob-parent: 5.1.2 2197 | merge2: 1.4.1 2198 | micromatch: 4.0.7 2199 | 2200 | fast-shallow-equal@1.0.0: {} 2201 | 2202 | fastest-stable-stringify@2.0.2: {} 2203 | 2204 | fastq@1.17.1: 2205 | dependencies: 2206 | reusify: 1.0.4 2207 | 2208 | fill-range@7.1.1: 2209 | dependencies: 2210 | to-regex-range: 5.0.1 2211 | 2212 | filter-obj@5.1.0: {} 2213 | 2214 | find-up@7.0.0: 2215 | dependencies: 2216 | locate-path: 7.2.0 2217 | path-exists: 5.0.0 2218 | unicorn-magic: 0.1.0 2219 | 2220 | foreground-child@3.3.0: 2221 | dependencies: 2222 | cross-spawn: 7.0.3 2223 | signal-exit: 4.1.0 2224 | 2225 | fsevents@2.3.3: 2226 | optional: true 2227 | 2228 | function-bind@1.1.2: {} 2229 | 2230 | generic-names@4.0.0: 2231 | dependencies: 2232 | loader-utils: 3.3.1 2233 | 2234 | get-caller-file@2.0.5: {} 2235 | 2236 | get-east-asian-width@1.2.0: {} 2237 | 2238 | get-stream@8.0.1: {} 2239 | 2240 | glob-parent@5.1.2: 2241 | dependencies: 2242 | is-glob: 4.0.3 2243 | 2244 | glob-parent@6.0.2: 2245 | dependencies: 2246 | is-glob: 4.0.3 2247 | 2248 | glob@10.4.5: 2249 | dependencies: 2250 | foreground-child: 3.3.0 2251 | jackspeak: 3.4.3 2252 | minimatch: 9.0.5 2253 | minipass: 7.1.2 2254 | package-json-from-dist: 1.0.0 2255 | path-scurry: 1.11.1 2256 | 2257 | globby@14.0.2: 2258 | dependencies: 2259 | '@sindresorhus/merge-streams': 2.3.0 2260 | fast-glob: 3.3.2 2261 | ignore: 5.3.2 2262 | path-type: 5.0.0 2263 | slash: 5.1.0 2264 | unicorn-magic: 0.1.0 2265 | 2266 | graceful-fs@4.2.11: {} 2267 | 2268 | has-flag@4.0.0: {} 2269 | 2270 | hasown@2.0.2: 2271 | dependencies: 2272 | function-bind: 1.1.2 2273 | 2274 | hex-rgb@5.0.0: {} 2275 | 2276 | html-parse-stringify@3.0.1: 2277 | dependencies: 2278 | void-elements: 3.1.0 2279 | 2280 | human-signals@5.0.0: {} 2281 | 2282 | husky@9.1.5: {} 2283 | 2284 | hyphenate-style-name@1.1.0: {} 2285 | 2286 | i18next@23.14.0: 2287 | dependencies: 2288 | '@babel/runtime': 7.25.0 2289 | 2290 | icss-replace-symbols@1.1.0: {} 2291 | 2292 | icss-utils@5.1.0(postcss@8.4.41): 2293 | dependencies: 2294 | postcss: 8.4.41 2295 | 2296 | ignore@5.3.2: {} 2297 | 2298 | indent-string@5.0.0: {} 2299 | 2300 | inline-style-prefixer@7.0.1: 2301 | dependencies: 2302 | css-in-js-utils: 3.1.0 2303 | 2304 | is-binary-path@2.1.0: 2305 | dependencies: 2306 | binary-extensions: 2.3.0 2307 | 2308 | is-core-module@2.15.0: 2309 | dependencies: 2310 | hasown: 2.0.2 2311 | 2312 | is-extglob@2.1.1: {} 2313 | 2314 | is-fullwidth-code-point@3.0.0: {} 2315 | 2316 | is-fullwidth-code-point@4.0.0: {} 2317 | 2318 | is-fullwidth-code-point@5.0.0: 2319 | dependencies: 2320 | get-east-asian-width: 1.2.0 2321 | 2322 | is-glob@4.0.3: 2323 | dependencies: 2324 | is-extglob: 2.1.1 2325 | 2326 | is-number@7.0.0: {} 2327 | 2328 | is-stream@2.0.1: {} 2329 | 2330 | is-stream@3.0.0: {} 2331 | 2332 | is-there@4.5.1: {} 2333 | 2334 | isexe@2.0.0: {} 2335 | 2336 | jackspeak@3.4.3: 2337 | dependencies: 2338 | '@isaacs/cliui': 8.0.2 2339 | optionalDependencies: 2340 | '@pkgjs/parseargs': 0.11.0 2341 | 2342 | jiti@1.21.6: {} 2343 | 2344 | js-cookie@2.2.1: {} 2345 | 2346 | js-tokens@4.0.0: {} 2347 | 2348 | kleur@4.1.5: {} 2349 | 2350 | lilconfig@2.1.0: {} 2351 | 2352 | lilconfig@3.1.2: {} 2353 | 2354 | lines-and-columns@1.2.4: {} 2355 | 2356 | lint-staged@15.2.9: 2357 | dependencies: 2358 | chalk: 5.3.0 2359 | commander: 12.1.0 2360 | debug: 4.3.6 2361 | execa: 8.0.1 2362 | lilconfig: 3.1.2 2363 | listr2: 8.2.4 2364 | micromatch: 4.0.7 2365 | pidtree: 0.6.0 2366 | string-argv: 0.3.2 2367 | yaml: 2.5.0 2368 | transitivePeerDependencies: 2369 | - supports-color 2370 | 2371 | listr2@8.2.4: 2372 | dependencies: 2373 | cli-truncate: 4.0.0 2374 | colorette: 2.0.20 2375 | eventemitter3: 5.0.1 2376 | log-update: 6.1.0 2377 | rfdc: 1.4.1 2378 | wrap-ansi: 9.0.0 2379 | 2380 | loader-utils@3.3.1: {} 2381 | 2382 | locate-path@7.2.0: 2383 | dependencies: 2384 | p-locate: 6.0.0 2385 | 2386 | lodash.camelcase@4.3.0: {} 2387 | 2388 | lodash.memoize@4.1.2: {} 2389 | 2390 | lodash.uniq@4.5.0: {} 2391 | 2392 | lodash@4.17.21: {} 2393 | 2394 | log-update@6.1.0: 2395 | dependencies: 2396 | ansi-escapes: 7.0.0 2397 | cli-cursor: 5.0.0 2398 | slice-ansi: 7.1.0 2399 | strip-ansi: 7.1.0 2400 | wrap-ansi: 9.0.0 2401 | 2402 | loose-envify@1.4.0: 2403 | dependencies: 2404 | js-tokens: 4.0.0 2405 | 2406 | lru-cache@10.4.3: {} 2407 | 2408 | mdn-data@2.0.14: {} 2409 | 2410 | mdn-data@2.0.28: {} 2411 | 2412 | mdn-data@2.0.30: {} 2413 | 2414 | merge-stream@2.0.0: {} 2415 | 2416 | merge2@1.4.1: {} 2417 | 2418 | micromatch@4.0.7: 2419 | dependencies: 2420 | braces: 3.0.3 2421 | picomatch: 2.3.1 2422 | 2423 | mimic-fn@4.0.0: {} 2424 | 2425 | mimic-function@5.0.1: {} 2426 | 2427 | minimatch@9.0.5: 2428 | dependencies: 2429 | brace-expansion: 2.0.1 2430 | 2431 | minipass@7.1.2: {} 2432 | 2433 | mkdirp@3.0.1: {} 2434 | 2435 | mri@1.2.0: {} 2436 | 2437 | ms@2.1.2: {} 2438 | 2439 | mz@2.7.0: 2440 | dependencies: 2441 | any-promise: 1.3.0 2442 | object-assign: 4.1.1 2443 | thenify-all: 1.6.0 2444 | 2445 | nano-css@5.6.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 2446 | dependencies: 2447 | '@jridgewell/sourcemap-codec': 1.5.0 2448 | css-tree: 1.1.3 2449 | csstype: 3.1.3 2450 | fastest-stable-stringify: 2.0.2 2451 | inline-style-prefixer: 7.0.1 2452 | react: 18.3.1 2453 | react-dom: 18.3.1(react@18.3.1) 2454 | rtl-css-js: 1.16.1 2455 | stacktrace-js: 2.0.2 2456 | stylis: 4.3.2 2457 | 2458 | nanoid@3.3.7: {} 2459 | 2460 | natural-compare-lite@1.4.0: {} 2461 | 2462 | node-releases@2.0.18: {} 2463 | 2464 | normalize-path@3.0.0: {} 2465 | 2466 | npm-run-path@5.3.0: 2467 | dependencies: 2468 | path-key: 4.0.0 2469 | 2470 | nth-check@2.1.1: 2471 | dependencies: 2472 | boolbase: 1.0.0 2473 | 2474 | object-assign@4.1.1: {} 2475 | 2476 | object-hash@3.0.0: {} 2477 | 2478 | onetime@6.0.0: 2479 | dependencies: 2480 | mimic-fn: 4.0.0 2481 | 2482 | onetime@7.0.0: 2483 | dependencies: 2484 | mimic-function: 5.0.1 2485 | 2486 | p-limit@4.0.0: 2487 | dependencies: 2488 | yocto-queue: 1.1.1 2489 | 2490 | p-locate@6.0.0: 2491 | dependencies: 2492 | p-limit: 4.0.0 2493 | 2494 | package-json-from-dist@1.0.0: {} 2495 | 2496 | path-exists@5.0.0: {} 2497 | 2498 | path-key@3.1.1: {} 2499 | 2500 | path-key@4.0.0: {} 2501 | 2502 | path-parse@1.0.7: {} 2503 | 2504 | path-scurry@1.11.1: 2505 | dependencies: 2506 | lru-cache: 10.4.3 2507 | minipass: 7.1.2 2508 | 2509 | path-type@5.0.0: {} 2510 | 2511 | picocolors@1.0.1: {} 2512 | 2513 | picomatch@2.3.1: {} 2514 | 2515 | pidtree@0.6.0: {} 2516 | 2517 | pify@2.3.0: {} 2518 | 2519 | pirates@4.0.6: {} 2520 | 2521 | postcss-calc@9.0.1(postcss@8.4.41): 2522 | dependencies: 2523 | postcss: 8.4.41 2524 | postcss-selector-parser: 6.1.2 2525 | postcss-value-parser: 4.2.0 2526 | 2527 | postcss-colormin@6.1.0(postcss@8.4.41): 2528 | dependencies: 2529 | browserslist: 4.23.3 2530 | caniuse-api: 3.0.0 2531 | colord: 2.9.3 2532 | postcss: 8.4.41 2533 | postcss-value-parser: 4.2.0 2534 | 2535 | postcss-convert-values@6.1.0(postcss@8.4.41): 2536 | dependencies: 2537 | browserslist: 4.23.3 2538 | postcss: 8.4.41 2539 | postcss-value-parser: 4.2.0 2540 | 2541 | postcss-discard-comments@6.0.2(postcss@8.4.41): 2542 | dependencies: 2543 | postcss: 8.4.41 2544 | 2545 | postcss-discard-duplicates@6.0.3(postcss@8.4.41): 2546 | dependencies: 2547 | postcss: 8.4.41 2548 | 2549 | postcss-discard-empty@6.0.3(postcss@8.4.41): 2550 | dependencies: 2551 | postcss: 8.4.41 2552 | 2553 | postcss-discard-overridden@6.0.2(postcss@8.4.41): 2554 | dependencies: 2555 | postcss: 8.4.41 2556 | 2557 | postcss-import@15.1.0(postcss@8.4.41): 2558 | dependencies: 2559 | postcss: 8.4.41 2560 | postcss-value-parser: 4.2.0 2561 | read-cache: 1.0.0 2562 | resolve: 1.22.8 2563 | 2564 | postcss-js@4.0.1(postcss@8.4.41): 2565 | dependencies: 2566 | camelcase-css: 2.0.1 2567 | postcss: 8.4.41 2568 | 2569 | postcss-load-config@4.0.2(postcss@8.4.41): 2570 | dependencies: 2571 | lilconfig: 3.1.2 2572 | yaml: 2.5.0 2573 | optionalDependencies: 2574 | postcss: 8.4.41 2575 | 2576 | postcss-merge-longhand@6.0.5(postcss@8.4.41): 2577 | dependencies: 2578 | postcss: 8.4.41 2579 | postcss-value-parser: 4.2.0 2580 | stylehacks: 6.1.1(postcss@8.4.41) 2581 | 2582 | postcss-merge-rules@6.1.1(postcss@8.4.41): 2583 | dependencies: 2584 | browserslist: 4.23.3 2585 | caniuse-api: 3.0.0 2586 | cssnano-utils: 4.0.2(postcss@8.4.41) 2587 | postcss: 8.4.41 2588 | postcss-selector-parser: 6.1.2 2589 | 2590 | postcss-minify-font-values@6.1.0(postcss@8.4.41): 2591 | dependencies: 2592 | postcss: 8.4.41 2593 | postcss-value-parser: 4.2.0 2594 | 2595 | postcss-minify-gradients@6.0.3(postcss@8.4.41): 2596 | dependencies: 2597 | colord: 2.9.3 2598 | cssnano-utils: 4.0.2(postcss@8.4.41) 2599 | postcss: 8.4.41 2600 | postcss-value-parser: 4.2.0 2601 | 2602 | postcss-minify-params@6.1.0(postcss@8.4.41): 2603 | dependencies: 2604 | browserslist: 4.23.3 2605 | cssnano-utils: 4.0.2(postcss@8.4.41) 2606 | postcss: 8.4.41 2607 | postcss-value-parser: 4.2.0 2608 | 2609 | postcss-minify-selectors@6.0.4(postcss@8.4.41): 2610 | dependencies: 2611 | postcss: 8.4.41 2612 | postcss-selector-parser: 6.1.2 2613 | 2614 | postcss-modules-extract-imports@3.1.0(postcss@8.4.41): 2615 | dependencies: 2616 | postcss: 8.4.41 2617 | 2618 | postcss-modules-local-by-default@4.0.5(postcss@8.4.41): 2619 | dependencies: 2620 | icss-utils: 5.1.0(postcss@8.4.41) 2621 | postcss: 8.4.41 2622 | postcss-selector-parser: 6.1.2 2623 | postcss-value-parser: 4.2.0 2624 | 2625 | postcss-modules-scope@3.2.0(postcss@8.4.41): 2626 | dependencies: 2627 | postcss: 8.4.41 2628 | postcss-selector-parser: 6.1.2 2629 | 2630 | postcss-modules-values@4.0.0(postcss@8.4.41): 2631 | dependencies: 2632 | icss-utils: 5.1.0(postcss@8.4.41) 2633 | postcss: 8.4.41 2634 | 2635 | postcss-modules@6.0.0(postcss@8.4.41): 2636 | dependencies: 2637 | generic-names: 4.0.0 2638 | icss-utils: 5.1.0(postcss@8.4.41) 2639 | lodash.camelcase: 4.3.0 2640 | postcss: 8.4.41 2641 | postcss-modules-extract-imports: 3.1.0(postcss@8.4.41) 2642 | postcss-modules-local-by-default: 4.0.5(postcss@8.4.41) 2643 | postcss-modules-scope: 3.2.0(postcss@8.4.41) 2644 | postcss-modules-values: 4.0.0(postcss@8.4.41) 2645 | string-hash: 1.1.3 2646 | 2647 | postcss-nested@6.2.0(postcss@8.4.41): 2648 | dependencies: 2649 | postcss: 8.4.41 2650 | postcss-selector-parser: 6.1.2 2651 | 2652 | postcss-normalize-charset@6.0.2(postcss@8.4.41): 2653 | dependencies: 2654 | postcss: 8.4.41 2655 | 2656 | postcss-normalize-display-values@6.0.2(postcss@8.4.41): 2657 | dependencies: 2658 | postcss: 8.4.41 2659 | postcss-value-parser: 4.2.0 2660 | 2661 | postcss-normalize-positions@6.0.2(postcss@8.4.41): 2662 | dependencies: 2663 | postcss: 8.4.41 2664 | postcss-value-parser: 4.2.0 2665 | 2666 | postcss-normalize-repeat-style@6.0.2(postcss@8.4.41): 2667 | dependencies: 2668 | postcss: 8.4.41 2669 | postcss-value-parser: 4.2.0 2670 | 2671 | postcss-normalize-string@6.0.2(postcss@8.4.41): 2672 | dependencies: 2673 | postcss: 8.4.41 2674 | postcss-value-parser: 4.2.0 2675 | 2676 | postcss-normalize-timing-functions@6.0.2(postcss@8.4.41): 2677 | dependencies: 2678 | postcss: 8.4.41 2679 | postcss-value-parser: 4.2.0 2680 | 2681 | postcss-normalize-unicode@6.1.0(postcss@8.4.41): 2682 | dependencies: 2683 | browserslist: 4.23.3 2684 | postcss: 8.4.41 2685 | postcss-value-parser: 4.2.0 2686 | 2687 | postcss-normalize-url@6.0.2(postcss@8.4.41): 2688 | dependencies: 2689 | postcss: 8.4.41 2690 | postcss-value-parser: 4.2.0 2691 | 2692 | postcss-normalize-whitespace@6.0.2(postcss@8.4.41): 2693 | dependencies: 2694 | postcss: 8.4.41 2695 | postcss-value-parser: 4.2.0 2696 | 2697 | postcss-ordered-values@6.0.2(postcss@8.4.41): 2698 | dependencies: 2699 | cssnano-utils: 4.0.2(postcss@8.4.41) 2700 | postcss: 8.4.41 2701 | postcss-value-parser: 4.2.0 2702 | 2703 | postcss-reduce-initial@6.1.0(postcss@8.4.41): 2704 | dependencies: 2705 | browserslist: 4.23.3 2706 | caniuse-api: 3.0.0 2707 | postcss: 8.4.41 2708 | 2709 | postcss-reduce-transforms@6.0.2(postcss@8.4.41): 2710 | dependencies: 2711 | postcss: 8.4.41 2712 | postcss-value-parser: 4.2.0 2713 | 2714 | postcss-selector-parser@6.1.2: 2715 | dependencies: 2716 | cssesc: 3.0.0 2717 | util-deprecate: 1.0.2 2718 | 2719 | postcss-svgo@6.0.3(postcss@8.4.41): 2720 | dependencies: 2721 | postcss: 8.4.41 2722 | postcss-value-parser: 4.2.0 2723 | svgo: 3.3.2 2724 | 2725 | postcss-unique-selectors@6.0.4(postcss@8.4.41): 2726 | dependencies: 2727 | postcss: 8.4.41 2728 | postcss-selector-parser: 6.1.2 2729 | 2730 | postcss-value-parser@4.2.0: {} 2731 | 2732 | postcss@8.4.41: 2733 | dependencies: 2734 | nanoid: 3.3.7 2735 | picocolors: 1.0.1 2736 | source-map-js: 1.2.0 2737 | 2738 | preact@10.23.2: {} 2739 | 2740 | query-string@9.1.0: 2741 | dependencies: 2742 | decode-uri-component: 0.4.1 2743 | filter-obj: 5.1.0 2744 | split-on-first: 3.0.0 2745 | 2746 | queue-microtask@1.2.3: {} 2747 | 2748 | react-dom@18.3.1(react@18.3.1): 2749 | dependencies: 2750 | loose-envify: 1.4.0 2751 | react: 18.3.1 2752 | scheduler: 0.23.2 2753 | 2754 | react-i18next@15.0.1(i18next@23.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 2755 | dependencies: 2756 | '@babel/runtime': 7.25.0 2757 | html-parse-stringify: 3.0.1 2758 | i18next: 23.14.0 2759 | react: 18.3.1 2760 | optionalDependencies: 2761 | react-dom: 18.3.1(react@18.3.1) 2762 | 2763 | react-universal-interface@0.6.2(react@18.3.1)(tslib@2.6.3): 2764 | dependencies: 2765 | react: 18.3.1 2766 | tslib: 2.6.3 2767 | 2768 | react-use@17.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 2769 | dependencies: 2770 | '@types/js-cookie': 2.2.7 2771 | '@xobotyi/scrollbar-width': 1.9.5 2772 | copy-to-clipboard: 3.3.3 2773 | fast-deep-equal: 3.1.3 2774 | fast-shallow-equal: 1.0.0 2775 | js-cookie: 2.2.1 2776 | nano-css: 5.6.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2777 | react: 18.3.1 2778 | react-dom: 18.3.1(react@18.3.1) 2779 | react-universal-interface: 0.6.2(react@18.3.1)(tslib@2.6.3) 2780 | resize-observer-polyfill: 1.5.1 2781 | screenfull: 5.2.0 2782 | set-harmonic-interval: 1.0.1 2783 | throttle-debounce: 3.0.1 2784 | ts-easing: 0.2.0 2785 | tslib: 2.6.3 2786 | 2787 | react@18.3.1: 2788 | dependencies: 2789 | loose-envify: 1.4.0 2790 | 2791 | read-cache@1.0.0: 2792 | dependencies: 2793 | pify: 2.3.0 2794 | 2795 | readdirp@3.6.0: 2796 | dependencies: 2797 | picomatch: 2.3.1 2798 | 2799 | regenerator-runtime@0.14.1: {} 2800 | 2801 | require-directory@2.1.1: {} 2802 | 2803 | resize-observer-polyfill@1.5.1: {} 2804 | 2805 | resolve@1.22.8: 2806 | dependencies: 2807 | is-core-module: 2.15.0 2808 | path-parse: 1.0.7 2809 | supports-preserve-symlinks-flag: 1.0.0 2810 | 2811 | restore-cursor@5.1.0: 2812 | dependencies: 2813 | onetime: 7.0.0 2814 | signal-exit: 4.1.0 2815 | 2816 | reusify@1.0.4: {} 2817 | 2818 | rev-hash@4.1.0: {} 2819 | 2820 | rfdc@1.4.1: {} 2821 | 2822 | rgb-hex@4.1.0: {} 2823 | 2824 | rtl-css-js@1.16.1: 2825 | dependencies: 2826 | '@babel/runtime': 7.25.0 2827 | 2828 | run-parallel@1.2.0: 2829 | dependencies: 2830 | queue-microtask: 1.2.3 2831 | 2832 | rxjs@7.8.1: 2833 | dependencies: 2834 | tslib: 2.6.3 2835 | 2836 | sade@1.8.1: 2837 | dependencies: 2838 | mri: 1.2.0 2839 | 2840 | scheduler@0.23.2: 2841 | dependencies: 2842 | loose-envify: 1.4.0 2843 | 2844 | screenfull@5.2.0: {} 2845 | 2846 | set-harmonic-interval@1.0.1: {} 2847 | 2848 | shebang-command@2.0.0: 2849 | dependencies: 2850 | shebang-regex: 3.0.0 2851 | 2852 | shebang-regex@3.0.0: {} 2853 | 2854 | shell-quote@1.8.1: {} 2855 | 2856 | signal-exit@4.1.0: {} 2857 | 2858 | slash@5.1.0: {} 2859 | 2860 | slice-ansi@5.0.0: 2861 | dependencies: 2862 | ansi-styles: 6.2.1 2863 | is-fullwidth-code-point: 4.0.0 2864 | 2865 | slice-ansi@7.1.0: 2866 | dependencies: 2867 | ansi-styles: 6.2.1 2868 | is-fullwidth-code-point: 5.0.0 2869 | 2870 | source-map-js@1.2.0: {} 2871 | 2872 | source-map@0.5.6: {} 2873 | 2874 | source-map@0.6.1: {} 2875 | 2876 | spawn-command@0.0.2: {} 2877 | 2878 | split-on-first@3.0.0: {} 2879 | 2880 | stack-generator@2.0.10: 2881 | dependencies: 2882 | stackframe: 1.3.4 2883 | 2884 | stackframe@1.3.4: {} 2885 | 2886 | stacktrace-gps@3.1.2: 2887 | dependencies: 2888 | source-map: 0.5.6 2889 | stackframe: 1.3.4 2890 | 2891 | stacktrace-js@2.0.2: 2892 | dependencies: 2893 | error-stack-parser: 2.1.4 2894 | stack-generator: 2.0.10 2895 | stacktrace-gps: 3.1.2 2896 | 2897 | string-argv@0.3.2: {} 2898 | 2899 | string-hash@1.1.3: {} 2900 | 2901 | string-width@4.2.3: 2902 | dependencies: 2903 | emoji-regex: 8.0.0 2904 | is-fullwidth-code-point: 3.0.0 2905 | strip-ansi: 6.0.1 2906 | 2907 | string-width@5.1.2: 2908 | dependencies: 2909 | eastasianwidth: 0.2.0 2910 | emoji-regex: 9.2.2 2911 | strip-ansi: 7.1.0 2912 | 2913 | string-width@7.2.0: 2914 | dependencies: 2915 | emoji-regex: 10.3.0 2916 | get-east-asian-width: 1.2.0 2917 | strip-ansi: 7.1.0 2918 | 2919 | strip-ansi@6.0.1: 2920 | dependencies: 2921 | ansi-regex: 5.0.1 2922 | 2923 | strip-ansi@7.1.0: 2924 | dependencies: 2925 | ansi-regex: 6.0.1 2926 | 2927 | strip-final-newline@3.0.0: {} 2928 | 2929 | stylehacks@6.1.1(postcss@8.4.41): 2930 | dependencies: 2931 | browserslist: 4.23.3 2932 | postcss: 8.4.41 2933 | postcss-selector-parser: 6.1.2 2934 | 2935 | stylis@4.3.2: {} 2936 | 2937 | sucrase@3.35.0: 2938 | dependencies: 2939 | '@jridgewell/gen-mapping': 0.3.5 2940 | commander: 4.1.1 2941 | glob: 10.4.5 2942 | lines-and-columns: 1.2.4 2943 | mz: 2.7.0 2944 | pirates: 4.0.6 2945 | ts-interface-checker: 0.1.13 2946 | 2947 | supports-color@7.2.0: 2948 | dependencies: 2949 | has-flag: 4.0.0 2950 | 2951 | supports-color@8.1.1: 2952 | dependencies: 2953 | has-flag: 4.0.0 2954 | 2955 | supports-preserve-symlinks-flag@1.0.0: {} 2956 | 2957 | svgo@3.3.2: 2958 | dependencies: 2959 | '@trysound/sax': 0.2.0 2960 | commander: 7.2.0 2961 | css-select: 5.1.0 2962 | css-tree: 2.3.1 2963 | css-what: 6.1.0 2964 | csso: 5.0.5 2965 | picocolors: 1.0.1 2966 | 2967 | tailwindcss@3.4.10: 2968 | dependencies: 2969 | '@alloc/quick-lru': 5.2.0 2970 | arg: 5.0.2 2971 | chokidar: 3.6.0 2972 | didyoumean: 1.2.2 2973 | dlv: 1.1.3 2974 | fast-glob: 3.3.2 2975 | glob-parent: 6.0.2 2976 | is-glob: 4.0.3 2977 | jiti: 1.21.6 2978 | lilconfig: 2.1.0 2979 | micromatch: 4.0.7 2980 | normalize-path: 3.0.0 2981 | object-hash: 3.0.0 2982 | picocolors: 1.0.1 2983 | postcss: 8.4.41 2984 | postcss-import: 15.1.0(postcss@8.4.41) 2985 | postcss-js: 4.0.1(postcss@8.4.41) 2986 | postcss-load-config: 4.0.2(postcss@8.4.41) 2987 | postcss-nested: 6.2.0(postcss@8.4.41) 2988 | postcss-selector-parser: 6.1.2 2989 | resolve: 1.22.8 2990 | sucrase: 3.35.0 2991 | transitivePeerDependencies: 2992 | - ts-node 2993 | 2994 | temp-dir@2.0.0: {} 2995 | 2996 | temp-dir@3.0.0: {} 2997 | 2998 | temp-write@5.0.0: 2999 | dependencies: 3000 | graceful-fs: 4.2.11 3001 | is-stream: 2.0.1 3002 | temp-dir: 2.0.0 3003 | uuid: 8.3.2 3004 | 3005 | tempy@3.1.0: 3006 | dependencies: 3007 | is-stream: 3.0.0 3008 | temp-dir: 3.0.0 3009 | type-fest: 2.19.0 3010 | unique-string: 3.0.0 3011 | 3012 | thenify-all@1.6.0: 3013 | dependencies: 3014 | thenify: 3.3.1 3015 | 3016 | thenify@3.3.1: 3017 | dependencies: 3018 | any-promise: 1.3.0 3019 | 3020 | throttle-debounce@3.0.1: {} 3021 | 3022 | to-regex-range@5.0.1: 3023 | dependencies: 3024 | is-number: 7.0.0 3025 | 3026 | toggle-selection@1.0.6: {} 3027 | 3028 | tree-kill@1.2.2: {} 3029 | 3030 | ts-easing@0.2.0: {} 3031 | 3032 | ts-interface-checker@0.1.13: {} 3033 | 3034 | tslib@2.6.3: {} 3035 | 3036 | type-fest@1.4.0: {} 3037 | 3038 | type-fest@2.19.0: {} 3039 | 3040 | typed-css-modules@0.9.1: 3041 | dependencies: 3042 | camelcase: 6.3.0 3043 | chalk: 4.1.2 3044 | chokidar: 3.6.0 3045 | glob: 10.4.5 3046 | icss-replace-symbols: 1.1.0 3047 | is-there: 4.5.1 3048 | mkdirp: 3.0.1 3049 | postcss: 8.4.41 3050 | postcss-modules-extract-imports: 3.1.0(postcss@8.4.41) 3051 | postcss-modules-local-by-default: 4.0.5(postcss@8.4.41) 3052 | postcss-modules-scope: 3.2.0(postcss@8.4.41) 3053 | postcss-modules-values: 4.0.0(postcss@8.4.41) 3054 | yargs: 17.7.2 3055 | 3056 | typescript@5.5.4: {} 3057 | 3058 | undici-types@6.19.8: {} 3059 | 3060 | unicorn-magic@0.1.0: {} 3061 | 3062 | unique-string@3.0.0: 3063 | dependencies: 3064 | crypto-random-string: 4.0.0 3065 | 3066 | update-browserslist-db@1.1.0(browserslist@4.23.3): 3067 | dependencies: 3068 | browserslist: 4.23.3 3069 | escalade: 3.1.2 3070 | picocolors: 1.0.1 3071 | 3072 | use-sync-external-store@1.2.2(react@18.3.1): 3073 | dependencies: 3074 | react: 18.3.1 3075 | 3076 | util-deprecate@1.0.2: {} 3077 | 3078 | uuid@8.3.2: {} 3079 | 3080 | void-elements@3.1.0: {} 3081 | 3082 | which@2.0.2: 3083 | dependencies: 3084 | isexe: 2.0.0 3085 | 3086 | wrap-ansi@7.0.0: 3087 | dependencies: 3088 | ansi-styles: 4.3.0 3089 | string-width: 4.2.3 3090 | strip-ansi: 6.0.1 3091 | 3092 | wrap-ansi@8.1.0: 3093 | dependencies: 3094 | ansi-styles: 6.2.1 3095 | string-width: 5.1.2 3096 | strip-ansi: 7.1.0 3097 | 3098 | wrap-ansi@9.0.0: 3099 | dependencies: 3100 | ansi-styles: 6.2.1 3101 | string-width: 7.2.0 3102 | strip-ansi: 7.1.0 3103 | 3104 | y18n@5.0.8: {} 3105 | 3106 | yaml@2.5.0: {} 3107 | 3108 | yargs-parser@21.1.1: {} 3109 | 3110 | yargs@17.7.2: 3111 | dependencies: 3112 | cliui: 8.0.1 3113 | escalade: 3.1.2 3114 | get-caller-file: 2.0.5 3115 | require-directory: 2.1.1 3116 | string-width: 4.2.3 3117 | y18n: 5.0.8 3118 | yargs-parser: 21.1.1 3119 | 3120 | yocto-queue@1.1.1: {} 3121 | 3122 | zustand@4.5.5(react@18.3.1): 3123 | dependencies: 3124 | use-sync-external-store: 1.2.2(react@18.3.1) 3125 | optionalDependencies: 3126 | react: 18.3.1 3127 | --------------------------------------------------------------------------------