From c3a11d1f20b4f5f5dc5591697f349d1362ab85d5 Mon Sep 17 00:00:00 2001 From: Sakibul Islam Date: Thu, 28 Nov 2024 11:00:01 +0000 Subject: [PATCH 1/2] Add Dribbble plugin --- assets/dribbble.png | Bin 0 -> 117145 bytes package-lock.json | 1247 ++++++++++++++++- plugins/dribbble/.gitignore | 33 + plugins/dribbble/README.md | 9 + plugins/dribbble/eslint.config.js | 25 + plugins/dribbble/framer.json | 6 + plugins/dribbble/index.html | 14 + plugins/dribbble/package.json | 41 + plugins/dribbble/postcss.config.js | 6 + plugins/dribbble/public/icon.svg | 15 + plugins/dribbble/src/App.tsx | 41 + plugins/dribbble/src/api.ts | 209 +++ plugins/dribbble/src/auth.ts | 103 ++ plugins/dribbble/src/cms.ts | 139 ++ plugins/dribbble/src/components/Button.tsx | 13 + .../src/components/CenteredSpinner.tsx | 8 + .../src/components/CheckboxTextField.tsx | 45 + .../src/components/ErrorBoundaryFallback.tsx | 12 + .../dribbble/src/components/FieldMapper.tsx | 111 ++ plugins/dribbble/src/components/Icons.tsx | 12 + .../src/components/ScrollFadeContainer.tsx | 81 ++ plugins/dribbble/src/components/Spinner.tsx | 40 + .../src/components/spinner.module.css | 60 + plugins/dribbble/src/constants.ts | 66 + plugins/dribbble/src/globals.css | 64 + plugins/dribbble/src/main.tsx | 64 + plugins/dribbble/src/pages/Authenticate.tsx | 65 + plugins/dribbble/src/pages/MapFields.tsx | 115 ++ plugins/dribbble/src/sync.ts | 227 +++ plugins/dribbble/src/utils.ts | 25 + plugins/dribbble/src/vite-env.d.ts | 1 + plugins/dribbble/tailwind.config.js | 45 + plugins/dribbble/tsconfig.json | 23 + plugins/dribbble/vite.config.ts | 12 + 34 files changed, 2911 insertions(+), 66 deletions(-) create mode 100644 assets/dribbble.png create mode 100644 plugins/dribbble/.gitignore create mode 100644 plugins/dribbble/README.md create mode 100644 plugins/dribbble/eslint.config.js create mode 100644 plugins/dribbble/framer.json create mode 100644 plugins/dribbble/index.html create mode 100644 plugins/dribbble/package.json create mode 100644 plugins/dribbble/postcss.config.js create mode 100644 plugins/dribbble/public/icon.svg create mode 100644 plugins/dribbble/src/App.tsx create mode 100644 plugins/dribbble/src/api.ts create mode 100644 plugins/dribbble/src/auth.ts create mode 100644 plugins/dribbble/src/cms.ts create mode 100644 plugins/dribbble/src/components/Button.tsx create mode 100644 plugins/dribbble/src/components/CenteredSpinner.tsx create mode 100644 plugins/dribbble/src/components/CheckboxTextField.tsx create mode 100644 plugins/dribbble/src/components/ErrorBoundaryFallback.tsx create mode 100644 plugins/dribbble/src/components/FieldMapper.tsx create mode 100644 plugins/dribbble/src/components/Icons.tsx create mode 100644 plugins/dribbble/src/components/ScrollFadeContainer.tsx create mode 100644 plugins/dribbble/src/components/Spinner.tsx create mode 100644 plugins/dribbble/src/components/spinner.module.css create mode 100644 plugins/dribbble/src/constants.ts create mode 100644 plugins/dribbble/src/globals.css create mode 100644 plugins/dribbble/src/main.tsx create mode 100644 plugins/dribbble/src/pages/Authenticate.tsx create mode 100644 plugins/dribbble/src/pages/MapFields.tsx create mode 100644 plugins/dribbble/src/sync.ts create mode 100644 plugins/dribbble/src/utils.ts create mode 100644 plugins/dribbble/src/vite-env.d.ts create mode 100644 plugins/dribbble/tailwind.config.js create mode 100644 plugins/dribbble/tsconfig.json create mode 100644 plugins/dribbble/vite.config.ts diff --git a/assets/dribbble.png b/assets/dribbble.png new file mode 100644 index 0000000000000000000000000000000000000000..885a722dce8a2f562320d62bdeff964c7a02add1 GIT binary patch literal 117145 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 DY~o;v literal 0 HcmV?d00001 diff --git a/package-lock.json b/package-lock.json index fbd3747a..706ebeb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,6 +53,267 @@ "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/runtime": { "version": "7.25.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", @@ -64,6 +325,64 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -448,14 +767,64 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.0.tgz", + "integrity": "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/core": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.0.tgz", + "integrity": "sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", @@ -510,6 +879,29 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", + "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@floating-ui/core": { "version": "1.6.8", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", @@ -584,6 +976,44 @@ "node": ">= 0.12" } }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -641,6 +1071,20 @@ "deprecated": "Use @eslint/object-schema instead", "dev": true }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@icons/material": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", @@ -2753,20 +3197,22 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.56.2", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.56.2.tgz", - "integrity": "sha512-gor0RI3/R5rVV3gXfddh1MM+hgl0Z4G7tj6Xxpq6p2I03NGPaJ8dITY9Gz05zYYb/EJq9vPas/T4wn9EaDPd4Q==", + "version": "5.61.5", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.61.5.tgz", + "integrity": "sha512-iG5vqurEOEbv+paP6kW3zPENa99kSIrd1THISJMaTwVlJ+N5yjVDNOUwp9McK2DWqWCXM3v13ubBbAyhxT78UQ==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" } }, "node_modules/@tanstack/react-query": { - "version": "5.56.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.56.2.tgz", - "integrity": "sha512-SR0GzHVo6yzhN72pnRhkEFRAHMsUo5ZPzAxfTMvUxFIDVS6W9LYUp6nXW3fcHVdg0ZJl8opSH85jqahvm6DSVg==", + "version": "5.61.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.61.5.tgz", + "integrity": "sha512-rjy8aqPgBBEz/rjJnpnuhi8TVkVTorMUsJlM3lMvrRb5wK6yzfk34Er0fnJ7w/4qyF01SnXsLB/QsTBsLF5PaQ==", + "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.56.2" + "@tanstack/query-core": "5.61.5" }, "funding": { "type": "github", @@ -2796,14 +3242,59 @@ "integrity": "sha512-BV9NplVgLmSi4mwKzD8BD/NQ8erOY/nUE/GpgWe2ckx+wIQF5RyRirn/QsSSCPeulVpc3RA/iJt6DpfTIZps0Q==", "dev": true }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", "@types/node": "*", "@types/responselike": "^1.0.0" } @@ -3555,6 +4046,26 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", + "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.0", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, "node_modules/@vitejs/plugin-react-swc": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.7.0.tgz", @@ -3568,10 +4079,11 @@ } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -3771,6 +4283,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "browserslist": "^4.23.3", "caniuse-lite": "^1.0.30001646", @@ -3996,9 +4509,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", - "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "dev": true, "funding": [ { @@ -4014,11 +4527,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -4126,9 +4640,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001660", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", - "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==", + "version": "1.0.30001684", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz", + "integrity": "sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==", "dev": true, "funding": [ { @@ -4143,7 +4657,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chalk": { "version": "4.1.2", @@ -4226,7 +4741,8 @@ "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" }, "node_modules/cliui": { "version": "8.0.1", @@ -4397,11 +4913,19 @@ "node": ">= 0.6" } }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4836,6 +5360,10 @@ "resolved": "plugins/doodles", "link": true }, + "node_modules/dribbble": { + "resolved": "plugins/dribbble", + "link": true + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -4843,10 +5371,11 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.5.24", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.24.tgz", - "integrity": "sha512-0x0wLCmpdKFCi9ulhvYZebgcPmHTkFVUfU2wzDykadkslKwT4oAmDTHEKLnlrDsMGZe4B+ksn8quZfZjYsBetA==", - "dev": true + "version": "1.5.65", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.65.tgz", + "integrity": "sha512-PWVzBjghx7/wop6n22vS2MLU8tKGd4Q91aCEGhG/TYmW6PP5OcSXcdnxTe1NNt0T66N8D6jxh4kC8UsdzOGaIw==", + "dev": true, + "license": "ISC" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -5589,6 +6118,16 @@ "node": ">=10" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -6077,6 +6616,19 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -6100,6 +6652,19 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -7164,9 +7729,10 @@ "link": true }, "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -7226,9 +7792,9 @@ "link": true }, "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "funding": [ { "type": "opencollective", @@ -7243,9 +7809,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { @@ -7587,9 +8154,10 @@ } }, "node_modules/react-error-boundary": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.13.tgz", - "integrity": "sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.1.2.tgz", + "integrity": "sha512-GQDxZ5Jd+Aq/qUxbCm1UtzmL/s++V7zKgE8yMktJiCQXCCFZnMZh9ng+6/Ne6PjNSXH0L9CjeOEREfRnq6Duag==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5" }, @@ -7627,6 +8195,16 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react-remove-scroll": { "version": "2.5.7", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz", @@ -8875,33 +9453,34 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.12", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.12.tgz", - "integrity": "sha512-Htf/gHj2+soPb9UayUNci/Ja3d8pTmu9ONTfh4QY8r3MATTZOzmv6UYWF7ZwikEIC8okpfqmGqrmDehua8mF8w==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.15.tgz", + "integrity": "sha512-r4MeXnfBmSOuKUWmXe6h2CcyfzJCEk4F0pptO5jlnYSIViUkVmsawj80N5h2lO3gwcmSb4n3PuN+e+GC1Guylw==", "dev": true, + "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", - "chokidar": "^3.5.3", + "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.3.0", + "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.21.0", + "jiti": "^1.21.6", "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", + "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", @@ -9177,6 +9756,244 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.16.0.tgz", + "integrity": "sha512-wDkVmlY6O2do4V+lZd0GtRfbtXbeD0q9WygwXXSJnC1xorE8eqyC2L1tJimqpSeFrOzRlYtWnUp/uzgHQOgfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.16.0", + "@typescript-eslint/parser": "8.16.0", + "@typescript-eslint/utils": "8.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.16.0.tgz", + "integrity": "sha512-5YTHKV8MYlyMI6BaEG7crQ9BhSc8RxzshOReKwZwRWN0+XvvTOm+L/UYLCYxFpfwYuAAqhxiq4yae0CMFwbL7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/type-utils": "8.16.0", + "@typescript-eslint/utils": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.16.0.tgz", + "integrity": "sha512-D7DbgGFtsqIPIFMPJwCad9Gfi/hC0PWErRRHFnaCWoEDYi5tQUDiJCTmGUbBiLzjqAck4KcXt9Ayj0CNlIrF+w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/typescript-estree": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.16.0.tgz", + "integrity": "sha512-mwsZWubQvBki2t5565uxF0EYvG+FwdFb8bMtDuGQLdCCnGPrDEDvm1gtfynuKlnpzeBRqdFCkMf9jg1fnAK8sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.16.0.tgz", + "integrity": "sha512-IqZHGG+g1XCWX9NyqnI/0CX5LL8/18awQqmkZSl2ynn8F76j579dByc0jhfVSnSnhf7zv76mKBQv9HQFKvDCgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.16.0", + "@typescript-eslint/utils": "8.16.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.16.0.tgz", + "integrity": "sha512-NzrHj6thBAOSE4d9bsuRNMvk+BvaQvmY4dDglgkgGC0EW/tB3Kelnp3tAKH87GEwzoxgeQn9fNGRyFJM/xd+GQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.16.0.tgz", + "integrity": "sha512-E2+9IzzXMc1iaBy9zmo+UYvluE3TW7bCGWSF41hVWUE01o8nzr1rvOQYSxelxr6StUvRcTMe633eY8mXASMaNw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.16.0.tgz", + "integrity": "sha512-C1zRy/mOL8Pj157GiX4kaw7iyRLKfJXBR3L82hk5kS/GyHcOFmy4YUq/zfZti72I9wnuQtA/+xzft4wCC8PJdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/typescript-estree": "8.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.16.0.tgz", + "integrity": "sha512-pq19gbaMOmFE3CbL0ZB8J8BFCo2ckfHBfaIsaOZgBIF4EoISJIdLX5xRhd0FGB0LlHReNRuzoJoMGpTjq8F2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.16.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", @@ -9192,9 +10009,9 @@ "link": true }, "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "dev": true, "funding": [ { @@ -9210,9 +10027,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -10270,6 +11088,303 @@ "vite-plugin-framer": "^1.0.1" } }, + "plugins/dribbble": { + "version": "0.0.0", + "dependencies": { + "@tanstack/react-query": "^5.61.5", + "classnames": "^2.5.1", + "framer-plugin": "^2", + "p-limit": "^6.1.0", + "react": "^18", + "react-dom": "^18", + "react-error-boundary": "^4.1.2", + "vite-plugin-mkcert": "^1" + }, + "devDependencies": { + "@eslint/js": "^9", + "@types/react": "^18", + "@types/react-dom": "^18", + "@vitejs/plugin-react": "^4.3.1", + "@vitejs/plugin-react-swc": "^3", + "autoprefixer": "^10.4.20", + "eslint": "^9.9.0", + "eslint-plugin-react-hooks": "^5.1.0-rc.0", + "eslint-plugin-react-refresh": "^0.4.9", + "globals": "^15.9.0", + "postcss": "^8.4.49", + "tailwindcss": "^3.4.15", + "typescript": "^5.3", + "typescript-eslint": "^8.0.1", + "vite": "^5", + "vite-plugin-framer": "^1" + } + }, + "plugins/dribbble/node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "plugins/dribbble/node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "plugins/dribbble/node_modules/@eslint/js": { + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz", + "integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "plugins/dribbble/node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "plugins/dribbble/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "plugins/dribbble/node_modules/eslint": { + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz", + "integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.9.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.15.0", + "@eslint/plugin-kit": "^0.2.3", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.5", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "plugins/dribbble/node_modules/eslint-plugin-react-hooks": { + "version": "5.1.0-rc-fb9a90fa48-20240614", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0-rc-fb9a90fa48-20240614.tgz", + "integrity": "sha512-xsiRwaDNF5wWNC4ZHLut+x/YcAxksUd9Rizt7LaEn3bV8VyYRpXnRJQlLOfYaVy9esk4DFP4zPPnoNVjq5Gc0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "plugins/dribbble/node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "plugins/dribbble/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "plugins/dribbble/node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "plugins/dribbble/node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "plugins/dribbble/node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "plugins/dribbble/node_modules/globals": { + "version": "15.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.12.0.tgz", + "integrity": "sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "plugins/dribbble/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "plugins/dribbble/node_modules/p-limit": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.1.0.tgz", + "integrity": "sha512-H0jc0q1vOzlEk0TqAKXKZxdl7kX3OFUzCnNVUnq5Pc3DGo0kpeaMuPqxQn235HibwBEb0/pm9dgKTjXy66fBkg==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "plugins/dribbble/node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "plugins/flip-image": { "version": "0.0.0", "dependencies": { diff --git a/plugins/dribbble/.gitignore b/plugins/dribbble/.gitignore new file mode 100644 index 00000000..25519452 --- /dev/null +++ b/plugins/dribbble/.gitignore @@ -0,0 +1,33 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies + +/node_modules +/.pnp +.pnp.js +.yarn + +# misc + +.DS_Store +\*.pem + +# files + +my-plugin +dev-plugin +dist + +# debug + +npm-debug.log* +yarn-debug.log* +yarn-error.log\* + +# local env files + +.env\*.local + +# Packed plugin + +plugin.zip diff --git a/plugins/dribbble/README.md b/plugins/dribbble/README.md new file mode 100644 index 00000000..5b861656 --- /dev/null +++ b/plugins/dribbble/README.md @@ -0,0 +1,9 @@ +# Dribbble Plugin + +Dribbble Plugin +- Sync shots +- Drop shots + +**By:** @sakib25800 + +![Dribbble Image](../../assets/dribbble.png) diff --git a/plugins/dribbble/eslint.config.js b/plugins/dribbble/eslint.config.js new file mode 100644 index 00000000..6e64b68b --- /dev/null +++ b/plugins/dribbble/eslint.config.js @@ -0,0 +1,25 @@ +import js from "@eslint/js" +import globals from "globals" +import reactHooks from "eslint-plugin-react-hooks" +import reactRefresh from "eslint-plugin-react-refresh" +import tseslint from "typescript-eslint" + +export default tseslint.config( + { ignores: ["dist"] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ["**/*.{ts,tsx}"], + languageOptions: { + ecmaVersion: 2022, + globals: globals.browser, + }, + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + }, + } +) diff --git a/plugins/dribbble/framer.json b/plugins/dribbble/framer.json new file mode 100644 index 00000000..f3d299b3 --- /dev/null +++ b/plugins/dribbble/framer.json @@ -0,0 +1,6 @@ +{ + "id": "357ab0", + "name": "Dribbble", + "modes": ["canvas", "configureManagedCollection", "syncManagedCollection"], + "icon": "/icon.svg" +} diff --git a/plugins/dribbble/index.html b/plugins/dribbble/index.html new file mode 100644 index 00000000..564cb956 --- /dev/null +++ b/plugins/dribbble/index.html @@ -0,0 +1,14 @@ + + + + + + + Dribbble + + +

+ + + + diff --git a/plugins/dribbble/package.json b/plugins/dribbble/package.json new file mode 100644 index 00000000..905e51d7 --- /dev/null +++ b/plugins/dribbble/package.json @@ -0,0 +1,41 @@ +{ + "name": "dribbble", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview", + "pack": "npx framer-plugin-tools@latest pack" + }, + "dependencies": { + "@tanstack/react-query": "^5.61.5", + "classnames": "^2.5.1", + "framer-plugin": "^2", + "p-limit": "^6.1.0", + "react": "^18", + "react-dom": "^18", + "react-error-boundary": "^4.1.2", + "vite-plugin-mkcert": "^1" + }, + "devDependencies": { + "@eslint/js": "^9", + "@types/react": "^18", + "@types/react-dom": "^18", + "@vitejs/plugin-react": "^4.3.1", + "@vitejs/plugin-react-swc": "^3", + "autoprefixer": "^10.4.20", + "eslint": "^9.9.0", + "eslint-plugin-react-hooks": "^5.1.0-rc.0", + "eslint-plugin-react-refresh": "^0.4.9", + "globals": "^15.9.0", + "postcss": "^8.4.49", + "tailwindcss": "^3.4.15", + "typescript": "^5.3", + "typescript-eslint": "^8.0.1", + "vite": "^5", + "vite-plugin-framer": "^1" + } +} diff --git a/plugins/dribbble/postcss.config.js b/plugins/dribbble/postcss.config.js new file mode 100644 index 00000000..d41ad635 --- /dev/null +++ b/plugins/dribbble/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/plugins/dribbble/public/icon.svg b/plugins/dribbble/public/icon.svg new file mode 100644 index 00000000..98c01e88 --- /dev/null +++ b/plugins/dribbble/public/icon.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/plugins/dribbble/src/App.tsx b/plugins/dribbble/src/App.tsx new file mode 100644 index 00000000..d478e4f8 --- /dev/null +++ b/plugins/dribbble/src/App.tsx @@ -0,0 +1,41 @@ +import { useEffect, useState } from "react" +import { getPluginContext, PluginContext, useSyncShotsMutation } from "./sync" +import { framer } from "framer-plugin" +import auth from "./auth" +import Authenticate from "./pages/Authenticate" +import MapFields from "./pages/MapFields" + +export function AuthenticatedApp({ context }: { context: PluginContext }) { + const syncMutation = useSyncShotsMutation({ + onSuccess: result => (result.status === "success" ? framer.closePlugin("Synchronization successful") : null), + onError: e => framer.notify(e.message, { variant: "error" }), + }) + + useEffect(() => { + framer.showUI({ + width: 340, + height: 410, + }) + }, []) + + return +} + +export function App({ context }: { context: PluginContext }) { + const [pluginContext, setPluginContext] = useState(context) + + const handleAuthenticated = async () => { + setPluginContext(await getPluginContext()) + } + + if (!auth.isAuthenticated()) { + framer.showUI({ + width: 260, + height: 345, + }) + + return + } + + return +} diff --git a/plugins/dribbble/src/api.ts b/plugins/dribbble/src/api.ts new file mode 100644 index 00000000..41672b66 --- /dev/null +++ b/plugins/dribbble/src/api.ts @@ -0,0 +1,209 @@ +import auth from "./auth" +import pLimit from "p-limit" + +type RequestQueryParams = Record + +interface RequestOptions { + path: string + method?: string + query?: RequestQueryParams + // eslint-disable-next-line + body?: any +} + +interface PaginationParams extends Record { + page: number + per_page: number +} + +export interface Shot { + id: number + title: string + description: string + width: number + height: number + images: { + hidpi: string | null + normal: string + teaser: string + } + low_profile: boolean + published_at: string + updated_at: string + html_url: string + animated: boolean + tags: string[] + attachments: Array<{ + id: number + url: string + thumbnail_url: string + size: number + content_type: string + created_at: string + }> + projects: Array<{ + id: number + name: string + description: string + shots_count: number + created_at: string + updated_at: string + }> + team?: { + id: number + name: string + login: string + html_url: string + avatar_url: string + bio: string + location: string + links: { + web: string + twitter: string + } + type: string + created_at: string + updated_at: string + } + video?: { + id: number + duration: number + width: number + height: number + url: string + small_preview_url: string + large_preview_url: string + xlarge_preview_url: string + } +} + +const DRIBBLE_BASE_URL = "https://api.dribbble.com/v2" +const MAX_PAGE_SIZE = 100 // v2 pagination limit +const CONCURRENCY_LIMIT = 10 // v2 limit: 60 requests per minute + +const request = async ({ + path, + method, + query, + body, +}: RequestOptions): Promise<{ data: T; headers: Headers }> => { + const tokens = auth.tokens.getOrThrow() + const url = new URL(`${DRIBBLE_BASE_URL}${path}`) + + if (query) { + for (const [key, value] of Object.entries(query)) { + if (value !== undefined) { + if (Array.isArray(value)) { + value.forEach(val => url.searchParams.append(key, decodeURIComponent(val))) + } else { + url.searchParams.append(key, String(value)) + } + } + } + } + + const res = await fetch(url, { + method: method?.toUpperCase() ?? "GET", + body: body ? JSON.stringify(body) : undefined, + headers: { + Authorization: `Bearer ${tokens.accessToken}`, + }, + }) + + if (method === "delete" && res.status === 204) { + return { data: {} as T, headers: res.headers } + } + + if (!res.ok) { + throw new Error(`Failed to fetch Dribbble API: ${res.status}`) + } + + const data = await res.json() + return { data: data as T, headers: res.headers } +} + +const parseLinkHeader = (headers: Headers): { next?: string } => { + const linkHeader = headers.get("Link") + if (!linkHeader) return {} + + const links = linkHeader.split(",").reduce( + (acc, part) => { + const match = part.match(/<(.+)>;\s*rel="(\w+)"/) + if (match) { + const [, url, rel] = match + return { ...acc, [rel]: url } + } + return acc + }, + {} as Record + ) + + return { next: links["next"] } +} + +const paginatedRequest = async (options: RequestOptions): Promise<{ data: T; nextUrl?: string }> => { + const { data, headers } = await request(options) + const { next } = parseLinkHeader(headers) + + return { + data, + nextUrl: next, + } +} + +export const fetchShots = async (params: PaginationParams): Promise<{ shots: Shot[]; nextUrl?: string }> => { + const response = await paginatedRequest({ + path: "/user/shots", + query: params, + }) + + return { + shots: response.data, + nextUrl: response.nextUrl, + } +} + +export const fetchAllShots = async (max: number): Promise => { + const limit = pLimit(CONCURRENCY_LIMIT) + let allShots: Shot[] = [] + + // Get initial page + const { shots: firstPage, nextUrl } = await fetchShots({ + page: 1, + per_page: MAX_PAGE_SIZE, + }) + + allShots = [...firstPage] + + const urlsToFetch: string[] = [] + let currentNextUrl = nextUrl + + // Collect all URLs to fetch + while (currentNextUrl && allShots.length + urlsToFetch.length * MAX_PAGE_SIZE < max) { + urlsToFetch.push(currentNextUrl) + const { nextUrl } = await fetchShots({ + page: Number(new URL(currentNextUrl).searchParams.get("page")), + per_page: MAX_PAGE_SIZE, + }) + currentNextUrl = nextUrl + } + + const results = await Promise.all( + urlsToFetch.map(url => + limit(() => + fetchShots({ + page: Number(new URL(url).searchParams.get("page")), + per_page: MAX_PAGE_SIZE, + }) + ) + ) + ) + + allShots = allShots.concat(...results.map(r => r.shots)) + + if (allShots.length > max) { + allShots = allShots.slice(0, max) + } + + return allShots +} diff --git a/plugins/dribbble/src/auth.ts b/plugins/dribbble/src/auth.ts new file mode 100644 index 00000000..7feb5877 --- /dev/null +++ b/plugins/dribbble/src/auth.ts @@ -0,0 +1,103 @@ +export interface Tokens { + access_token: string + token_type: "bearer" + scope: string +} + +export interface StoredTokens { + createdAt: number + accessToken: string + scope: string +} + +export interface Authorize { + url: string + writeKey: string + readKey: string +} + +const PLUGIN_TOKENS_KEY = "dribbbleTokens" +const isLocal = () => window.location.hostname.includes("localhost") +export const AUTH_URI = isLocal() ? "https://localhost:8787" : "https://oauth.fetch.tools/dribbble-plugin" + +class Auth { + storedTokens?: StoredTokens | null + + async authorize() { + const res = await fetch(`${AUTH_URI}/authorize`, { + method: "POST", + }) + + if (res.status !== 200) { + throw new Error("Failed to generate OAuth URL.") + } + + return (await res.json()) as Authorize + } + + async fetchTokens(readKey: string) { + const res = await fetch(`${AUTH_URI}/poll?readKey=${readKey}`, { + method: "POST", + }) + + if (res.status !== 200) { + throw new Error("Something went wrong polling for tokens.") + } + + const tokens = (await res.json()) as Tokens + + this.tokens.save(tokens) + return tokens + } + + isAuthenticated() { + const tokens = this.tokens.get() + if (!tokens) return false + + return true + } + + logout() { + this.tokens.clear() + } + + public readonly tokens = { + save: (tokens: Tokens) => { + const storedTokens: StoredTokens = { + createdAt: Date.now(), + accessToken: tokens.access_token, + scope: tokens.scope, + } + + this.storedTokens = storedTokens + window.localStorage.setItem(PLUGIN_TOKENS_KEY, JSON.stringify(storedTokens)) + + return storedTokens + }, + get: (): StoredTokens | null => { + if (this.storedTokens) return this.storedTokens + + const serializedTokens = window.localStorage.getItem(PLUGIN_TOKENS_KEY) + if (!serializedTokens) return null + + const storedTokens = JSON.parse(serializedTokens) as StoredTokens + + this.storedTokens = storedTokens + return storedTokens + }, + getOrThrow: (): StoredTokens => { + const tokens = this.tokens.get() + if (!tokens) { + throw new Error("Dribbble API token missing") + } + + return tokens + }, + clear: () => { + this.storedTokens = null + window.localStorage.removeItem(PLUGIN_TOKENS_KEY) + }, + } +} + +export default new Auth() diff --git a/plugins/dribbble/src/cms.ts b/plugins/dribbble/src/cms.ts new file mode 100644 index 00000000..3bc2d7cc --- /dev/null +++ b/plugins/dribbble/src/cms.ts @@ -0,0 +1,139 @@ +import { framer, ManagedCollectionField } from "framer-plugin" +import { useEffect } from "react" + +export const MAX_CMS_ITEMS = 10_000 +export const PLUGIN_LOG_SYNC_KEY = "dribbbleLogSyncResult" +export const FIELD_DELIMITER = "rfa4Emr21pUgs0in" +export const SLUG_COMPATIBLE_FIELDS = ["string", "formattedText"] + +export interface ItemResult { + fieldName?: string + message: string +} + +export interface SyncStatus { + errors: ItemResult[] + warnings: ItemResult[] + info: ItemResult[] +} + +export interface SyncResult extends SyncStatus { + status: "success" | "completed_with_errors" +} + +export type FieldsById = Map + +const isLoggingEnabled = () => { + return localStorage.getItem(PLUGIN_LOG_SYNC_KEY) === "true" +} + +export function logSyncResult(result: SyncResult, collectionItems?: Record[]) { + if (!isLoggingEnabled()) return + + if (collectionItems) { + console.table(collectionItems) + } + + if (result.errors.length > 0) { + console.log("Completed errors:") + console.table(result.errors) + } + + if (result.warnings.length > 0) { + console.log("Completed warnings:") + console.table(result.warnings) + } + + console.log("Completed info:") + console.table(result.info) +} + +export const useLoggingToggle = () => { + useEffect(() => { + const isLoggingEnabled = () => localStorage.getItem(PLUGIN_LOG_SYNC_KEY) === "true" + + const toggle = () => { + const newState = !isLoggingEnabled() + localStorage.setItem(PLUGIN_LOG_SYNC_KEY, newState ? "true" : "false") + framer.notify(`Logging ${newState ? "enabled" : "disabled"}`, { variant: "info" }) + } + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.ctrlKey && e.shiftKey && e.key === "L") { + e.preventDefault() + toggle() + } + } + + document.addEventListener("keydown", handleKeyDown) + + return () => { + document.removeEventListener("keydown", handleKeyDown) + } + }, []) +} + +// Match everything except for letters, numbers and parentheses. +const nonSlugCharactersRegExp = /[^\p{Letter}\p{Number}()]+/gu +// Match leading/trailing dashes, for trimming purposes. +const trimSlugRegExp = /^-+|-+$/gu + +export function getPossibleSlugFields(fields: ManagedCollectionField[]) { + return fields.filter(field => SLUG_COMPATIBLE_FIELDS.includes(field.type)) +} + +/** + * Takes a freeform string and removes all characters except letters, numbers, + * and parentheses. Also makes it lower case, and separates words by dashes. + * This makes the value URL safe. + */ +export function slugify(value: string): string { + return value.toLowerCase().replace(nonSlugCharactersRegExp, "-").replace(trimSlugRegExp, "") +} + +/** + * Generates an 8-character unique ID from a text using the djb2 hash function. + * Converts the 32-bit hash to an unsigned integer and then to a hex string. + */ +export function generateHash(text: string): string { + let hash = 5381 + for (let i = 0, len = text.length; i < len; i++) { + hash = (hash * 33) ^ text.charCodeAt(i) + } + // Convert to unsigned 32-bit integer + const unsignedHash = hash >>> 0 + return unsignedHash.toString(16).padStart(8, "0") +} + +/** + * Creates a consistent hash from an array of field IDs + */ +export function createFieldSetHash(fieldIds: string[]): string { + // Ensure consistent ordering + const sortedIds = [...fieldIds].sort() + return generateHash(sortedIds.join(FIELD_DELIMITER)) +} + +/** + * Processes a field set to determine the complementary fields + */ +export function computeFieldSets(params: { + currentFields: ManagedCollectionField[] + allPossibleFieldIds: string[] + storedHash: string +}) { + const { currentFields, allPossibleFieldIds, storedHash } = params + const currentFieldIds = currentFields.map(field => field.id) + + const includedFieldIds = currentFieldIds + + const excludedFieldIds = allPossibleFieldIds.filter(id => !currentFieldIds.includes(id)) + + const currentHash = createFieldSetHash(includedFieldIds) + + return { + includedFieldIds, + excludedFieldIds, + hasHashChanged: storedHash !== currentHash, + } +} diff --git a/plugins/dribbble/src/components/Button.tsx b/plugins/dribbble/src/components/Button.tsx new file mode 100644 index 00000000..313ef979 --- /dev/null +++ b/plugins/dribbble/src/components/Button.tsx @@ -0,0 +1,13 @@ +import cx from "classnames" +import { Spinner } from "./Spinner" + +interface Props extends React.ButtonHTMLAttributes { + variant?: "primary" | "secondary" | "destructive" + isLoading?: boolean +} + +export const Button = ({ variant = "primary", children, className, isLoading = false, disabled, ...rest }: Props) => ( + +) diff --git a/plugins/dribbble/src/components/CenteredSpinner.tsx b/plugins/dribbble/src/components/CenteredSpinner.tsx new file mode 100644 index 00000000..1f270465 --- /dev/null +++ b/plugins/dribbble/src/components/CenteredSpinner.tsx @@ -0,0 +1,8 @@ +import { Spinner, SpinnerProps } from "./Spinner" +import cx from "classnames" + +export const CenteredSpinner = ({ className, size }: { className?: string; size?: SpinnerProps["size"] }) => ( +
+ +
+) diff --git a/plugins/dribbble/src/components/CheckboxTextField.tsx b/plugins/dribbble/src/components/CheckboxTextField.tsx new file mode 100644 index 00000000..70c2f2ea --- /dev/null +++ b/plugins/dribbble/src/components/CheckboxTextField.tsx @@ -0,0 +1,45 @@ +import { useState } from "react" +import cx from "classnames" + +interface Props { + value: string + disabled: boolean + checked: boolean + onChange: () => void +} + +export function CheckboxTextfield({ value, disabled, checked: initialChecked, onChange }: Props) { + const [checked, setChecked] = useState(initialChecked) + + const toggle = () => { + if (disabled) return + + setChecked(!checked) + onChange() + } + + return ( +
+ + +
+ ) +} diff --git a/plugins/dribbble/src/components/ErrorBoundaryFallback.tsx b/plugins/dribbble/src/components/ErrorBoundaryFallback.tsx new file mode 100644 index 00000000..fe05b105 --- /dev/null +++ b/plugins/dribbble/src/components/ErrorBoundaryFallback.tsx @@ -0,0 +1,12 @@ +import { QueryErrorResetBoundary } from "@tanstack/react-query" + +export const ErrorBoundaryFallback = () => ( + + {({ reset }) => ( +
+ Something went wrong... + +
+ )} +
+) diff --git a/plugins/dribbble/src/components/FieldMapper.tsx b/plugins/dribbble/src/components/FieldMapper.tsx new file mode 100644 index 00000000..c72db8e2 --- /dev/null +++ b/plugins/dribbble/src/components/FieldMapper.tsx @@ -0,0 +1,111 @@ +import { CheckboxTextfield } from "../components/CheckboxTextField" +import { IconChevron } from "../components/Icons" +import { ScrollFadeContainer } from "../components/ScrollFadeContainer" +import { assert } from "../utils" +import { ManagedCollectionField } from "framer-plugin" +import { Fragment, useMemo } from "react" +import cx from "classnames" + +export interface ManagedCollectionFieldConfig { + field: ManagedCollectionField | null + originalFieldName: string +} + +interface FieldMapperProps { + collectionFieldConfig: ManagedCollectionFieldConfig[] + fieldNameOverrides: Record + isFieldSelected: (fieldId: string) => boolean + onFieldToggle: (fieldId: string) => void + onFieldNameChange: (fieldId: string, value: string) => void + fromLabel?: string + toLabel?: string + className?: string + height?: number +} +const getInitialSortedFields = ( + fields: ManagedCollectionFieldConfig[], + isFieldSelected: (fieldId: string) => boolean +): ManagedCollectionFieldConfig[] => { + return [...fields].sort((a, b) => { + const aIsSelected = a.field && isFieldSelected(a.field.id) + const bIsSelected = b.field && isFieldSelected(b.field.id) + + // Sort based on whether the fields are selected + if (aIsSelected && !bIsSelected) return -1 + if (!aIsSelected && bIsSelected) return 1 + + // Then sort by whether they are supported fields + if (a.field && !b.field) return -1 + if (!a.field && b.field) return 1 + + return 0 + }) +} + +export const FieldMapper = ({ + collectionFieldConfig, + fieldNameOverrides, + isFieldSelected, + onFieldToggle, + onFieldNameChange, + fromLabel = "Column", + toLabel = "Field", + height, + className, +}: FieldMapperProps) => { + // We only want to sort on initial render + const sortedCollectionFieldConfig = useMemo( + () => getInitialSortedFields(collectionFieldConfig, isFieldSelected), + // eslint-disable-next-line react-hooks/exhaustive-deps + [collectionFieldConfig] + ) + + return ( + +
+ {fromLabel} + {toLabel} + {sortedCollectionFieldConfig.map((fieldConfig, i) => { + const isUnsupported = !fieldConfig.field + const isSelected = fieldConfig.field ? isFieldSelected(fieldConfig.field.id) : false + + return ( + + { + assert(fieldConfig.field) + onFieldToggle(fieldConfig.field.id) + }} + /> +
+ +
+ { + assert(fieldConfig.field) + onFieldNameChange(fieldConfig.field.id, e.target.value) + }} + /> +
+ ) + })} +
+
+ ) +} diff --git a/plugins/dribbble/src/components/Icons.tsx b/plugins/dribbble/src/components/Icons.tsx new file mode 100644 index 00000000..3bb1e087 --- /dev/null +++ b/plugins/dribbble/src/components/Icons.tsx @@ -0,0 +1,12 @@ +import cx from "classnames" + +export const IconChevron = ({ className }: { className?: string }) => ( + + + +) diff --git a/plugins/dribbble/src/components/ScrollFadeContainer.tsx b/plugins/dribbble/src/components/ScrollFadeContainer.tsx new file mode 100644 index 00000000..8ce83ae2 --- /dev/null +++ b/plugins/dribbble/src/components/ScrollFadeContainer.tsx @@ -0,0 +1,81 @@ +import React, { useEffect, useRef, useState, useCallback } from "react" + +interface Props { + children: React.ReactNode + className?: string + fadeHeight?: number + height: number +} + +export const ScrollFadeContainer = ({ children, className, fadeHeight = 45, height }: Props) => { + const [showTopFade, setShowTopFade] = useState(false) + const [showBottomFade, setShowBottomFade] = useState(false) + const containerRef = useRef(null) + const scrollTimeout = useRef() + + const checkScroll = useCallback(() => { + const element = containerRef.current + if (!element) return + + if (scrollTimeout.current) { + clearTimeout(scrollTimeout.current) + } + + scrollTimeout.current = setTimeout(() => { + requestAnimationFrame(() => { + if (!element) return + const { scrollTop, scrollHeight, clientHeight } = element + const scrollBottom = scrollHeight - scrollTop - clientHeight + + setShowTopFade(scrollTop > 10) + setShowBottomFade(scrollBottom > 10) + }) + }, 50) + }, []) + + useEffect(() => { + const element = containerRef.current + if (!element) return + + requestAnimationFrame(() => { + if (!element) return + const { scrollHeight, clientHeight } = element + setShowBottomFade(scrollHeight > clientHeight) + checkScroll() + }) + + element.addEventListener("scroll", checkScroll, { passive: true }) + + return () => { + if (scrollTimeout.current) { + clearTimeout(scrollTimeout.current) + } + + element.removeEventListener("scroll", checkScroll) + } + }, [checkScroll]) + + return ( +
+
+ {children} +
+
+
+
+ ) +} diff --git a/plugins/dribbble/src/components/Spinner.tsx b/plugins/dribbble/src/components/Spinner.tsx new file mode 100644 index 00000000..594ad9d9 --- /dev/null +++ b/plugins/dribbble/src/components/Spinner.tsx @@ -0,0 +1,40 @@ +import cx from "classnames" +import styles from "./spinner.module.css" + +export interface SpinnerProps { + /** Size of the spinner */ + size?: "normal" | "medium" | "large" + /** Set the spinner to have a static position inline with other content */ + inline?: boolean + className?: string + inheritColor?: boolean +} + +function styleForSize(size: SpinnerProps["size"]) { + switch (size) { + case "normal": + return styles.normalStyle + case "medium": + return styles.mediumStyle + case "large": + return styles.largeStyle + } +} + +function spinnerClassNames(size: SpinnerProps["size"] = "normal") { + return cx(styles.spin, styles.baseStyle, styleForSize(size)) +} + +export const Spinner = ({ size, inline = false, inheritColor, className, ...rest }: SpinnerProps) => { + return ( +
+ ) +} diff --git a/plugins/dribbble/src/components/spinner.module.css b/plugins/dribbble/src/components/spinner.module.css new file mode 100644 index 00000000..a0d1a7ac --- /dev/null +++ b/plugins/dribbble/src/components/spinner.module.css @@ -0,0 +1,60 @@ +.baseStyle { + --spinner-translate: 0; + background-color: #fff; +} + +.buttonWithDepthSpinner { + background-color: currentColor; +} + +.normalStyle { + width: 12px; + height: 12px; + -webkit-mask: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAApNJREFUSA2tlUtLlFEYgEe7mtFFQ8NLFBG1SJAwahtiLVy5ceVSEPQH1LZf0Lp9FO1bdYNyI7gyBFcS2kAzilYq3sfxeYZ55Zv6FGfohWfOd97vnPd23u9MXeZoaeb1Q7gPHXAFzsAS5GACxiEPqVKXqs1kmtAPQi+cAtedToz1PJ8ExxPwDl7CAlRImoN7rBiF86ABHTiGAzMIh8n3O+ifwWc4EL0n5TGTYQijvjNSxbUadozInUeGZ3nuh1WYgpIkM+hGMwYajAgdrfUn+AaLoLTCAxiAOxCZxL4RdB+gZMjxEjwHo4hS8Jh5DR9hz0mKaNizegoNEI7Wee6FXJTISG6AUgDr+QImoAiHie+mwZJow/PRps4uwnsnRj8Epqc4voVJJ8eULOtW4BFYYjPpgjc+3IWI2sh/wFeoVl6xYQ48dDGLfjOwc/ygQqz595hUMVouS9QHBm4l6k3HWpnBLqicgVrlCxujSbR1Swd+UDpQYRS/oVaZZ2MWwlZBB9Zdxf+QTYw8SRgq6sAvz7vH6JULEB9USVHFjzZskgPRQR4ayxoXXIdaHdg9HrB2rErByWxZERnYtrWUzD0esDbtTmXPhzXwHrKLxEUbYGbVyDkWWwkdadfqbPmwDXZSCyh2VAf8BM/nOOJH5Y0QopMt2I5U/KO4DZZJB47XwEX+ex0mGroMV8sLwp56gyuGwiyW4SZ4c8bt2cZzO7jBNeqtsRHbeZ1g9krsMbhfYPv/c5gecA+4yAVuitFszE5Hjva872OMdXagF19JIoOYWyozMTI3J0tmA6gLvXPfO1cc/XOqOLe/HbjwD8yCXRD3VNJROEgatiRZMJsKsbZHia3nAdphtq8fkiWyXJbBe8uM1aXKPouNnz2Bm1kwAAAAAElFTkSuQmCC"); + mask: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAApNJREFUSA2tlUtLlFEYgEe7mtFFQ8NLFBG1SJAwahtiLVy5ceVSEPQH1LZf0Lp9FO1bdYNyI7gyBFcS2kAzilYq3sfxeYZ55Zv6FGfohWfOd97vnPd23u9MXeZoaeb1Q7gPHXAFzsAS5GACxiEPqVKXqs1kmtAPQi+cAtedToz1PJ8ExxPwDl7CAlRImoN7rBiF86ABHTiGAzMIh8n3O+ifwWc4EL0n5TGTYQijvjNSxbUadozInUeGZ3nuh1WYgpIkM+hGMwYajAgdrfUn+AaLoLTCAxiAOxCZxL4RdB+gZMjxEjwHo4hS8Jh5DR9hz0mKaNizegoNEI7Wee6FXJTISG6AUgDr+QImoAiHie+mwZJow/PRps4uwnsnRj8Epqc4voVJJ8eULOtW4BFYYjPpgjc+3IWI2sh/wFeoVl6xYQ48dDGLfjOwc/ygQqz595hUMVouS9QHBm4l6k3HWpnBLqicgVrlCxujSbR1Swd+UDpQYRS/oVaZZ2MWwlZBB9Zdxf+QTYw8SRgq6sAvz7vH6JULEB9USVHFjzZskgPRQR4ayxoXXIdaHdg9HrB2rErByWxZERnYtrWUzD0esDbtTmXPhzXwHrKLxEUbYGbVyDkWWwkdadfqbPmwDXZSCyh2VAf8BM/nOOJH5Y0QopMt2I5U/KO4DZZJB47XwEX+ex0mGroMV8sLwp56gyuGwiyW4SZ4c8bt2cZzO7jBNeqtsRHbeZ1g9krsMbhfYPv/c5gecA+4yAVuitFszE5Hjva872OMdXagF19JIoOYWyozMTI3J0tmA6gLvXPfO1cc/XOqOLe/HbjwD8yCXRD3VNJROEgatiRZMJsKsbZHia3nAdphtq8fkiWyXJbBe8uM1aXKPouNnz2Bm1kwAAAAAElFTkSuQmCC"); + -webkit-mask-size: 12px; + mask-size: 12px; +} + +.mediumStyle { + width: 24px; + height: 24px; + -webkit-mask: url("data:image/png;base64,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"); + mask: url("data:image/png;base64,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"); + -webkit-mask-size: 24px; + mask-size: 24px; +} + +.largeStyle { + width: 30px; + height: 30px; + -webkit-mask: url("data:image/png;base64,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"); + mask: url("data:image/png;base64,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"); + -webkit-mask-size: 30px; + mask-size: 30px; +} + +.centeredStyle { + --spinner-translate: -50%; + position: absolute; + top: 50%; + left: 50%; + transform: translate(var(--spinner-translate), var(--spinner-translate)); +} + +.spin { + animation-duration: 800ms; + animation-iteration-count: infinite; + animation-name: spin; + animation-timing-function: linear; +} + +@keyframes spin { + 0% { + transform: translate(var(--spinner-translate), var(--spinner-translate)) rotate(0deg); + } + + 100% { + transform: translate(var(--spinner-translate), var(--spinner-translate)) rotate(360deg); + } +} diff --git a/plugins/dribbble/src/constants.ts b/plugins/dribbble/src/constants.ts new file mode 100644 index 00000000..82f8841a --- /dev/null +++ b/plugins/dribbble/src/constants.ts @@ -0,0 +1,66 @@ +import { ManagedCollectionField } from "framer-plugin" + +export const SHOT_FIELDS: ManagedCollectionField[] = [ + { + id: "id", + name: "Id", + type: "number", + userEditable: false, + }, + { + id: "title", + name: "Title", + type: "string", + userEditable: false, + }, + { + id: "images.hidpi", + name: "Image", + type: "image", + userEditable: false, + }, + { + id: "low_profile", + name: "Low Profile", + type: "boolean", + userEditable: false, + }, + { + id: "html_url", + name: "URL", + type: "link", + userEditable: false, + }, + { + id: "published_at", + name: "Published At", + type: "date", + userEditable: false, + }, + { + id: "updated_at", + name: "Updated At", + type: "date", + userEditable: false, + }, + { + id: "animated", + name: "Animated", + type: "boolean", + userEditable: false, + }, + // These fields SHOULD work but the Dribbble API + // returns null for some reason + // { + // id: "description", + // name: "Description", + // type: "formattedText", + // userEditable: false, + // }, + // { + // id: "video.url", + // name: "Video", + // type: "image", + // userEditable: false, + // }, +] diff --git a/plugins/dribbble/src/globals.css b/plugins/dribbble/src/globals.css new file mode 100644 index 00000000..8cbe1cc8 --- /dev/null +++ b/plugins/dribbble/src/globals.css @@ -0,0 +1,64 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + h6 { + @apply font-semibold text-primary leading-[1.2]; + } +} + +@layer utilities { + /* Chrome, Safari and Opera */ + .no-scrollbar::-webkit-scrollbar { + display: none; + } + + .no-scrollbar { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + } +} + +@layer components { + .row { + display: flex; + flex-direction: row; + gap: 10px; + } + + .col { + display: flex; + flex-direction: column; + gap: 10px; + } + + .row-lg { + display: flex; + flex-direction: row; + gap: 15px; + } + + .col-lg { + display: flex; + flex-direction: column; + gap: 15px; + } +} + +body, +html, +#root { + width: 100%; + height: 100%; + overflow: hidden; +} + +main { + width: 100%; + height: 100%; + padding: 15px; + display: flex; + flex-direction: column; + gap: 15px; +} diff --git a/plugins/dribbble/src/main.tsx b/plugins/dribbble/src/main.tsx new file mode 100644 index 00000000..49bb6da5 --- /dev/null +++ b/plugins/dribbble/src/main.tsx @@ -0,0 +1,64 @@ +import "./globals.css" +import "framer-plugin/framer.css" + +import { QueryClient, QueryClientProvider } from "@tanstack/react-query" +import React, { Suspense } from "react" +import ReactDOM from "react-dom/client" +import { App } from "./App.tsx" +import { framer } from "framer-plugin" +import { ErrorBoundary } from "react-error-boundary" +import { CenteredSpinner } from "./components/CenteredSpinner.tsx" +import { ErrorBoundaryFallback } from "./components/ErrorBoundaryFallback.tsx" +import { getPluginContext, shouldSyncImmediately, syncShots } from "./sync.ts" +import { assert } from "./utils.ts" + +export const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: 1, + staleTime: 1000 * 60 * 5, + refetchOnWindowFocus: false, + throwOnError: true, + }, + }, +}) + +function renderPlugin(app: React.ReactNode) { + const root = document.getElementById("root") + if (!root) throw new Error("Root element not found") + + ReactDOM.createRoot(root).render( + + + + }>{app} + + + + ) +} + +async function runPlugin() { + const mode = framer.mode + + try { + const pluginContext = await getPluginContext() + + if (mode === "syncManagedCollection" && shouldSyncImmediately(pluginContext)) { + assert(pluginContext.slugFieldId) + return syncShots({ + fields: pluginContext.collectionFields, + slugFieldId: pluginContext.slugFieldId, + includedFieldIds: pluginContext.includedFieldIds, + }).then(() => framer.closePlugin()) + } + renderPlugin() + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + framer.closePlugin("An unexpected error ocurred: " + message, { + variant: "error", + }) + } +} + +runPlugin() diff --git a/plugins/dribbble/src/pages/Authenticate.tsx b/plugins/dribbble/src/pages/Authenticate.tsx new file mode 100644 index 00000000..16f83559 --- /dev/null +++ b/plugins/dribbble/src/pages/Authenticate.tsx @@ -0,0 +1,65 @@ +import { useRef, useState } from "react" +import auth from "../auth" +import { Button } from "../components/Button" +import { getPluginContext, PluginContext } from "../sync" +import { framer } from "framer-plugin" + +interface Props { + onAuthenticated: (context: PluginContext) => void +} + +export default function Authenticate({ onAuthenticated }: Props) { + const [isLoading, setIsLoading] = useState(false) + const pollInterval = useRef>() + + const pollForTokens = (readKey: string) => { + if (pollInterval.current) { + clearInterval(pollInterval.current) + } + + return new Promise( + resolve => + (pollInterval.current = setInterval( + () => + auth.fetchTokens(readKey).then(tokens => { + clearInterval(pollInterval.current) + resolve(tokens) + }), + 1500 + )) + ) + } + + const login = async () => { + setIsLoading(true) + + try { + const authorization = await auth.authorize() + window.open(authorization.url) + await pollForTokens(authorization.readKey) + + onAuthenticated(await getPluginContext()) + } catch (e) { + framer.notify(e instanceof Error ? e.message : JSON.stringify(e), { variant: "error" }) + } finally { + setIsLoading(false) + } + } + + return ( +
+
+ Dribbble Logo +
+
Connect to Dribbble
+

+ Sign in to Dribbble to sync shots and import your content. +

+
+
+ +
+ ) +} diff --git a/plugins/dribbble/src/pages/MapFields.tsx b/plugins/dribbble/src/pages/MapFields.tsx new file mode 100644 index 00000000..5f82987d --- /dev/null +++ b/plugins/dribbble/src/pages/MapFields.tsx @@ -0,0 +1,115 @@ +import { useState } from "react" +import { Button } from "../components/Button" +import { SHOT_FIELDS } from "../constants" +import { assert, isDefined } from "../utils" +import { getPossibleSlugFields, useLoggingToggle } from "../cms" +import { FieldMapper } from "../components/FieldMapper" +import { PluginContext, SyncShotsMutationOptions } from "../sync" +import { ManagedCollectionField } from "framer-plugin" + +interface Props { + context: PluginContext + onSubmit: (opts: SyncShotsMutationOptions) => void + isLoading: boolean +} + +const getInitialSlugFieldId = (context: PluginContext, slugFields: ManagedCollectionField[]): string | null => { + if (context.type === "update" && context.slugFieldId) return context.slugFieldId + + return slugFields.length > 0 ? slugFields[0].id : null +} + +export default function MapFields({ context, onSubmit, isLoading }: Props) { + useLoggingToggle() + + const [includedFieldIds, setIncludedFieldIds] = useState>( + () => new Set(context.type === "update" ? context.includedFieldIds : SHOT_FIELDS.map(field => field.id)) + ) + + const slugFields = getPossibleSlugFields(SHOT_FIELDS).filter(field => includedFieldIds.has(field.id)) + const [slugFieldId, setSlugFieldId] = useState(() => getInitialSlugFieldId(context, slugFields)) + + const [collectionFieldConfig] = useState( + SHOT_FIELDS.map(field => ({ + field, + originalFieldName: field.name, + })) + ) + const [fieldNameOverrides, setFieldNameOverrides] = useState>(() => + context.type === "update" + ? Object.fromEntries(context.collectionFields.map(field => [field.id, field.name])) + : {} + ) + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + assert(slugFieldId) + + const allFields = collectionFieldConfig + .filter(fieldConfig => fieldConfig.field && includedFieldIds.has(fieldConfig.field.id)) + .map(fieldConfig => fieldConfig.field) + .filter(isDefined) + .map(field => { + // Create copy to prevent showing overriden name temporarily in the UI + const fieldCopy = { ...field } + if (fieldNameOverrides[field.id]) { + fieldCopy.name = fieldNameOverrides[field.id] + } + + return field + }) + + onSubmit({ includedFieldIds: Array.from(includedFieldIds), fields: allFields, slugFieldId }) + } + + return ( +
+
+ + +
+ includedFieldIds.has(fieldId)} + onFieldToggle={fieldId => { + setIncludedFieldIds(current => { + const nextSet = new Set(current) + if (nextSet.has(fieldId)) { + nextSet.delete(fieldId) + } else { + nextSet.add(fieldId) + } + return nextSet + }) + }} + onFieldNameChange={(fieldId, value) => { + setFieldNameOverrides(current => ({ + ...current, + [fieldId]: value, + })) + }} + /> +
+ +
+ + ) +} diff --git a/plugins/dribbble/src/sync.ts b/plugins/dribbble/src/sync.ts new file mode 100644 index 00000000..88c78c5c --- /dev/null +++ b/plugins/dribbble/src/sync.ts @@ -0,0 +1,227 @@ +import { framer, ManagedCollection, ManagedCollectionField } from "framer-plugin" +import { useMutation } from "@tanstack/react-query" +import { fetchAllShots, Shot } from "./api" +import { isDefined } from "./utils" +import { SHOT_FIELDS } from "./constants" +import { + computeFieldSets, + createFieldSetHash, + FieldsById, + logSyncResult, + MAX_CMS_ITEMS, + slugify, + SyncResult, + SyncStatus, +} from "./cms" +import auth from "./auth" + +const PLUGIN_INCLUDED_FIELDS_HASH = "dribbblePluginShotIncludedFieldsHash" +const PLUGIN_SLUG_FIELD_ID_KEY = "dribbblePluginSlugFieldId" + +export interface SyncShotsMutationOptions { + fields: ManagedCollectionField[] + includedFieldIds: string[] + slugFieldId: string +} + +export interface ProcessShotsParams { + fields: ManagedCollectionField[] + fieldsById: FieldsById + slugFieldId: string + unsyncedItemIds: Set +} + +export interface ProcessShotParams extends ProcessShotsParams { + shot: Shot + status: SyncStatus +} + +export interface PluginContextNew { + type: "new" + collection: ManagedCollection + isAuthenticated: boolean +} + +export interface PluginContextUpdate { + type: "update" + collection: ManagedCollection + collectionFields: ManagedCollectionField[] + includedFieldIds: string[] + slugFieldId: string + isAuthenticated: boolean +} + +export type PluginContext = PluginContextNew | PluginContextUpdate + +export function shouldSyncImmediately(pluginContext: PluginContext): pluginContext is PluginContextUpdate { + if (pluginContext.type !== "update") return false + + return true +} + +export async function getPluginContext(): Promise { + const collection = await framer.getManagedCollection() + const collectionFields = await collection.getFields() + + const isAuthenticated = auth.isAuthenticated() + const allPossibleFieldIds = SHOT_FIELDS.map(field => field.id) + + const [slugFieldId, rawIncludedFieldHash] = await Promise.all([ + collection.getPluginData(PLUGIN_SLUG_FIELD_ID_KEY), + collection.getPluginData(PLUGIN_INCLUDED_FIELDS_HASH), + ]) + + if (!rawIncludedFieldHash || !slugFieldId) { + return { + type: "new", + collection, + isAuthenticated, + } + } + + const { includedFieldIds } = computeFieldSets({ + currentFields: collectionFields, + allPossibleFieldIds, + storedHash: rawIncludedFieldHash, + }) + + return { + type: "update", + collection, + collectionFields, + includedFieldIds, + isAuthenticated, + slugFieldId, + } +} + +// eslint-disable-next-line +function isShotPropertyValueMissing(value: any): boolean { + // Usual suspects + if (value === null || value === undefined || value === "") { + return true + } + + // Empty array + if (Array.isArray(value)) { + return value.length === 0 + } + + // Empty object + if (typeof value === "object") { + return Object.keys(value).length === 0 + } + + return false +} + +function processShot({ shot, slugFieldId, fieldsById, unsyncedItemIds, status }: ProcessShotParams) { + let slugValue: string | null = null + const fieldData: Record = {} + const fieldId = String(shot.id) + unsyncedItemIds.delete(fieldId) + + for (const [propertyName, field] of fieldsById) { + // Allow for nested dot notation to access fields e.g. images.hidpi + let value: unknown + if (propertyName.includes(".")) { + value = propertyName.split(".").reduce((obj: unknown, key) => { + if (obj && typeof obj === "object") { + return (obj as Record)[key] + } + return undefined + }, shot) + } else { + value = shot[propertyName as keyof Shot] + } + + if (field.id === slugFieldId && typeof value === "string") { + slugValue = slugify(value) + } + + if (isShotPropertyValueMissing(value)) { + status.warnings.push({ + fieldName: propertyName, + message: `Value is missing for field ${field.name}`, + }) + } + + fieldData[propertyName] = value + } + + if (!slugValue) { + status.warnings.push({ message: "Slug missing. Skipping item." }) + return null + } + + return { id: fieldId, slug: slugValue, fieldData } +} + +function processShots(shots: Shot[], processShotsParams: ProcessShotsParams) { + const seenItemIds = new Set() + const status: SyncStatus = { + info: [], + warnings: [], + errors: [], + } + + const collectionItems = shots.map(shot => processShot({ shot, status, ...processShotsParams })).filter(isDefined) + + return { + collectionItems, + status, + seenItemIds, + } +} + +export async function syncShots({ fields, slugFieldId, includedFieldIds }: SyncShotsMutationOptions) { + const collection = await framer.getManagedCollection() + await collection.setFields(fields) + + const fieldsById = new Map(fields.map(field => [field.id, field])) + const unsyncedItemIds = new Set(await collection.getItemIds()) + + const allShots = await fetchAllShots(MAX_CMS_ITEMS) + + const { collectionItems, status } = processShots(allShots, { + fields, + fieldsById, + unsyncedItemIds, + slugFieldId, + }) + + await collection.addItems(collectionItems) + + const itemsToDelete = Array.from(unsyncedItemIds) + await collection.removeItems(itemsToDelete) + + await Promise.all([ + await collection.setPluginData(PLUGIN_INCLUDED_FIELDS_HASH, createFieldSetHash(includedFieldIds)), + await collection.setPluginData(PLUGIN_SLUG_FIELD_ID_KEY, slugFieldId), + ]) + + const result: SyncResult = { + status: status.errors.length === 0 ? "success" : "completed_with_errors", + errors: status.errors, + info: status.info, + warnings: status.warnings, + } + + logSyncResult(result, collectionItems) + + return result +} + +export const useSyncShotsMutation = ({ + onSuccess, + onError, +}: { + onSuccess?: (result: SyncResult) => void + onError?: (e: Error) => void +}) => { + return useMutation({ + mutationFn: (args: SyncShotsMutationOptions) => syncShots(args), + onSuccess, + onError, + }) +} diff --git a/plugins/dribbble/src/utils.ts b/plugins/dribbble/src/utils.ts new file mode 100644 index 00000000..a1e224f7 --- /dev/null +++ b/plugins/dribbble/src/utils.ts @@ -0,0 +1,25 @@ +export function assert(condition: unknown, ...msg: unknown[]): asserts condition { + if (condition) return + + const e = Error("Assertion Error" + (msg.length > 0 ? ": " + msg.join(" ") : "")) + // Hack the stack so the assert call itself disappears. Works in jest and in chrome. + if (e.stack) { + try { + const lines = e.stack.split("\n") + if (lines[1]?.includes("assert")) { + lines.splice(1, 1) + e.stack = lines.join("\n") + } else if (lines[0]?.includes("assert")) { + lines.splice(0, 1) + e.stack = lines.join("\n") + } + } catch { + // nothing + } + } + throw e +} + +export function isDefined(value: T): value is NonNullable { + return value !== undefined && value !== null +} diff --git a/plugins/dribbble/src/vite-env.d.ts b/plugins/dribbble/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/plugins/dribbble/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/plugins/dribbble/tailwind.config.js b/plugins/dribbble/tailwind.config.js new file mode 100644 index 00000000..6bad2ded --- /dev/null +++ b/plugins/dribbble/tailwind.config.js @@ -0,0 +1,45 @@ +/** @type {import('tailwindcss').Config} */ +export default { + darkMode: ["class", "[data-framer-theme='dark']"], + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + theme: { + extend: { + backgroundColor: { + primary: "var(--framer-color-bg)", + secondary: "var(--framer-color-bg-secondary)", + tertiary: "var(--framer-color-bg-tertiary)", + tertiaryDimmedLight: "rgba(243, 243, 243, 0.75)", + tertiaryDimmedDark: "rgba(43, 43, 43, 0.75)", + divider: "var(--framer-color-divider)", + tint: "var(--framer-color-tint)", + tintDimmed: "var(--framer-color-tint-dimmed)", + tintDark: "var(--framer-color-tint-dark)", + blackDimmed: "rgba(0, 0, 0, 0.5)", + "dribbble-primary": "#EA4C89", + "framer-red": "#FF3366", + "framer-blue": "#0099FF", + }, + colors: { + primary: "var(--framer-color-text)", + secondary: "var(--framer-color-text-secondary)", + tertiary: "var(--framer-color-text-tertiary)", + inverted: "var(--framer-color-text-inverted)", + tint: "var(--framer-color-tint)", + "dribbble-primary": "#EA4C89", + "framer-red": "#FF3366", + }, + borderColor: { + divider: "var(--framer-color-divider)", + }, + fontSize: { + "2xs": "10px", + }, + padding: { + 15: "15px", + }, + gridTemplateColumns: { + fieldPicker: "1fr 8px 1fr", + }, + }, + }, +} diff --git a/plugins/dribbble/tsconfig.json b/plugins/dribbble/tsconfig.json new file mode 100644 index 00000000..325606c6 --- /dev/null +++ b/plugins/dribbble/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ES2022", + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/plugins/dribbble/vite.config.ts b/plugins/dribbble/vite.config.ts new file mode 100644 index 00000000..7efb6ef8 --- /dev/null +++ b/plugins/dribbble/vite.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "vite" +import react from "@vitejs/plugin-react-swc" +import mkcert from "vite-plugin-mkcert" +import framer from "vite-plugin-framer" + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react(), mkcert(), framer()], + build: { + target: "ES2022", + }, +}) From d363f3d31f6a6f85b58058d2d7bdb46b5f3b1a13 Mon Sep 17 00:00:00 2001 From: Sakibul Islam Date: Thu, 28 Nov 2024 11:11:20 +0000 Subject: [PATCH 2/2] Refactor --- plugins/dribbble/README.md | 4 +--- plugins/dribbble/src/App.tsx | 14 +++++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/plugins/dribbble/README.md b/plugins/dribbble/README.md index 5b861656..02cdb4e3 100644 --- a/plugins/dribbble/README.md +++ b/plugins/dribbble/README.md @@ -1,8 +1,6 @@ # Dribbble Plugin -Dribbble Plugin -- Sync shots -- Drop shots +Sync shots from Dribbble **By:** @sakib25800 diff --git a/plugins/dribbble/src/App.tsx b/plugins/dribbble/src/App.tsx index d478e4f8..d51a178b 100644 --- a/plugins/dribbble/src/App.tsx +++ b/plugins/dribbble/src/App.tsx @@ -24,16 +24,20 @@ export function AuthenticatedApp({ context }: { context: PluginContext }) { export function App({ context }: { context: PluginContext }) { const [pluginContext, setPluginContext] = useState(context) + useEffect(() => { + if (!auth.isAuthenticated()) { + framer.showUI({ + width: 260, + height: 345, + }) + } + }, []) + const handleAuthenticated = async () => { setPluginContext(await getPluginContext()) } if (!auth.isAuthenticated()) { - framer.showUI({ - width: 260, - height: 345, - }) - return }