├── .github └── assets │ └── try-on-fc.png ├── .gitignore ├── .pre-commit-config.yaml ├── CNAME ├── LICENSE ├── README.md ├── _config.yml ├── demo ├── Dockerfile ├── docker-compose.yml └── init.sh ├── gallery ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── public │ └── favicon.ico ├── src │ ├── App.vue │ ├── index.css │ ├── main.js │ ├── router.js │ └── views │ │ ├── Home.vue │ │ └── Login.vue ├── tailwind.config.js ├── vite.config.js └── yarn.lock ├── package.json ├── photos ├── __init__.py ├── api.py ├── config │ ├── __init__.py │ ├── desktop.py │ └── docs.py ├── hooks.py ├── modules.txt ├── patches.txt ├── photos │ ├── __init__.py │ ├── desk_page │ │ └── photos │ │ │ └── photos.json │ ├── doctype │ │ ├── __init__.py │ │ ├── person │ │ │ ├── __init__.py │ │ │ ├── person.js │ │ │ ├── person.json │ │ │ ├── person.py │ │ │ ├── person_dashboard.py │ │ │ └── test_person.py │ │ ├── photo │ │ │ ├── __init__.py │ │ │ ├── photo.js │ │ │ ├── photo.json │ │ │ ├── photo.py │ │ │ └── test_photo.py │ │ ├── roi │ │ │ ├── __init__.py │ │ │ ├── roi.js │ │ │ ├── roi.json │ │ │ ├── roi.py │ │ │ └── test_roi.py │ │ ├── roi_object │ │ │ ├── __init__.py │ │ │ ├── roi_object.json │ │ │ └── roi_object.py │ │ └── roi_person │ │ │ ├── __init__.py │ │ │ ├── roi_person.json │ │ │ └── roi_person.py │ └── workspace │ │ └── photos │ │ └── photos.json ├── public │ ├── css │ │ └── font-awesome.min.css │ ├── favicon.ico │ └── logo.svg ├── reference.py ├── templates │ ├── __init__.py │ └── pages │ │ └── __init__.py ├── utils.py └── www │ └── __init__.py ├── pyproject.toml └── yarn.lock /.github/assets/try-on-fc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavindsouza/photos/768bad51b91e072e4da0671c4c8838455b8b3ca4/.github/assets/try-on-fc.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pyc 3 | *.egg-info 4 | *.swp 5 | tags 6 | photos/docs/current 7 | __pycache__/ 8 | node_modules 9 | .vscode 10 | build/ 11 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: '.git' 2 | default_stages: [commit] 3 | fail_fast: false 4 | 5 | repos: 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v4.3.0 8 | hooks: 9 | - id: trailing-whitespace 10 | files: "frappe.*" 11 | exclude: ".*json$|.*txt$|.*csv|.*md|.*svg" 12 | - id: check-yaml 13 | - id: check-merge-conflict 14 | - id: check-ast 15 | - id: check-json 16 | - id: check-toml 17 | - id: check-yaml 18 | - id: debug-statements 19 | 20 | - repo: https://github.com/timothycrosley/isort 21 | rev: 5.9.1 22 | hooks: 23 | - id: isort 24 | 25 | - repo: https://github.com/asottile/pyupgrade 26 | rev: v2.37.1 27 | hooks: 28 | - id: pyupgrade 29 | args: ['--py310-plus'] 30 | 31 | - repo: https://github.com/psf/black 32 | rev: 22.6.0 33 | hooks: 34 | - id: black 35 | 36 | - repo: https://github.com/PyCQA/flake8 37 | rev: 4.0.1 38 | hooks: 39 | - id: flake8 40 | additional_dependencies: 41 | - flake8-bugbear 42 | - flake8-implicit-str-concat 43 | args: ['--ignore', 'E501,E203'] 44 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | photos.gavv.in -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Gavin D'souza 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Photos 2 | 3 | AI Powered Facial Recognition & Photos Clustering (like Google Photos) for your Frappe sites. 4 | 5 | [![Try on Frappe Cloud](https://github.com/gavindsouza/photos/raw/main/.github/assets/try-on-fc.png)](https://frappecloud.com/marketplace/apps/photos?referrer=a6d8da54) 6 | 7 | #### Demo 8 | 9 | 11 | 12 | Clone the repo and run `docker compose up` in the demo folder to spin up a demo instance :) 13 | 14 | #### Usage 15 | 16 | You may follow these steps to try out Photos: 17 | 18 | 1. Login to Desk 19 | 1. Upload a new Image file 20 | 1. Navigate to the latest Photo Document 21 | 1. Scroll down to see the human faces detected 22 | 1. Click on each face to show label & more information 23 | 1. If label is not set for a given ROI, add it 24 | 25 | Photos gets better the more images you store and the more people you label. It may make a few mistakes guessing the names of the people, but it gets better with more data! 26 | 27 | #### Installation 28 | 29 | Installation is pretty straightforward **IF** you're accustomed to [Frappe](https://frappeframework.com) Apps: 30 | 31 | 1. `bench get-app https://github.com/gavindsouza/photos` 32 | 1. `bench --site photos-site install-app photos` 33 | 34 | If you don't know that and still want to try it out, here's the Frappe documentation for the same - https://frappeframework.com/docs/v14/user/en/installation. 35 | 36 | #### More Information 37 | 38 | This project consists of: 39 | 40 | - Photos: A Frappe App that contains the backend logic & Admin views 41 | - Gallery: A Vue 3 App that contains the tailored gallery UI _[WIP]_ 42 | 43 | #### Motivation 44 | 45 | The initial inspiration for this project was to be an `Open Source Alternative to Google Photos`. However, I've only been able to make a week's time for this in 2 years. If you're seriously looking for an open source alternative to Google Photos, I'd recommend https://photoprism.app/ which is everyting I wanted to build when I thought of doing this back in 2018 ^_^ 46 | 47 | #### License 48 | 49 | [MIT](LICENSE) 50 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | title: Photos 2 | description: Open source "alternative" to Google Photos. Maintained by Gavin D'souza 3 | email: me@gavv.in 4 | url: https://photos.gavv.in 5 | author: 6 | name: Gavin D'souza 7 | url: https://gavv.in/ 8 | 9 | remote_theme: pages-themes/cayman@v0.2.0 10 | plugins: 11 | - jekyll-remote-theme 12 | - jekyll-seo-tag 13 | -------------------------------------------------------------------------------- /demo/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM frappe/bench:latest 2 | USER root 3 | RUN apt-get update && apt-get install ffmpeg libsm6 libxext6 -y 4 | USER frappe 5 | -------------------------------------------------------------------------------- /demo/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | name: photos 3 | services: 4 | mariadb: 5 | image: mariadb:10.6 6 | command: 7 | - --character-set-server=utf8mb4 8 | - --collation-server=utf8mb4_unicode_ci 9 | - --skip-character-set-client-handshake 10 | environment: 11 | MYSQL_ROOT_PASSWORD: root 12 | volumes: 13 | - mariadb-data:/var/lib/mysql 14 | 15 | redis: 16 | image: redis:alpine 17 | 18 | frappe: 19 | build: 20 | context: . 21 | dockerfile: Dockerfile 22 | command: bash /workspace/init.sh 23 | environment: 24 | - SHELL=/bin/bash 25 | working_dir: /home/frappe 26 | volumes: 27 | - .:/workspace 28 | ports: 29 | - 8000:8000 30 | - 9000:9000 31 | 32 | volumes: 33 | mariadb-data: -------------------------------------------------------------------------------- /demo/init.sh: -------------------------------------------------------------------------------- 1 | #!bin/bash 2 | 3 | if [ -d "/home/frappe/frappe-bench/apps/frappe" ]; then 4 | echo "Bench already exists, skipping init" 5 | cd frappe-bench 6 | bench start 7 | else 8 | echo "Creating new bench..." 9 | fi 10 | 11 | bench --version 12 | 13 | bench -v init --skip-redis-config-generation frappe-bench 14 | 15 | cd frappe-bench 16 | 17 | # Use containers instead of localhost 18 | bench set-mariadb-host mariadb 19 | bench set-redis-cache-host redis:6379 20 | bench set-redis-queue-host redis:6379 21 | bench set-redis-socketio-host redis:6379 22 | 23 | # Remove redis, watch from Procfile 24 | sed -i '/redis/d' ./Procfile 25 | sed -i '/watch/d' ./Procfile 26 | 27 | pip install -U wheel cmake 28 | bench -v get-app gavindsouza/photos 29 | 30 | bench new-site photos.localhost \ 31 | --force \ 32 | --mariadb-root-password root \ 33 | --admin-password admin \ 34 | --no-mariadb-socket 35 | 36 | bench --site photos.localhost install-app photos 37 | bench --site photos.localhost set-config developer_mode 1 38 | bench --site photos.localhost clear-cache 39 | bench --site photos.localhost set-config mute_emails 1 40 | bench use photos.localhost 41 | 42 | bench start -------------------------------------------------------------------------------- /gallery/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Pyxels 8 | 9 | 10 |
11 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /gallery/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gallery", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build --base=/assets/photos/gallery/ && yarn copy-html-entry", 7 | "preview": "vite preview", 8 | "copy-html-entry": "cp ../photos/public/gallery/index.html ../photos/www/gallery.html" 9 | }, 10 | "dependencies": { 11 | "@fortawesome/fontawesome-svg-core": "^1.2.36", 12 | "@fortawesome/free-brands-svg-icons": "^5.15.4", 13 | "@fortawesome/vue-fontawesome": "^2.0.6", 14 | "@headlessui/vue": "^1.4.3", 15 | "@heroicons/vue": "^1.0.5", 16 | "frappe-ui": "^0.0.13", 17 | "socket.io-client": "^2.4.0", 18 | "vue": "^3.2.25", 19 | "vue-router": "^4" 20 | }, 21 | "devDependencies": { 22 | "@vitejs/plugin-vue": "^2.0.0", 23 | "autoprefixer": "^10.4.2", 24 | "postcss": "^8.4.5", 25 | "tailwindcss": "^3.0.15", 26 | "vite": "^2.7.2" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /gallery/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /gallery/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavindsouza/photos/768bad51b91e072e4da0671c4c8838455b8b3ca4/gallery/public/favicon.ico -------------------------------------------------------------------------------- /gallery/src/App.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gallery/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'frappe-ui/src/style.css'; 2 | -------------------------------------------------------------------------------- /gallery/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import { FrappeUI, Button } from "frappe-ui"; 3 | import router from "./router"; 4 | import App from "./App.vue"; 5 | import "./index.css"; 6 | 7 | let app = createApp(App); 8 | app.use(router); 9 | app.use(FrappeUI); 10 | app.component("Button", Button); 11 | app.mount("#app"); 12 | -------------------------------------------------------------------------------- /gallery/src/router.js: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router' 2 | 3 | const routes = [ 4 | { 5 | path: '/', 6 | name: 'Home', 7 | component: () => import('@/views/Home.vue'), 8 | }, 9 | { 10 | path: "/login", 11 | name: "Login", 12 | component: () => 13 | import("./views/Login.vue"), 14 | meta: { 15 | isLoginPage: true, 16 | }, 17 | props: true, 18 | }, 19 | ] 20 | 21 | let router = createRouter({ 22 | history: createWebHistory('/gallery'), 23 | routes, 24 | }) 25 | 26 | export default router -------------------------------------------------------------------------------- /gallery/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 230 | 231 | 296 | -------------------------------------------------------------------------------- /gallery/src/views/Login.vue: -------------------------------------------------------------------------------- 1 | 118 | 150 | -------------------------------------------------------------------------------- /gallery/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./index.html", 4 | "./src/**/*.{vue,js,ts,jsx,tsx}", 5 | ], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [], 10 | } 11 | -------------------------------------------------------------------------------- /gallery/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import vue from "@vitejs/plugin-vue"; 3 | import path from "path"; 4 | import { getProxyOptions } from "frappe-ui/src/utils/vite-dev-server"; 5 | import { webserver_port } from "../../../sites/common_site_config.json"; 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | plugins: [vue()], 10 | server: { 11 | port: 8080, 12 | proxy: getProxyOptions({ port: webserver_port }), 13 | }, 14 | resolve: { 15 | alias: { 16 | "@": path.resolve(__dirname, "src"), 17 | }, 18 | }, 19 | build: { 20 | outDir: `../${path.basename(path.resolve('..'))}/public/gallery`, 21 | emptyOutDir: true, 22 | target: "es2015", 23 | }, 24 | optimizeDeps: { 25 | include: ["frappe-ui > feather-icons"], 26 | } 27 | }); -------------------------------------------------------------------------------- /gallery/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/code-frame@^7.0.0": 6 | version "7.16.7" 7 | resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz" 8 | integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== 9 | dependencies: 10 | "@babel/highlight" "^7.16.7" 11 | 12 | "@babel/helper-validator-identifier@^7.16.7": 13 | version "7.16.7" 14 | resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz" 15 | integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== 16 | 17 | "@babel/highlight@^7.16.7": 18 | version "7.16.7" 19 | resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz" 20 | integrity sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw== 21 | dependencies: 22 | "@babel/helper-validator-identifier" "^7.16.7" 23 | chalk "^2.0.0" 24 | js-tokens "^4.0.0" 25 | 26 | "@babel/parser@^7.16.4": 27 | version "7.16.8" 28 | resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz" 29 | integrity sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw== 30 | 31 | "@fortawesome/fontawesome-common-types@^0.2.36": 32 | version "0.2.36" 33 | resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz#b44e52db3b6b20523e0c57ef8c42d315532cb903" 34 | integrity sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg== 35 | 36 | "@fortawesome/fontawesome-svg-core@^1.2.36": 37 | version "1.2.36" 38 | resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz#4f2ea6f778298e0c47c6524ce2e7fd58eb6930e3" 39 | integrity sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA== 40 | dependencies: 41 | "@fortawesome/fontawesome-common-types" "^0.2.36" 42 | 43 | "@fortawesome/free-brands-svg-icons@^5.15.4": 44 | version "5.15.4" 45 | resolved "https://registry.yarnpkg.com/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.4.tgz#ec8a44dd383bcdd58aa7d1c96f38251e6fec9733" 46 | integrity sha512-f1witbwycL9cTENJegcmcZRYyawAFbm8+c6IirLmwbbpqz46wyjbQYLuxOc7weXFXfB7QR8/Vd2u5R3q6JYD9g== 47 | dependencies: 48 | "@fortawesome/fontawesome-common-types" "^0.2.36" 49 | 50 | "@fortawesome/vue-fontawesome@^2.0.6": 51 | version "2.0.6" 52 | resolved "https://registry.yarnpkg.com/@fortawesome/vue-fontawesome/-/vue-fontawesome-2.0.6.tgz#87e691ed87f28f4667238573a29743f543a087f6" 53 | integrity sha512-V3vT3flY15AKbUS31aZOP12awQI3aAzkr2B1KnqcHLmwrmy51DW3pwyBczKdypV8QxBZ8U68Hl2XxK2nudTxpg== 54 | 55 | "@headlessui/vue@^1.4.3": 56 | version "1.4.3" 57 | resolved "https://registry.yarnpkg.com/@headlessui/vue/-/vue-1.4.3.tgz#685898891cde4d8a38a01945d4d84855d5fe7224" 58 | integrity sha512-cDAd3aP7jH5bO2WO4eSkyQQg0k6Zw8gobl+zL8LGApqix28EtlxultyKH/mVKnbo8Kiq6IDRzwP1HfKOf1QZbQ== 59 | 60 | "@heroicons/vue@^1.0.5": 61 | version "1.0.5" 62 | resolved "https://registry.yarnpkg.com/@heroicons/vue/-/vue-1.0.5.tgz#ccef02a9a75e47129a0b8faf5d1d93bd90723264" 63 | integrity sha512-idWtp20Fjr7mqnD7EdGDUDi83oWHnx3SwyuQY6GZyF33OApzpBOLxz7xa4t6rPOddGz9tI5RGnndLk+ake7ijQ== 64 | 65 | "@nodelib/fs.scandir@2.1.5": 66 | version "2.1.5" 67 | resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" 68 | integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== 69 | dependencies: 70 | "@nodelib/fs.stat" "2.0.5" 71 | run-parallel "^1.1.9" 72 | 73 | "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": 74 | version "2.0.5" 75 | resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" 76 | integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== 77 | 78 | "@nodelib/fs.walk@^1.2.3": 79 | version "1.2.8" 80 | resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" 81 | integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== 82 | dependencies: 83 | "@nodelib/fs.scandir" "2.1.5" 84 | fastq "^1.6.0" 85 | 86 | "@popperjs/core@^2.11.2", "@popperjs/core@^2.9.0": 87 | version "2.11.5" 88 | resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.5.tgz#db5a11bf66bdab39569719555b0f76e138d7bd64" 89 | integrity sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw== 90 | 91 | "@tailwindcss/forms@^0.4.0": 92 | version "0.4.1" 93 | resolved "https://registry.yarnpkg.com/@tailwindcss/forms/-/forms-0.4.1.tgz#5a47ccd60490cbba84e662f2b9cf3d71a5126d17" 94 | integrity sha512-gS9xjCmJjUBz/eP12QlENPLnf0tCx68oYE3mri0GMP5jdtVwLbGUNSRpjsp6NzLAZzZy3ueOwrcqB78Ax6Z84A== 95 | dependencies: 96 | mini-svg-data-uri "^1.2.3" 97 | 98 | "@tailwindcss/typography@^0.5.0": 99 | version "0.5.2" 100 | resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.5.2.tgz#24b069dab24d7a2467d01aca0dd432cb4b29f0ee" 101 | integrity sha512-coq8DBABRPFcVhVIk6IbKyyHUt7YTEC/C992tatFB+yEx5WGBQrCgsSFjxHUr8AWXphWckadVJbominEduYBqw== 102 | dependencies: 103 | lodash.castarray "^4.4.0" 104 | lodash.isplainobject "^4.0.6" 105 | lodash.merge "^4.6.2" 106 | 107 | "@tiptap/core@^2.0.0-beta.174": 108 | version "2.0.0-beta.174" 109 | resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.0.0-beta.174.tgz#cfdf16b7d7401e4b255dc69147d784f5f537b942" 110 | integrity sha512-APQDto40PdvagG1HTwkKlieQS4Vp6GXNe7qgV1Qo2QCgJCLyxc/fXCTghtrOx0CQb+9JT7fjSLZxbSyUFXjx7Q== 111 | dependencies: 112 | "@types/prosemirror-commands" "^1.0.4" 113 | "@types/prosemirror-keymap" "^1.0.4" 114 | "@types/prosemirror-model" "^1.16.0" 115 | "@types/prosemirror-schema-list" "^1.0.3" 116 | "@types/prosemirror-state" "^1.2.8" 117 | "@types/prosemirror-transform" "^1.1.5" 118 | "@types/prosemirror-view" "^1.23.1" 119 | prosemirror-commands "^1.2.1" 120 | prosemirror-keymap "^1.1.5" 121 | prosemirror-model "^1.16.1" 122 | prosemirror-schema-list "^1.1.6" 123 | prosemirror-state "^1.3.4" 124 | prosemirror-transform "^1.3.3" 125 | prosemirror-view "^1.23.6" 126 | 127 | "@tiptap/extension-blockquote@^2.0.0-beta.26": 128 | version "2.0.0-beta.26" 129 | resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.0.0-beta.26.tgz#e5ae4b7bd9376db37407a23e22080c7b11287f3b" 130 | integrity sha512-A6yjcYovONJfOjQFk6vDYXswaCdCtCwjL7w9VTB0R2DLTuJvvRt9DWN0IDcMrj5G+aMgDq4GUUTitv+2Y8krDg== 131 | 132 | "@tiptap/extension-bold@^2.0.0-beta.26": 133 | version "2.0.0-beta.26" 134 | resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.0.0-beta.26.tgz#aa1c7850df28cec8e0614fde437183bd4ae3e66b" 135 | integrity sha512-pnO0I5sEQM3pmowjMGQ74adLzvc6HqGyLyqMizaGMicPu9uTYlSdId+qckYEEgPwPMaEShtv2Vg+ZHs7KVqfcg== 136 | 137 | "@tiptap/extension-bubble-menu@^2.0.0-beta.55": 138 | version "2.0.0-beta.55" 139 | resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.55.tgz#a26ad892cea6af9eeada22235701b06d0921af48" 140 | integrity sha512-v32/QnwwRbepdbrho8mTYru1/XNW/rJi3Mjrgo3rrIs67R86aEPmhmdzD3QEQUJhAJkduuwdw8zElmVWqIJQ9w== 141 | dependencies: 142 | prosemirror-state "^1.3.4" 143 | prosemirror-view "^1.23.6" 144 | tippy.js "^6.3.7" 145 | 146 | "@tiptap/extension-bullet-list@^2.0.0-beta.26": 147 | version "2.0.0-beta.26" 148 | resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.0.0-beta.26.tgz#b42126d2d984c04041b14037e8d3ec1bcf16e7ec" 149 | integrity sha512-1n5HV8gY1tLjPk4x48nva6SZlFHoPlRfF6pqSu9JcJxPO7FUSPxUokuz4swYNe0LRrtykfyNz44dUcxKVhoFow== 150 | 151 | "@tiptap/extension-code-block@^2.0.0-beta.37": 152 | version "2.0.0-beta.37" 153 | resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.0.0-beta.37.tgz#c07c007248a21d9e0434458fd05c363b7078227f" 154 | integrity sha512-mJAM+PHaNoKRYwM3D36lZ51/aoPxxvZNQn3UBnZ6G7l0ZJSgB3JvBEzqK6S8nNFeYIIxGwv4QF6vXe4MG9ie2g== 155 | dependencies: 156 | prosemirror-state "^1.3.4" 157 | 158 | "@tiptap/extension-code@^2.0.0-beta.26": 159 | version "2.0.0-beta.26" 160 | resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.0.0-beta.26.tgz#bbfa600a252ee2cded6947b56b6c4c33d998e53a" 161 | integrity sha512-QcFWdEFfbJ1n5UFFBD17QPPAJ3J5p/b7XV484u0shCzywO7aNPV32QeHy1z0eMoyZtCbOWf6hg/a7Ugv8IwpHw== 162 | 163 | "@tiptap/extension-document@^2.0.0-beta.15": 164 | version "2.0.0-beta.15" 165 | resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.0.0-beta.15.tgz#5d17a0289244a913ab2ef08e8495a1e46950711e" 166 | integrity sha512-ypENC+xUYD5m2t+KOKNYqyXnanXd5fxyIyhR1qeEEwwQwMXGNrO3kCH6O4mIDCpy+/WqHvVay2tV5dVsXnvY8w== 167 | 168 | "@tiptap/extension-dropcursor@^2.0.0-beta.25": 169 | version "2.0.0-beta.25" 170 | resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.0.0-beta.25.tgz#962f290a200259533a26194daca5a4b4a53e72d3" 171 | integrity sha512-GYf5s6dkZtsDy+TEkrQK6kLbfbitG4qnk02D+FlhlJMI/Nnx8rYCRJbwEHDdqrfX7XwZzULMqqqHvzxZYrEeNg== 172 | dependencies: 173 | "@types/prosemirror-dropcursor" "^1.0.3" 174 | prosemirror-dropcursor "^1.4.0" 175 | 176 | "@tiptap/extension-floating-menu@^2.0.0-beta.50": 177 | version "2.0.0-beta.50" 178 | resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.50.tgz#e8785d5f051a848ae053ce139581dce96b951a35" 179 | integrity sha512-aQu1HtthMIYEPylr6kzioLxMiObLbcgwx9xZzF03KwNnkjQLbjZOeJX2RwSYVpiVgtfPBGOm3N/br6NSYec4yQ== 180 | dependencies: 181 | prosemirror-state "^1.3.4" 182 | prosemirror-view "^1.23.6" 183 | tippy.js "^6.3.7" 184 | 185 | "@tiptap/extension-gapcursor@^2.0.0-beta.34": 186 | version "2.0.0-beta.34" 187 | resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.0.0-beta.34.tgz#0e4971affb1621934422dd5fc4bf2dd7a84f70f7" 188 | integrity sha512-Vm8vMWWQ2kJcUOLfB5CEo5pYgyudI7JeeiZvX9ScPmUmgKVYhEpt3EAICY9pUYJ41aAVH35gZLXkUtsz2f9GHw== 189 | dependencies: 190 | "@types/prosemirror-gapcursor" "^1.0.4" 191 | prosemirror-gapcursor "^1.2.1" 192 | 193 | "@tiptap/extension-hard-break@^2.0.0-beta.30": 194 | version "2.0.0-beta.30" 195 | resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.0.0-beta.30.tgz#165494f1194a7bad08907e6d64d349dd15851b72" 196 | integrity sha512-X9xj/S+CikrbIE7ccUFVwit5QHEbflnKVxod+4zPwr1cxogFbE9AyLZE2MpYdx3z9LcnTYYi9leBqFrP4T/Olw== 197 | 198 | "@tiptap/extension-heading@^2.0.0-beta.26": 199 | version "2.0.0-beta.26" 200 | resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.0.0-beta.26.tgz#112b14b4d488772bda36abbf7cb2bc8aba7c42f5" 201 | integrity sha512-nR6W/3rjnZH1Swo7tGBoYsmO6xMvu9MGq6jlm3WVHCB7B3CsrRvCkTwGjVIbKTaZC4bQfx5gvAUpQFvwuU+M5w== 202 | 203 | "@tiptap/extension-history@^2.0.0-beta.21": 204 | version "2.0.0-beta.21" 205 | resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.0.0-beta.21.tgz#5d96a17a83a7130744f0757a3275dd5b11eb1bf7" 206 | integrity sha512-0v8Cl30V4dsabdpspLdk+f+lMoIvLFlJN5WRxtc7RRZ5gfJVxPHwooIKdvC51brfh/oJtWFCNMRjhoz0fRaF9A== 207 | dependencies: 208 | "@types/prosemirror-history" "^1.0.3" 209 | prosemirror-history "^1.2.0" 210 | 211 | "@tiptap/extension-horizontal-rule@^2.0.0-beta.31": 212 | version "2.0.0-beta.31" 213 | resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.0.0-beta.31.tgz#efb383a6cedbbf4f2175d7d207eaeeba626faab0" 214 | integrity sha512-MNc4retfjRgkv3qxqGya0+/BEd1Kmn+oMsCRvE+8x3sXyKIse+vdqMuG5qUcA6np0ZD/9hh1riiQ1GQdgc23Ng== 215 | dependencies: 216 | prosemirror-state "^1.3.4" 217 | 218 | "@tiptap/extension-image@^2.0.0-beta.27": 219 | version "2.0.0-beta.27" 220 | resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.0.0-beta.27.tgz#62152240cfa7ead03080c38485c1ebda4a603d18" 221 | integrity sha512-kdJ7V39yNdVWUco/RBe7WgvFevd81l+pU6+Je9HpelqBBP953wDttzLMuAWQB4AeLv9WhKSlORHiFv2SKsV5NA== 222 | 223 | "@tiptap/extension-italic@^2.0.0-beta.26": 224 | version "2.0.0-beta.26" 225 | resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.0.0-beta.26.tgz#b00c9e32b81b1bd94eaed24bb2a22e44d5dc54a3" 226 | integrity sha512-vejGe2ra4K5ipFOn1U9viqF9X9nPTX8WSJpSOux+9UbKjHpANy7bz69tp66OIi/Wh5L/MMDc+luH/04qfVnpZw== 227 | 228 | "@tiptap/extension-list-item@^2.0.0-beta.20": 229 | version "2.0.0-beta.20" 230 | resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.0.0-beta.20.tgz#7169528b226dee4590e013bdf6e5fc6d83729b0f" 231 | integrity sha512-5IPEspJt38t9ROj4xLUesOVEYlTT/R9Skd9meHRxJQZX1qrzBICs5PC/WRIsnexrvTBhdxpYgCYjpvpsJBlKuQ== 232 | 233 | "@tiptap/extension-ordered-list@^2.0.0-beta.27": 234 | version "2.0.0-beta.27" 235 | resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.0.0-beta.27.tgz#ed48a53a9b012d578613b68375db31e8664bfdc9" 236 | integrity sha512-apFDeignxdZb3cA3p1HJu0zw1JgJdBYUBz1r7f99qdNybYuk3I/1MPUvlOuOgvIrBB/wydoyVDP+v9F7QN3tfQ== 237 | 238 | "@tiptap/extension-paragraph@^2.0.0-beta.23": 239 | version "2.0.0-beta.23" 240 | resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.0.0-beta.23.tgz#2ab77308519494994d7a9e5a4acd14042f45f28c" 241 | integrity sha512-VWAxyzecErYWk97Kv/Gkghh97zAQTcaVOisEnYYArZAlyYDaYM48qVssAC/vnRRynP2eQxb1EkppbAxE+bMHAA== 242 | 243 | "@tiptap/extension-placeholder@^2.0.0-beta.48": 244 | version "2.0.0-beta.48" 245 | resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.0.0-beta.48.tgz#aff02fbdcd27772ff503b5f84a2f1d83da846006" 246 | integrity sha512-TZNGAHocPoV5DtB8Q5BwQU2uf5vDiwLxbgVHRAIme9P4VsVqa/U1i1TkyN5A5BVdfOzc+E4EOU7cKuyjy7DNyA== 247 | dependencies: 248 | prosemirror-model "^1.16.1" 249 | prosemirror-state "^1.3.4" 250 | prosemirror-view "^1.23.6" 251 | 252 | "@tiptap/extension-strike@^2.0.0-beta.27": 253 | version "2.0.0-beta.27" 254 | resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.0.0-beta.27.tgz#c5187bf3c28837f95a5c0c0617d0dd31c318353d" 255 | integrity sha512-2dmCgtesuDdivM/54Q+Y6Tc3JbGz1SkHP6c62piuqBiYLWg3xa16zChZOhfN8szbbQlBgLT6XRTDt3c2Ux+Dug== 256 | 257 | "@tiptap/extension-text@^2.0.0-beta.15": 258 | version "2.0.0-beta.15" 259 | resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.0.0-beta.15.tgz#f08cff1b78f1c6996464dfba1fef8ec1e107617f" 260 | integrity sha512-S3j2+HyV2gsXZP8Wg/HA+YVXQsZ3nrXgBM9HmGAxB0ESOO50l7LWfip0f3qcw1oRlh5H3iLPkA6/f7clD2/TFA== 261 | 262 | "@tiptap/starter-kit@^2.0.0-beta.183": 263 | version "2.0.0-beta.183" 264 | resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-2.0.0-beta.183.tgz#35ff9f4b236bd321ffdd44c5061514959c1b4b9d" 265 | integrity sha512-Lcms6lEfFfdL1oHoATcNKfu1C8+yhuZnI5Pq+U6o2zSslfnUSDf3jgmy6nSoZrrkqvFoXjQk4dxMDFg3giw2Kg== 266 | dependencies: 267 | "@tiptap/core" "^2.0.0-beta.174" 268 | "@tiptap/extension-blockquote" "^2.0.0-beta.26" 269 | "@tiptap/extension-bold" "^2.0.0-beta.26" 270 | "@tiptap/extension-bullet-list" "^2.0.0-beta.26" 271 | "@tiptap/extension-code" "^2.0.0-beta.26" 272 | "@tiptap/extension-code-block" "^2.0.0-beta.37" 273 | "@tiptap/extension-document" "^2.0.0-beta.15" 274 | "@tiptap/extension-dropcursor" "^2.0.0-beta.25" 275 | "@tiptap/extension-gapcursor" "^2.0.0-beta.34" 276 | "@tiptap/extension-hard-break" "^2.0.0-beta.30" 277 | "@tiptap/extension-heading" "^2.0.0-beta.26" 278 | "@tiptap/extension-history" "^2.0.0-beta.21" 279 | "@tiptap/extension-horizontal-rule" "^2.0.0-beta.31" 280 | "@tiptap/extension-italic" "^2.0.0-beta.26" 281 | "@tiptap/extension-list-item" "^2.0.0-beta.20" 282 | "@tiptap/extension-ordered-list" "^2.0.0-beta.27" 283 | "@tiptap/extension-paragraph" "^2.0.0-beta.23" 284 | "@tiptap/extension-strike" "^2.0.0-beta.27" 285 | "@tiptap/extension-text" "^2.0.0-beta.15" 286 | 287 | "@tiptap/vue-3@^2.0.0-beta.90": 288 | version "2.0.0-beta.90" 289 | resolved "https://registry.yarnpkg.com/@tiptap/vue-3/-/vue-3-2.0.0-beta.90.tgz#139bfc26ce95a47fae88f9d076876e6c3ecbd878" 290 | integrity sha512-5QwYpwqomqD1DmEL9DVS0wXuVouzp+2CThXl9QzPxXXsE+pm03yjjk+VhNMiFkA+DaY0hhnrvtWU7n0o00ExRQ== 291 | dependencies: 292 | "@tiptap/extension-bubble-menu" "^2.0.0-beta.55" 293 | "@tiptap/extension-floating-menu" "^2.0.0-beta.50" 294 | prosemirror-state "^1.3.4" 295 | prosemirror-view "^1.23.6" 296 | 297 | "@types/orderedmap@*": 298 | version "1.0.0" 299 | resolved "https://registry.yarnpkg.com/@types/orderedmap/-/orderedmap-1.0.0.tgz#807455a192bba52cbbb4517044bc82bdbfa8c596" 300 | integrity sha512-dxKo80TqYx3YtBipHwA/SdFmMMyLCnP+5mkEqN0eMjcTBzHkiiX0ES118DsjDBjvD+zeSsSU9jULTZ+frog+Gw== 301 | 302 | "@types/parse-json@^4.0.0": 303 | version "4.0.0" 304 | resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" 305 | integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== 306 | 307 | "@types/prosemirror-commands@*", "@types/prosemirror-commands@^1.0.4": 308 | version "1.0.4" 309 | resolved "https://registry.yarnpkg.com/@types/prosemirror-commands/-/prosemirror-commands-1.0.4.tgz#d08551415127d93ae62e7239d30db0b5e7208e22" 310 | integrity sha512-utDNYB3EXLjAfYIcRWJe6pn3kcQ5kG4RijbT/0Y/TFOm6yhvYS/D9eJVnijdg9LDjykapcezchxGRqFD5LcyaQ== 311 | dependencies: 312 | "@types/prosemirror-model" "*" 313 | "@types/prosemirror-state" "*" 314 | "@types/prosemirror-view" "*" 315 | 316 | "@types/prosemirror-dropcursor@^1.0.3": 317 | version "1.0.3" 318 | resolved "https://registry.yarnpkg.com/@types/prosemirror-dropcursor/-/prosemirror-dropcursor-1.0.3.tgz#49250849b8a0b86e8c29eb1ba70a463e53e46947" 319 | integrity sha512-b0/8njnJ4lwyHKcGuCMf3x7r1KjxyugB1R/c2iMCjplsJHSC7UY9+OysqgJR5uUXRekUSGniiLgBtac/lvH6wg== 320 | dependencies: 321 | "@types/prosemirror-state" "*" 322 | 323 | "@types/prosemirror-gapcursor@^1.0.4": 324 | version "1.0.4" 325 | resolved "https://registry.yarnpkg.com/@types/prosemirror-gapcursor/-/prosemirror-gapcursor-1.0.4.tgz#7df7d373edb33ea8da12084bfd462cf84cd69761" 326 | integrity sha512-9xKjFIG5947dzerFvkLWp6F53JwrUYoYwh3SgcTFEp8SbSfNNrez/PFYVZKPnoqPoaK5WtTdQTaMwpCV9rXQIg== 327 | dependencies: 328 | "@types/prosemirror-model" "*" 329 | "@types/prosemirror-state" "*" 330 | 331 | "@types/prosemirror-history@^1.0.3": 332 | version "1.0.3" 333 | resolved "https://registry.yarnpkg.com/@types/prosemirror-history/-/prosemirror-history-1.0.3.tgz#f1110efbe758129b5475e466ff077f0a8d9b964f" 334 | integrity sha512-5TloMDRavgLjOAKXp1Li8u0xcsspzbT1Cm9F2pwHOkgvQOz1jWQb2VIXO7RVNsFjLBZdIXlyfSLivro3DuMWXg== 335 | dependencies: 336 | "@types/prosemirror-model" "*" 337 | "@types/prosemirror-state" "*" 338 | 339 | "@types/prosemirror-keymap@^1.0.4": 340 | version "1.0.4" 341 | resolved "https://registry.yarnpkg.com/@types/prosemirror-keymap/-/prosemirror-keymap-1.0.4.tgz#f73c79810e8d0e0a20d153d84f998f02e5afbc0c" 342 | integrity sha512-ycevwkqUh+jEQtPwqO7sWGcm+Sybmhu8MpBsM8DlO3+YTKnXbKA6SDz/+q14q1wK3UA8lHJyfR+v+GPxfUSemg== 343 | dependencies: 344 | "@types/prosemirror-commands" "*" 345 | "@types/prosemirror-model" "*" 346 | "@types/prosemirror-state" "*" 347 | "@types/prosemirror-view" "*" 348 | 349 | "@types/prosemirror-model@*", "@types/prosemirror-model@^1.16.0": 350 | version "1.16.1" 351 | resolved "https://registry.yarnpkg.com/@types/prosemirror-model/-/prosemirror-model-1.16.1.tgz#0ce6c80cd81b398b8a11b1bf7cf695bff3160c9a" 352 | integrity sha512-SrrCe2cHlYrQ9o55e2i/c3wt1yRajTTpRLvzfmB+2DWjWEbBLTByVWyjrdpKtQTxAaTeU2aeDGo1iuwl/jF27w== 353 | dependencies: 354 | "@types/orderedmap" "*" 355 | 356 | "@types/prosemirror-schema-list@^1.0.3": 357 | version "1.0.3" 358 | resolved "https://registry.yarnpkg.com/@types/prosemirror-schema-list/-/prosemirror-schema-list-1.0.3.tgz#bdf1893a7915fbdc5c49b3cac9368e96213d70de" 359 | integrity sha512-uWybOf+M2Ea7rlbs0yLsS4YJYNGXYtn4N+w8HCw3Vvfl6wBAROzlMt0gV/D/VW/7J/LlAjwMezuGe8xi24HzXA== 360 | dependencies: 361 | "@types/orderedmap" "*" 362 | "@types/prosemirror-model" "*" 363 | "@types/prosemirror-state" "*" 364 | 365 | "@types/prosemirror-state@*", "@types/prosemirror-state@^1.2.8": 366 | version "1.2.8" 367 | resolved "https://registry.yarnpkg.com/@types/prosemirror-state/-/prosemirror-state-1.2.8.tgz#65080eeec52f63c50bf7034377f07773b4f6b2ac" 368 | integrity sha512-mq9uyQWcpu8jeamO6Callrdvf/e1H/aRLR2kZWSpZrPHctEsxWHBbluD/wqVjXBRIOoMHLf6ZvOkrkmGLoCHVA== 369 | dependencies: 370 | "@types/prosemirror-model" "*" 371 | "@types/prosemirror-transform" "*" 372 | "@types/prosemirror-view" "*" 373 | 374 | "@types/prosemirror-transform@*", "@types/prosemirror-transform@^1.1.5": 375 | version "1.1.6" 376 | resolved "https://registry.yarnpkg.com/@types/prosemirror-transform/-/prosemirror-transform-1.1.6.tgz#4a06979f656331c46c2725039a57360cc35853af" 377 | integrity sha512-7HwXOWc5vZQqIfEUUVAz13lPgBqAWJTv89qEpzAtBFB6hOszFmhsvQ02Jqe2LvKauAoJDa3Qpv/dbJAmgyiTuQ== 378 | dependencies: 379 | "@types/prosemirror-model" "*" 380 | 381 | "@types/prosemirror-view@*", "@types/prosemirror-view@^1.23.1": 382 | version "1.23.1" 383 | resolved "https://registry.yarnpkg.com/@types/prosemirror-view/-/prosemirror-view-1.23.1.tgz#a9a926bb6b6e6873e3a9d8caa61c32f3402629eb" 384 | integrity sha512-6e1B2oKUnhmZPUrsVvYjDqeVjE6jGezygjtoHsAK4ZENAxHzHqy5NT4jUvdPTWjCYeH0t2Y7pSfRPNrPIyQX4A== 385 | dependencies: 386 | "@types/prosemirror-model" "*" 387 | "@types/prosemirror-state" "*" 388 | "@types/prosemirror-transform" "*" 389 | 390 | "@vitejs/plugin-vue@^2.0.0": 391 | version "2.0.1" 392 | resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-2.0.1.tgz" 393 | integrity sha512-wtdMnGVvys9K8tg+DxowU1ytTrdVveXr3LzdhaKakysgGXyrsfaeds2cDywtvujEASjWOwWL/OgWM+qoeM8Plg== 394 | 395 | "@vue/compiler-core@3.2.26": 396 | version "3.2.26" 397 | resolved "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.26.tgz" 398 | integrity sha512-N5XNBobZbaASdzY9Lga2D9Lul5vdCIOXvUMd6ThcN8zgqQhPKfCV+wfAJNNJKQkSHudnYRO2gEB+lp0iN3g2Tw== 399 | dependencies: 400 | "@babel/parser" "^7.16.4" 401 | "@vue/shared" "3.2.26" 402 | estree-walker "^2.0.2" 403 | source-map "^0.6.1" 404 | 405 | "@vue/compiler-dom@3.2.26": 406 | version "3.2.26" 407 | resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.26.tgz" 408 | integrity sha512-smBfaOW6mQDxcT3p9TKT6mE22vjxjJL50GFVJiI0chXYGU/xzC05QRGrW3HHVuJrmLTLx5zBhsZ2dIATERbarg== 409 | dependencies: 410 | "@vue/compiler-core" "3.2.26" 411 | "@vue/shared" "3.2.26" 412 | 413 | "@vue/compiler-sfc@3.2.26": 414 | version "3.2.26" 415 | resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.26.tgz" 416 | integrity sha512-ePpnfktV90UcLdsDQUh2JdiTuhV0Skv2iYXxfNMOK/F3Q+2BO0AulcVcfoksOpTJGmhhfosWfMyEaEf0UaWpIw== 417 | dependencies: 418 | "@babel/parser" "^7.16.4" 419 | "@vue/compiler-core" "3.2.26" 420 | "@vue/compiler-dom" "3.2.26" 421 | "@vue/compiler-ssr" "3.2.26" 422 | "@vue/reactivity-transform" "3.2.26" 423 | "@vue/shared" "3.2.26" 424 | estree-walker "^2.0.2" 425 | magic-string "^0.25.7" 426 | postcss "^8.1.10" 427 | source-map "^0.6.1" 428 | 429 | "@vue/compiler-ssr@3.2.26": 430 | version "3.2.26" 431 | resolved "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.26.tgz" 432 | integrity sha512-2mywLX0ODc4Zn8qBoA2PDCsLEZfpUGZcyoFRLSOjyGGK6wDy2/5kyDOWtf0S0UvtoyVq95OTSGIALjZ4k2q/ag== 433 | dependencies: 434 | "@vue/compiler-dom" "3.2.26" 435 | "@vue/shared" "3.2.26" 436 | 437 | "@vue/devtools-api@^6.0.0-beta.18": 438 | version "6.0.0-beta.21.1" 439 | resolved "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.0.0-beta.21.1.tgz" 440 | integrity sha512-FqC4s3pm35qGVeXRGOjTsRzlkJjrBLriDS9YXbflHLsfA9FrcKzIyWnLXoNm+/7930E8rRakXuAc2QkC50swAw== 441 | 442 | "@vue/reactivity-transform@3.2.26": 443 | version "3.2.26" 444 | resolved "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.26.tgz" 445 | integrity sha512-XKMyuCmzNA7nvFlYhdKwD78rcnmPb7q46uoR00zkX6yZrUmcCQ5OikiwUEVbvNhL5hBJuvbSO95jB5zkUon+eQ== 446 | dependencies: 447 | "@babel/parser" "^7.16.4" 448 | "@vue/compiler-core" "3.2.26" 449 | "@vue/shared" "3.2.26" 450 | estree-walker "^2.0.2" 451 | magic-string "^0.25.7" 452 | 453 | "@vue/reactivity@3.2.26": 454 | version "3.2.26" 455 | resolved "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.26.tgz" 456 | integrity sha512-h38bxCZLW6oFJVDlCcAiUKFnXI8xP8d+eO0pcDxx+7dQfSPje2AO6M9S9QO6MrxQB7fGP0DH0dYQ8ksf6hrXKQ== 457 | dependencies: 458 | "@vue/shared" "3.2.26" 459 | 460 | "@vue/runtime-core@3.2.26": 461 | version "3.2.26" 462 | resolved "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.26.tgz" 463 | integrity sha512-BcYi7qZ9Nn+CJDJrHQ6Zsmxei2hDW0L6AB4vPvUQGBm2fZyC0GXd/4nVbyA2ubmuhctD5RbYY8L+5GUJszv9mQ== 464 | dependencies: 465 | "@vue/reactivity" "3.2.26" 466 | "@vue/shared" "3.2.26" 467 | 468 | "@vue/runtime-dom@3.2.26": 469 | version "3.2.26" 470 | resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.26.tgz" 471 | integrity sha512-dY56UIiZI+gjc4e8JQBwAifljyexfVCkIAu/WX8snh8vSOt/gMSEGwPRcl2UpYpBYeyExV8WCbgvwWRNt9cHhQ== 472 | dependencies: 473 | "@vue/runtime-core" "3.2.26" 474 | "@vue/shared" "3.2.26" 475 | csstype "^2.6.8" 476 | 477 | "@vue/server-renderer@3.2.26": 478 | version "3.2.26" 479 | resolved "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.26.tgz" 480 | integrity sha512-Jp5SggDUvvUYSBIvYEhy76t4nr1vapY/FIFloWmQzn7UxqaHrrBpbxrqPcTrSgGrcaglj0VBp22BKJNre4aA1w== 481 | dependencies: 482 | "@vue/compiler-ssr" "3.2.26" 483 | "@vue/shared" "3.2.26" 484 | 485 | "@vue/shared@3.2.26": 486 | version "3.2.26" 487 | resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.2.26.tgz" 488 | integrity sha512-vPV6Cq+NIWbH5pZu+V+2QHE9y1qfuTq49uNWw4f7FDEeZaDU2H2cx5jcUZOAKW7qTrUS4k6qZPbMy1x4N96nbA== 489 | 490 | acorn-node@^1.6.1: 491 | version "1.8.2" 492 | resolved "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz" 493 | integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== 494 | dependencies: 495 | acorn "^7.0.0" 496 | acorn-walk "^7.0.0" 497 | xtend "^4.0.2" 498 | 499 | acorn-walk@^7.0.0: 500 | version "7.2.0" 501 | resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" 502 | integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== 503 | 504 | acorn@^7.0.0: 505 | version "7.4.1" 506 | resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" 507 | integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== 508 | 509 | after@0.8.2: 510 | version "0.8.2" 511 | resolved "https://registry.npmjs.org/after/-/after-0.8.2.tgz" 512 | integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= 513 | 514 | ansi-styles@^3.2.1: 515 | version "3.2.1" 516 | resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" 517 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 518 | dependencies: 519 | color-convert "^1.9.0" 520 | 521 | ansi-styles@^4.1.0: 522 | version "4.3.0" 523 | resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" 524 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 525 | dependencies: 526 | color-convert "^2.0.1" 527 | 528 | anymatch@~3.1.2: 529 | version "3.1.2" 530 | resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" 531 | integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== 532 | dependencies: 533 | normalize-path "^3.0.0" 534 | picomatch "^2.0.4" 535 | 536 | arg@^5.0.1: 537 | version "5.0.1" 538 | resolved "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz" 539 | integrity sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA== 540 | 541 | arraybuffer.slice@~0.0.7: 542 | version "0.0.7" 543 | resolved "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz" 544 | integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== 545 | 546 | autoprefixer@^10.4.2: 547 | version "10.4.2" 548 | resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz" 549 | integrity sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ== 550 | dependencies: 551 | browserslist "^4.19.1" 552 | caniuse-lite "^1.0.30001297" 553 | fraction.js "^4.1.2" 554 | normalize-range "^0.1.2" 555 | picocolors "^1.0.0" 556 | postcss-value-parser "^4.2.0" 557 | 558 | backo2@1.0.2: 559 | version "1.0.2" 560 | resolved "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz" 561 | integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= 562 | 563 | base64-arraybuffer@0.1.4: 564 | version "0.1.4" 565 | resolved "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz" 566 | integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI= 567 | 568 | binary-extensions@^2.0.0: 569 | version "2.2.0" 570 | resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" 571 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== 572 | 573 | blob@0.0.5: 574 | version "0.0.5" 575 | resolved "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz" 576 | integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== 577 | 578 | braces@^3.0.1, braces@~3.0.2: 579 | version "3.0.2" 580 | resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" 581 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 582 | dependencies: 583 | fill-range "^7.0.1" 584 | 585 | browserslist@^4.19.1: 586 | version "4.19.1" 587 | resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz" 588 | integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A== 589 | dependencies: 590 | caniuse-lite "^1.0.30001286" 591 | electron-to-chromium "^1.4.17" 592 | escalade "^3.1.1" 593 | node-releases "^2.0.1" 594 | picocolors "^1.0.0" 595 | 596 | callsites@^3.0.0: 597 | version "3.1.0" 598 | resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" 599 | integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== 600 | 601 | camelcase-css@^2.0.1: 602 | version "2.0.1" 603 | resolved "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz" 604 | integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== 605 | 606 | caniuse-lite@^1.0.30001286, caniuse-lite@^1.0.30001297: 607 | version "1.0.30001299" 608 | resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001299.tgz" 609 | integrity sha512-iujN4+x7QzqA2NCSrS5VUy+4gLmRd4xv6vbBBsmfVqTx8bLAD8097euLqQgKxSVLvxjSDcvF1T/i9ocgnUFexw== 610 | 611 | chalk@^2.0.0: 612 | version "2.4.2" 613 | resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" 614 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 615 | dependencies: 616 | ansi-styles "^3.2.1" 617 | escape-string-regexp "^1.0.5" 618 | supports-color "^5.3.0" 619 | 620 | chalk@^4.1.2: 621 | version "4.1.2" 622 | resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" 623 | integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== 624 | dependencies: 625 | ansi-styles "^4.1.0" 626 | supports-color "^7.1.0" 627 | 628 | chokidar@^3.5.2: 629 | version "3.5.2" 630 | resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz" 631 | integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== 632 | dependencies: 633 | anymatch "~3.1.2" 634 | braces "~3.0.2" 635 | glob-parent "~5.1.2" 636 | is-binary-path "~2.1.0" 637 | is-glob "~4.0.1" 638 | normalize-path "~3.0.0" 639 | readdirp "~3.6.0" 640 | optionalDependencies: 641 | fsevents "~2.3.2" 642 | 643 | chokidar@^3.5.3: 644 | version "3.5.3" 645 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" 646 | integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== 647 | dependencies: 648 | anymatch "~3.1.2" 649 | braces "~3.0.2" 650 | glob-parent "~5.1.2" 651 | is-binary-path "~2.1.0" 652 | is-glob "~4.0.1" 653 | normalize-path "~3.0.0" 654 | readdirp "~3.6.0" 655 | optionalDependencies: 656 | fsevents "~2.3.2" 657 | 658 | classnames@^2.2.5: 659 | version "2.3.1" 660 | resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" 661 | integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== 662 | 663 | color-convert@^1.9.0: 664 | version "1.9.3" 665 | resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" 666 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 667 | dependencies: 668 | color-name "1.1.3" 669 | 670 | color-convert@^2.0.1: 671 | version "2.0.1" 672 | resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" 673 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 674 | dependencies: 675 | color-name "~1.1.4" 676 | 677 | color-name@1.1.3: 678 | version "1.1.3" 679 | resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" 680 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 681 | 682 | color-name@^1.1.4, color-name@~1.1.4: 683 | version "1.1.4" 684 | resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" 685 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 686 | 687 | component-bind@1.0.0: 688 | version "1.0.0" 689 | resolved "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz" 690 | integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= 691 | 692 | component-emitter@~1.3.0: 693 | version "1.3.0" 694 | resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz" 695 | integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== 696 | 697 | component-inherit@0.0.3: 698 | version "0.0.3" 699 | resolved "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz" 700 | integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= 701 | 702 | core-js@^3.1.3: 703 | version "3.21.1" 704 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.1.tgz#f2e0ddc1fc43da6f904706e8e955bc19d06a0d94" 705 | integrity sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig== 706 | 707 | cosmiconfig@^7.0.1: 708 | version "7.0.1" 709 | resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz" 710 | integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== 711 | dependencies: 712 | "@types/parse-json" "^4.0.0" 713 | import-fresh "^3.2.1" 714 | parse-json "^5.0.0" 715 | path-type "^4.0.0" 716 | yaml "^1.10.0" 717 | 718 | cssesc@^3.0.0: 719 | version "3.0.0" 720 | resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" 721 | integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== 722 | 723 | csstype@^2.6.8: 724 | version "2.6.19" 725 | resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.19.tgz" 726 | integrity sha512-ZVxXaNy28/k3kJg0Fou5MiYpp88j7H9hLZp8PDC3jV0WFjfH5E9xHb56L0W59cPbKbcHXeP4qyT8PrHp8t6LcQ== 727 | 728 | debug@~3.1.0: 729 | version "3.1.0" 730 | resolved "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz" 731 | integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== 732 | dependencies: 733 | ms "2.0.0" 734 | 735 | defined@^1.0.0: 736 | version "1.0.0" 737 | resolved "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz" 738 | integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= 739 | 740 | detective@^5.2.0: 741 | version "5.2.0" 742 | resolved "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz" 743 | integrity sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg== 744 | dependencies: 745 | acorn-node "^1.6.1" 746 | defined "^1.0.0" 747 | minimist "^1.1.1" 748 | 749 | didyoumean@^1.2.2: 750 | version "1.2.2" 751 | resolved "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz" 752 | integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== 753 | 754 | dlv@^1.1.3: 755 | version "1.1.3" 756 | resolved "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz" 757 | integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== 758 | 759 | electron-to-chromium@^1.4.17: 760 | version "1.4.46" 761 | resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.46.tgz" 762 | integrity sha512-UtV0xUA/dibCKKP2JMxOpDtXR74zABevuUEH4K0tvduFSIoxRVcYmQsbB51kXsFTX8MmOyWMt8tuZAlmDOqkrQ== 763 | 764 | engine.io-client@~3.5.0: 765 | version "3.5.2" 766 | resolved "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.2.tgz" 767 | integrity sha512-QEqIp+gJ/kMHeUun7f5Vv3bteRHppHH/FMBQX/esFj/fuYfjyUKWGMo3VCvIP/V8bE9KcjHmRZrhIz2Z9oNsDA== 768 | dependencies: 769 | component-emitter "~1.3.0" 770 | component-inherit "0.0.3" 771 | debug "~3.1.0" 772 | engine.io-parser "~2.2.0" 773 | has-cors "1.1.0" 774 | indexof "0.0.1" 775 | parseqs "0.0.6" 776 | parseuri "0.0.6" 777 | ws "~7.4.2" 778 | xmlhttprequest-ssl "~1.6.2" 779 | yeast "0.1.2" 780 | 781 | engine.io-parser@~2.2.0: 782 | version "2.2.1" 783 | resolved "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz" 784 | integrity sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg== 785 | dependencies: 786 | after "0.8.2" 787 | arraybuffer.slice "~0.0.7" 788 | base64-arraybuffer "0.1.4" 789 | blob "0.0.5" 790 | has-binary2 "~1.0.2" 791 | 792 | error-ex@^1.3.1: 793 | version "1.3.2" 794 | resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" 795 | integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== 796 | dependencies: 797 | is-arrayish "^0.2.1" 798 | 799 | esbuild-android-arm64@0.13.15: 800 | version "0.13.15" 801 | resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz#3fc3ff0bab76fe35dd237476b5d2b32bb20a3d44" 802 | integrity sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg== 803 | 804 | esbuild-darwin-64@0.13.15: 805 | version "0.13.15" 806 | resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz#8e9169c16baf444eacec60d09b24d11b255a8e72" 807 | integrity sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ== 808 | 809 | esbuild-darwin-arm64@0.13.15: 810 | version "0.13.15" 811 | resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz" 812 | integrity sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ== 813 | 814 | esbuild-freebsd-64@0.13.15: 815 | version "0.13.15" 816 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz#0b8b7eca1690c8ec94c75680c38c07269c1f4a85" 817 | integrity sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA== 818 | 819 | esbuild-freebsd-arm64@0.13.15: 820 | version "0.13.15" 821 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz#2e1a6c696bfdcd20a99578b76350b41db1934e52" 822 | integrity sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ== 823 | 824 | esbuild-linux-32@0.13.15: 825 | version "0.13.15" 826 | resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz#6fd39f36fc66dd45b6b5f515728c7bbebc342a69" 827 | integrity sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g== 828 | 829 | esbuild-linux-64@0.13.15: 830 | version "0.13.15" 831 | resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz#9cb8e4bcd7574e67946e4ee5f1f1e12386bb6dd3" 832 | integrity sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA== 833 | 834 | esbuild-linux-arm64@0.13.15: 835 | version "0.13.15" 836 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz#3891aa3704ec579a1b92d2a586122e5b6a2bfba1" 837 | integrity sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA== 838 | 839 | esbuild-linux-arm@0.13.15: 840 | version "0.13.15" 841 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz#8a00e99e6a0c6c9a6b7f334841364d8a2b4aecfe" 842 | integrity sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA== 843 | 844 | esbuild-linux-mips64le@0.13.15: 845 | version "0.13.15" 846 | resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz#36b07cc47c3d21e48db3bb1f4d9ef8f46aead4f7" 847 | integrity sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg== 848 | 849 | esbuild-linux-ppc64le@0.13.15: 850 | version "0.13.15" 851 | resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz#f7e6bba40b9a11eb9dcae5b01550ea04670edad2" 852 | integrity sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ== 853 | 854 | esbuild-netbsd-64@0.13.15: 855 | version "0.13.15" 856 | resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz#a2fedc549c2b629d580a732d840712b08d440038" 857 | integrity sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w== 858 | 859 | esbuild-openbsd-64@0.13.15: 860 | version "0.13.15" 861 | resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz#b22c0e5806d3a1fbf0325872037f885306b05cd7" 862 | integrity sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g== 863 | 864 | esbuild-sunos-64@0.13.15: 865 | version "0.13.15" 866 | resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz#d0b6454a88375ee8d3964daeff55c85c91c7cef4" 867 | integrity sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw== 868 | 869 | esbuild-windows-32@0.13.15: 870 | version "0.13.15" 871 | resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz#c96d0b9bbb52f3303322582ef8e4847c5ad375a7" 872 | integrity sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw== 873 | 874 | esbuild-windows-64@0.13.15: 875 | version "0.13.15" 876 | resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz#1f79cb9b1e1bb02fb25cd414cb90d4ea2892c294" 877 | integrity sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ== 878 | 879 | esbuild-windows-arm64@0.13.15: 880 | version "0.13.15" 881 | resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz#482173070810df22a752c686509c370c3be3b3c3" 882 | integrity sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA== 883 | 884 | esbuild@^0.13.12: 885 | version "0.13.15" 886 | resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.13.15.tgz" 887 | integrity sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw== 888 | optionalDependencies: 889 | esbuild-android-arm64 "0.13.15" 890 | esbuild-darwin-64 "0.13.15" 891 | esbuild-darwin-arm64 "0.13.15" 892 | esbuild-freebsd-64 "0.13.15" 893 | esbuild-freebsd-arm64 "0.13.15" 894 | esbuild-linux-32 "0.13.15" 895 | esbuild-linux-64 "0.13.15" 896 | esbuild-linux-arm "0.13.15" 897 | esbuild-linux-arm64 "0.13.15" 898 | esbuild-linux-mips64le "0.13.15" 899 | esbuild-linux-ppc64le "0.13.15" 900 | esbuild-netbsd-64 "0.13.15" 901 | esbuild-openbsd-64 "0.13.15" 902 | esbuild-sunos-64 "0.13.15" 903 | esbuild-windows-32 "0.13.15" 904 | esbuild-windows-64 "0.13.15" 905 | esbuild-windows-arm64 "0.13.15" 906 | 907 | escalade@^3.1.1: 908 | version "3.1.1" 909 | resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" 910 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== 911 | 912 | escape-string-regexp@^1.0.5: 913 | version "1.0.5" 914 | resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" 915 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 916 | 917 | estree-walker@^2.0.2: 918 | version "2.0.2" 919 | resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz" 920 | integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== 921 | 922 | fast-glob@^3.2.11, fast-glob@^3.2.7: 923 | version "3.2.11" 924 | resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz" 925 | integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== 926 | dependencies: 927 | "@nodelib/fs.stat" "^2.0.2" 928 | "@nodelib/fs.walk" "^1.2.3" 929 | glob-parent "^5.1.2" 930 | merge2 "^1.3.0" 931 | micromatch "^4.0.4" 932 | 933 | fastq@^1.6.0: 934 | version "1.13.0" 935 | resolved "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz" 936 | integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== 937 | dependencies: 938 | reusify "^1.0.4" 939 | 940 | feather-icons@^4.28.0: 941 | version "4.29.0" 942 | resolved "https://registry.yarnpkg.com/feather-icons/-/feather-icons-4.29.0.tgz#4e40e3cbb7bf359ffbbf700edbebdde4e4a74ab6" 943 | integrity sha512-Y7VqN9FYb8KdaSF0qD1081HCkm0v4Eq/fpfQYQnubpqi0hXx14k+gF9iqtRys1SIcTEi97xDi/fmsPFZ8xo0GQ== 944 | dependencies: 945 | classnames "^2.2.5" 946 | core-js "^3.1.3" 947 | 948 | fill-range@^7.0.1: 949 | version "7.0.1" 950 | resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" 951 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 952 | dependencies: 953 | to-regex-range "^5.0.1" 954 | 955 | fraction.js@^4.1.2: 956 | version "4.1.2" 957 | resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.2.tgz" 958 | integrity sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA== 959 | 960 | frappe-ui@^0.0.13: 961 | version "0.0.13" 962 | resolved "https://registry.yarnpkg.com/frappe-ui/-/frappe-ui-0.0.13.tgz#0dec30a8f28386d14fd721ea255b4be201c10863" 963 | integrity sha512-aLKiZib55/QMS9xJCmQmqu9hKWcx5wYN3vsv6YBvlIyQQZpPiAIBwPKrYtBMqE2Wk+BDYpw/kkYfW96J1yNORw== 964 | dependencies: 965 | "@headlessui/vue" "^1.4.3" 966 | "@popperjs/core" "^2.11.2" 967 | "@tailwindcss/forms" "^0.4.0" 968 | "@tailwindcss/typography" "^0.5.0" 969 | "@tiptap/extension-image" "^2.0.0-beta.27" 970 | "@tiptap/extension-placeholder" "^2.0.0-beta.48" 971 | "@tiptap/starter-kit" "^2.0.0-beta.183" 972 | "@tiptap/vue-3" "^2.0.0-beta.90" 973 | autoprefixer "^10.4.2" 974 | feather-icons "^4.28.0" 975 | postcss "^8.4.5" 976 | socket.io-client "^2.4.0" 977 | tailwindcss "^3.0.12" 978 | 979 | fsevents@~2.3.2: 980 | version "2.3.2" 981 | resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" 982 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 983 | 984 | function-bind@^1.1.1: 985 | version "1.1.1" 986 | resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" 987 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 988 | 989 | glob-parent@^5.1.2, glob-parent@~5.1.2: 990 | version "5.1.2" 991 | resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" 992 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 993 | dependencies: 994 | is-glob "^4.0.1" 995 | 996 | glob-parent@^6.0.2: 997 | version "6.0.2" 998 | resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" 999 | integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== 1000 | dependencies: 1001 | is-glob "^4.0.3" 1002 | 1003 | has-binary2@~1.0.2: 1004 | version "1.0.3" 1005 | resolved "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz" 1006 | integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== 1007 | dependencies: 1008 | isarray "2.0.1" 1009 | 1010 | has-cors@1.1.0: 1011 | version "1.1.0" 1012 | resolved "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz" 1013 | integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= 1014 | 1015 | has-flag@^3.0.0: 1016 | version "3.0.0" 1017 | resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" 1018 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 1019 | 1020 | has-flag@^4.0.0: 1021 | version "4.0.0" 1022 | resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" 1023 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 1024 | 1025 | has@^1.0.3: 1026 | version "1.0.3" 1027 | resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" 1028 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 1029 | dependencies: 1030 | function-bind "^1.1.1" 1031 | 1032 | import-fresh@^3.2.1: 1033 | version "3.3.0" 1034 | resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" 1035 | integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== 1036 | dependencies: 1037 | parent-module "^1.0.0" 1038 | resolve-from "^4.0.0" 1039 | 1040 | indexof@0.0.1: 1041 | version "0.0.1" 1042 | resolved "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz" 1043 | integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= 1044 | 1045 | is-arrayish@^0.2.1: 1046 | version "0.2.1" 1047 | resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" 1048 | integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= 1049 | 1050 | is-binary-path@~2.1.0: 1051 | version "2.1.0" 1052 | resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" 1053 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 1054 | dependencies: 1055 | binary-extensions "^2.0.0" 1056 | 1057 | is-core-module@^2.8.0, is-core-module@^2.8.1: 1058 | version "2.8.1" 1059 | resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz" 1060 | integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== 1061 | dependencies: 1062 | has "^1.0.3" 1063 | 1064 | is-extglob@^2.1.1: 1065 | version "2.1.1" 1066 | resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" 1067 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= 1068 | 1069 | is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: 1070 | version "4.0.3" 1071 | resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" 1072 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 1073 | dependencies: 1074 | is-extglob "^2.1.1" 1075 | 1076 | is-number@^7.0.0: 1077 | version "7.0.0" 1078 | resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" 1079 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 1080 | 1081 | isarray@2.0.1: 1082 | version "2.0.1" 1083 | resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz" 1084 | integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= 1085 | 1086 | js-tokens@^4.0.0: 1087 | version "4.0.0" 1088 | resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" 1089 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 1090 | 1091 | json-parse-even-better-errors@^2.3.0: 1092 | version "2.3.1" 1093 | resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" 1094 | integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== 1095 | 1096 | lilconfig@^2.0.4: 1097 | version "2.0.4" 1098 | resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz" 1099 | integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== 1100 | 1101 | lilconfig@^2.0.5: 1102 | version "2.0.5" 1103 | resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.5.tgz#19e57fd06ccc3848fd1891655b5a447092225b25" 1104 | integrity sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg== 1105 | 1106 | lines-and-columns@^1.1.6: 1107 | version "1.2.4" 1108 | resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" 1109 | integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== 1110 | 1111 | lodash.castarray@^4.4.0: 1112 | version "4.4.0" 1113 | resolved "https://registry.yarnpkg.com/lodash.castarray/-/lodash.castarray-4.4.0.tgz#c02513515e309daddd4c24c60cfddcf5976d9115" 1114 | integrity sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU= 1115 | 1116 | lodash.isplainobject@^4.0.6: 1117 | version "4.0.6" 1118 | resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" 1119 | integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= 1120 | 1121 | lodash.merge@^4.6.2: 1122 | version "4.6.2" 1123 | resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" 1124 | integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== 1125 | 1126 | magic-string@^0.25.7: 1127 | version "0.25.7" 1128 | resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz" 1129 | integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== 1130 | dependencies: 1131 | sourcemap-codec "^1.4.4" 1132 | 1133 | merge2@^1.3.0: 1134 | version "1.4.1" 1135 | resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" 1136 | integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== 1137 | 1138 | micromatch@^4.0.4: 1139 | version "4.0.4" 1140 | resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz" 1141 | integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== 1142 | dependencies: 1143 | braces "^3.0.1" 1144 | picomatch "^2.2.3" 1145 | 1146 | mini-svg-data-uri@^1.2.3: 1147 | version "1.4.4" 1148 | resolved "https://registry.yarnpkg.com/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz#8ab0aabcdf8c29ad5693ca595af19dd2ead09939" 1149 | integrity sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg== 1150 | 1151 | minimist@^1.1.1: 1152 | version "1.2.5" 1153 | resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz" 1154 | integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== 1155 | 1156 | ms@2.0.0: 1157 | version "2.0.0" 1158 | resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" 1159 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 1160 | 1161 | nanoid@^3.1.30: 1162 | version "3.1.32" 1163 | resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.32.tgz" 1164 | integrity sha512-F8mf7R3iT9bvThBoW4tGXhXFHCctyCiUUPrWF8WaTqa3h96d9QybkSeba43XVOOE3oiLfkVDe4bT8MeGmkrTxw== 1165 | 1166 | nanoid@^3.3.1: 1167 | version "3.3.2" 1168 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.2.tgz#c89622fafb4381cd221421c69ec58547a1eec557" 1169 | integrity sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA== 1170 | 1171 | node-releases@^2.0.1: 1172 | version "2.0.1" 1173 | resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz" 1174 | integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== 1175 | 1176 | normalize-path@^3.0.0, normalize-path@~3.0.0: 1177 | version "3.0.0" 1178 | resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" 1179 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 1180 | 1181 | normalize-range@^0.1.2: 1182 | version "0.1.2" 1183 | resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz" 1184 | integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= 1185 | 1186 | object-hash@^2.2.0: 1187 | version "2.2.0" 1188 | resolved "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz" 1189 | integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== 1190 | 1191 | object-hash@^3.0.0: 1192 | version "3.0.0" 1193 | resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" 1194 | integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== 1195 | 1196 | orderedmap@^1.1.0: 1197 | version "1.1.5" 1198 | resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-1.1.5.tgz#4174c90b61bd7c25294932edf789f3b5677744d0" 1199 | integrity sha512-/fzlCGKRmfayGoI9UUXvJfc2nMZlJHW30QqEvwPvlg8tsX7jyiUSomYie6mYqx7Z9bOMGoag0H/q1PS/0PjYkg== 1200 | 1201 | parent-module@^1.0.0: 1202 | version "1.0.1" 1203 | resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" 1204 | integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== 1205 | dependencies: 1206 | callsites "^3.0.0" 1207 | 1208 | parse-json@^5.0.0: 1209 | version "5.2.0" 1210 | resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" 1211 | integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== 1212 | dependencies: 1213 | "@babel/code-frame" "^7.0.0" 1214 | error-ex "^1.3.1" 1215 | json-parse-even-better-errors "^2.3.0" 1216 | lines-and-columns "^1.1.6" 1217 | 1218 | parseqs@0.0.6: 1219 | version "0.0.6" 1220 | resolved "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz" 1221 | integrity sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w== 1222 | 1223 | parseuri@0.0.6: 1224 | version "0.0.6" 1225 | resolved "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz" 1226 | integrity sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow== 1227 | 1228 | path-parse@^1.0.7: 1229 | version "1.0.7" 1230 | resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" 1231 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 1232 | 1233 | path-type@^4.0.0: 1234 | version "4.0.0" 1235 | resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" 1236 | integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== 1237 | 1238 | picocolors@^1.0.0: 1239 | version "1.0.0" 1240 | resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" 1241 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== 1242 | 1243 | picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: 1244 | version "2.3.1" 1245 | resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" 1246 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 1247 | 1248 | postcss-js@^4.0.0: 1249 | version "4.0.0" 1250 | resolved "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz" 1251 | integrity sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ== 1252 | dependencies: 1253 | camelcase-css "^2.0.1" 1254 | 1255 | postcss-load-config@^3.1.0: 1256 | version "3.1.1" 1257 | resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.1.tgz" 1258 | integrity sha512-c/9XYboIbSEUZpiD1UQD0IKiUe8n9WHYV7YFe7X7J+ZwCsEKkUJSFWjS9hBU1RR9THR7jMXst8sxiqP0jjo2mg== 1259 | dependencies: 1260 | lilconfig "^2.0.4" 1261 | yaml "^1.10.2" 1262 | 1263 | postcss-load-config@^3.1.4: 1264 | version "3.1.4" 1265 | resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" 1266 | integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== 1267 | dependencies: 1268 | lilconfig "^2.0.5" 1269 | yaml "^1.10.2" 1270 | 1271 | postcss-nested@5.0.6: 1272 | version "5.0.6" 1273 | resolved "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz" 1274 | integrity sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA== 1275 | dependencies: 1276 | postcss-selector-parser "^6.0.6" 1277 | 1278 | postcss-selector-parser@^6.0.10: 1279 | version "6.0.10" 1280 | resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" 1281 | integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== 1282 | dependencies: 1283 | cssesc "^3.0.0" 1284 | util-deprecate "^1.0.2" 1285 | 1286 | postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.8: 1287 | version "6.0.8" 1288 | resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.8.tgz" 1289 | integrity sha512-D5PG53d209Z1Uhcc0qAZ5U3t5HagH3cxu+WLZ22jt3gLUpXM4eXXfiO14jiDWST3NNooX/E8wISfOhZ9eIjGTQ== 1290 | dependencies: 1291 | cssesc "^3.0.0" 1292 | util-deprecate "^1.0.2" 1293 | 1294 | postcss-value-parser@^4.2.0: 1295 | version "4.2.0" 1296 | resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" 1297 | integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== 1298 | 1299 | postcss@^8.1.10, postcss@^8.4.5: 1300 | version "8.4.5" 1301 | resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz" 1302 | integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg== 1303 | dependencies: 1304 | nanoid "^3.1.30" 1305 | picocolors "^1.0.0" 1306 | source-map-js "^1.0.1" 1307 | 1308 | postcss@^8.4.12: 1309 | version "8.4.12" 1310 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905" 1311 | integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg== 1312 | dependencies: 1313 | nanoid "^3.3.1" 1314 | picocolors "^1.0.0" 1315 | source-map-js "^1.0.2" 1316 | 1317 | prosemirror-commands@^1.2.1: 1318 | version "1.2.2" 1319 | resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.2.2.tgz#1bd167372ee20abf488aca9cece63c43fab182c9" 1320 | integrity sha512-TX+KpWudMon06frryfpO/u7hsQv2hu8L4VSVbCpi3/7wXHBgl+35mV85qfa3RpT8xD2f3MdeoTqH0vy5JdbXPg== 1321 | dependencies: 1322 | prosemirror-model "^1.0.0" 1323 | prosemirror-state "^1.0.0" 1324 | prosemirror-transform "^1.0.0" 1325 | 1326 | prosemirror-dropcursor@^1.4.0: 1327 | version "1.4.0" 1328 | resolved "https://registry.yarnpkg.com/prosemirror-dropcursor/-/prosemirror-dropcursor-1.4.0.tgz#91a859d4ee79c99b1c0ba6ee61c093b195c0d9f0" 1329 | integrity sha512-6+YwTjmqDwlA/Dm+5wK67ezgqgjA/MhSDgaNxKUzH97SmeuWFXyLeDRxxOPZeSo7yTxcDGUCWTEjmQZsVBuMrQ== 1330 | dependencies: 1331 | prosemirror-state "^1.0.0" 1332 | prosemirror-transform "^1.1.0" 1333 | prosemirror-view "^1.1.0" 1334 | 1335 | prosemirror-gapcursor@^1.2.1: 1336 | version "1.2.2" 1337 | resolved "https://registry.yarnpkg.com/prosemirror-gapcursor/-/prosemirror-gapcursor-1.2.2.tgz#7394613ba4a1601fad1f36f1cff8961968c22ffa" 1338 | integrity sha512-7YzuRBbu9W7HGQde84kCHfIjaRLNcAdeijbgqrm/R9dsdTWkV+rrdcmic/sCc+bptiNpvjCEE+R6hrbT8zFQeQ== 1339 | dependencies: 1340 | prosemirror-keymap "^1.0.0" 1341 | prosemirror-model "^1.0.0" 1342 | prosemirror-state "^1.0.0" 1343 | prosemirror-view "^1.0.0" 1344 | 1345 | prosemirror-history@^1.2.0: 1346 | version "1.2.0" 1347 | resolved "https://registry.yarnpkg.com/prosemirror-history/-/prosemirror-history-1.2.0.tgz#04cc4df8d2f7b2a46651a2780de191ada6d465ea" 1348 | integrity sha512-B9v9xtf4fYbKxQwIr+3wtTDNLDZcmMMmGiI3TAPShnUzvo+Rmv1GiUrsQChY1meetHl7rhML2cppF3FTs7f7UQ== 1349 | dependencies: 1350 | prosemirror-state "^1.2.2" 1351 | prosemirror-transform "^1.0.0" 1352 | rope-sequence "^1.3.0" 1353 | 1354 | prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.5: 1355 | version "1.1.5" 1356 | resolved "https://registry.yarnpkg.com/prosemirror-keymap/-/prosemirror-keymap-1.1.5.tgz#b5984c7d30f5c75956c853126c54e9e624c0327b" 1357 | integrity sha512-8SZgPH3K+GLsHL2wKuwBD9rxhsbnVBTwpHCO4VUO5GmqUQlxd/2GtBVWTsyLq4Dp3N9nGgPd3+lZFKUDuVp+Vw== 1358 | dependencies: 1359 | prosemirror-state "^1.0.0" 1360 | w3c-keyname "^2.2.0" 1361 | 1362 | prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.16.1: 1363 | version "1.16.1" 1364 | resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.16.1.tgz#fb388270bc9609b66298d6a7e15d0cc1d6c61253" 1365 | integrity sha512-r1/w0HDU40TtkXp0DyKBnFPYwd8FSlUSJmGCGFv4DeynfeSlyQF2FD0RQbVEMOe6P3PpUSXM6LZBV7W/YNZ4mA== 1366 | dependencies: 1367 | orderedmap "^1.1.0" 1368 | 1369 | prosemirror-schema-list@^1.1.6: 1370 | version "1.1.6" 1371 | resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.1.6.tgz#c3e13fe2f74750e4a53ff88d798dc0c4ccca6707" 1372 | integrity sha512-aFGEdaCWmJzouZ8DwedmvSsL50JpRkqhQ6tcpThwJONVVmCgI36LJHtoQ4VGZbusMavaBhXXr33zyD2IVsTlkw== 1373 | dependencies: 1374 | prosemirror-model "^1.0.0" 1375 | prosemirror-transform "^1.0.0" 1376 | 1377 | prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.4: 1378 | version "1.3.4" 1379 | resolved "https://registry.yarnpkg.com/prosemirror-state/-/prosemirror-state-1.3.4.tgz#4c6b52628216e753fc901c6d2bfd84ce109e8952" 1380 | integrity sha512-Xkkrpd1y/TQ6HKzN3agsQIGRcLckUMA9u3j207L04mt8ToRgpGeyhbVv0HI7omDORIBHjR29b7AwlATFFf2GLA== 1381 | dependencies: 1382 | prosemirror-model "^1.0.0" 1383 | prosemirror-transform "^1.0.0" 1384 | 1385 | prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.3.3: 1386 | version "1.4.2" 1387 | resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.4.2.tgz#35f56091bcab3359f1eb90e82ce9f20cc52105c1" 1388 | integrity sha512-bcIsf3uRZhfab0xRfyyxOEh6eqSszq/hJbDbmUumFnbHBoWhB/uXbpz6vvUxfk0XiEvrZDJ+5pXRrNDc1Hu3vQ== 1389 | dependencies: 1390 | prosemirror-model "^1.0.0" 1391 | 1392 | prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.23.6: 1393 | version "1.23.12" 1394 | resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.23.12.tgz#196436a964abbd25a7d8efad879575e80d286ba6" 1395 | integrity sha512-uvw9ZVz5dNDD9w1bzHkU2r4NWFlpFz85v9rCD8NAhQBau6LYhwM/crjry+C4JgeR8gy6pMXS5eJ1zhNLcK4ctQ== 1396 | dependencies: 1397 | prosemirror-model "^1.16.0" 1398 | prosemirror-state "^1.0.0" 1399 | prosemirror-transform "^1.1.0" 1400 | 1401 | queue-microtask@^1.2.2: 1402 | version "1.2.3" 1403 | resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" 1404 | integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== 1405 | 1406 | quick-lru@^5.1.1: 1407 | version "5.1.1" 1408 | resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz" 1409 | integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== 1410 | 1411 | readdirp@~3.6.0: 1412 | version "3.6.0" 1413 | resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" 1414 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== 1415 | dependencies: 1416 | picomatch "^2.2.1" 1417 | 1418 | resolve-from@^4.0.0: 1419 | version "4.0.0" 1420 | resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" 1421 | integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== 1422 | 1423 | resolve@^1.20.0, resolve@^1.21.0: 1424 | version "1.21.0" 1425 | resolved "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz" 1426 | integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA== 1427 | dependencies: 1428 | is-core-module "^2.8.0" 1429 | path-parse "^1.0.7" 1430 | supports-preserve-symlinks-flag "^1.0.0" 1431 | 1432 | resolve@^1.22.0: 1433 | version "1.22.0" 1434 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" 1435 | integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== 1436 | dependencies: 1437 | is-core-module "^2.8.1" 1438 | path-parse "^1.0.7" 1439 | supports-preserve-symlinks-flag "^1.0.0" 1440 | 1441 | reusify@^1.0.4: 1442 | version "1.0.4" 1443 | resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" 1444 | integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== 1445 | 1446 | rollup@^2.59.0: 1447 | version "2.64.0" 1448 | resolved "https://registry.npmjs.org/rollup/-/rollup-2.64.0.tgz" 1449 | integrity sha512-+c+lbw1lexBKSMb1yxGDVfJ+vchJH3qLbmavR+awDinTDA2C5Ug9u7lkOzj62SCu0PKUExsW36tpgW7Fmpn3yQ== 1450 | optionalDependencies: 1451 | fsevents "~2.3.2" 1452 | 1453 | rope-sequence@^1.3.0: 1454 | version "1.3.2" 1455 | resolved "https://registry.yarnpkg.com/rope-sequence/-/rope-sequence-1.3.2.tgz#a19e02d72991ca71feb6b5f8a91154e48e3c098b" 1456 | integrity sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg== 1457 | 1458 | run-parallel@^1.1.9: 1459 | version "1.2.0" 1460 | resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" 1461 | integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== 1462 | dependencies: 1463 | queue-microtask "^1.2.2" 1464 | 1465 | socket.io-client@^2.4.0: 1466 | version "2.4.0" 1467 | resolved "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz" 1468 | integrity sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ== 1469 | dependencies: 1470 | backo2 "1.0.2" 1471 | component-bind "1.0.0" 1472 | component-emitter "~1.3.0" 1473 | debug "~3.1.0" 1474 | engine.io-client "~3.5.0" 1475 | has-binary2 "~1.0.2" 1476 | indexof "0.0.1" 1477 | parseqs "0.0.6" 1478 | parseuri "0.0.6" 1479 | socket.io-parser "~3.3.0" 1480 | to-array "0.1.4" 1481 | 1482 | socket.io-parser@~3.3.0: 1483 | version "3.3.2" 1484 | resolved "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.2.tgz" 1485 | integrity sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg== 1486 | dependencies: 1487 | component-emitter "~1.3.0" 1488 | debug "~3.1.0" 1489 | isarray "2.0.1" 1490 | 1491 | source-map-js@^1.0.1: 1492 | version "1.0.1" 1493 | resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz" 1494 | integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA== 1495 | 1496 | source-map-js@^1.0.2: 1497 | version "1.0.2" 1498 | resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" 1499 | integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== 1500 | 1501 | source-map@^0.6.1: 1502 | version "0.6.1" 1503 | resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" 1504 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 1505 | 1506 | sourcemap-codec@^1.4.4: 1507 | version "1.4.8" 1508 | resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz" 1509 | integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== 1510 | 1511 | supports-color@^5.3.0: 1512 | version "5.5.0" 1513 | resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" 1514 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 1515 | dependencies: 1516 | has-flag "^3.0.0" 1517 | 1518 | supports-color@^7.1.0: 1519 | version "7.2.0" 1520 | resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" 1521 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== 1522 | dependencies: 1523 | has-flag "^4.0.0" 1524 | 1525 | supports-preserve-symlinks-flag@^1.0.0: 1526 | version "1.0.0" 1527 | resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" 1528 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 1529 | 1530 | tailwindcss@^3.0.12: 1531 | version "3.0.24" 1532 | resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.0.24.tgz#22e31e801a44a78a1d9a81ecc52e13b69d85704d" 1533 | integrity sha512-H3uMmZNWzG6aqmg9q07ZIRNIawoiEcNFKDfL+YzOPuPsXuDXxJxB9icqzLgdzKNwjG3SAro2h9SYav8ewXNgig== 1534 | dependencies: 1535 | arg "^5.0.1" 1536 | chokidar "^3.5.3" 1537 | color-name "^1.1.4" 1538 | detective "^5.2.0" 1539 | didyoumean "^1.2.2" 1540 | dlv "^1.1.3" 1541 | fast-glob "^3.2.11" 1542 | glob-parent "^6.0.2" 1543 | is-glob "^4.0.3" 1544 | lilconfig "^2.0.5" 1545 | normalize-path "^3.0.0" 1546 | object-hash "^3.0.0" 1547 | picocolors "^1.0.0" 1548 | postcss "^8.4.12" 1549 | postcss-js "^4.0.0" 1550 | postcss-load-config "^3.1.4" 1551 | postcss-nested "5.0.6" 1552 | postcss-selector-parser "^6.0.10" 1553 | postcss-value-parser "^4.2.0" 1554 | quick-lru "^5.1.1" 1555 | resolve "^1.22.0" 1556 | 1557 | tailwindcss@^3.0.15: 1558 | version "3.0.15" 1559 | resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.15.tgz" 1560 | integrity sha512-bT2iy7FtjwgsXik4ZoJnHXR+SRCiGR1W95fVqpLZebr64m4ahwUwRbIAc5w5+2fzr1YF4Ct2eI7dojMRRl8sVQ== 1561 | dependencies: 1562 | arg "^5.0.1" 1563 | chalk "^4.1.2" 1564 | chokidar "^3.5.2" 1565 | color-name "^1.1.4" 1566 | cosmiconfig "^7.0.1" 1567 | detective "^5.2.0" 1568 | didyoumean "^1.2.2" 1569 | dlv "^1.1.3" 1570 | fast-glob "^3.2.7" 1571 | glob-parent "^6.0.2" 1572 | is-glob "^4.0.3" 1573 | normalize-path "^3.0.0" 1574 | object-hash "^2.2.0" 1575 | postcss-js "^4.0.0" 1576 | postcss-load-config "^3.1.0" 1577 | postcss-nested "5.0.6" 1578 | postcss-selector-parser "^6.0.8" 1579 | postcss-value-parser "^4.2.0" 1580 | quick-lru "^5.1.1" 1581 | resolve "^1.21.0" 1582 | 1583 | tippy.js@^6.3.7: 1584 | version "6.3.7" 1585 | resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c" 1586 | integrity sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ== 1587 | dependencies: 1588 | "@popperjs/core" "^2.9.0" 1589 | 1590 | to-array@0.1.4: 1591 | version "0.1.4" 1592 | resolved "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz" 1593 | integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= 1594 | 1595 | to-regex-range@^5.0.1: 1596 | version "5.0.1" 1597 | resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" 1598 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 1599 | dependencies: 1600 | is-number "^7.0.0" 1601 | 1602 | util-deprecate@^1.0.2: 1603 | version "1.0.2" 1604 | resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" 1605 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 1606 | 1607 | vite@^2.7.2: 1608 | version "2.7.12" 1609 | resolved "https://registry.npmjs.org/vite/-/vite-2.7.12.tgz" 1610 | integrity sha512-KvPYToRQWhRfBeVkyhkZ5hASuHQkqZUUdUcE3xyYtq5oYEPIJ0h9LWiWTO6v990glmSac2cEPeYeXzpX5Z6qKQ== 1611 | dependencies: 1612 | esbuild "^0.13.12" 1613 | postcss "^8.4.5" 1614 | resolve "^1.20.0" 1615 | rollup "^2.59.0" 1616 | optionalDependencies: 1617 | fsevents "~2.3.2" 1618 | 1619 | vue-router@^4: 1620 | version "4.0.12" 1621 | resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.0.12.tgz" 1622 | integrity sha512-CPXvfqe+mZLB1kBWssssTiWg4EQERyqJZes7USiqfW9B5N2x+nHlnsM1D3b5CaJ6qgCvMmYJnz+G0iWjNCvXrg== 1623 | dependencies: 1624 | "@vue/devtools-api" "^6.0.0-beta.18" 1625 | 1626 | vue@^3.2.25: 1627 | version "3.2.26" 1628 | resolved "https://registry.npmjs.org/vue/-/vue-3.2.26.tgz" 1629 | integrity sha512-KD4lULmskL5cCsEkfhERVRIOEDrfEL9CwAsLYpzptOGjaGFNWo3BQ9g8MAb7RaIO71rmVOziZ/uEN/rHwcUIhg== 1630 | dependencies: 1631 | "@vue/compiler-dom" "3.2.26" 1632 | "@vue/compiler-sfc" "3.2.26" 1633 | "@vue/runtime-dom" "3.2.26" 1634 | "@vue/server-renderer" "3.2.26" 1635 | "@vue/shared" "3.2.26" 1636 | 1637 | w3c-keyname@^2.2.0: 1638 | version "2.2.4" 1639 | resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.4.tgz#4ade6916f6290224cdbd1db8ac49eab03d0eef6b" 1640 | integrity sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw== 1641 | 1642 | ws@~7.4.2: 1643 | version "7.4.6" 1644 | resolved "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz" 1645 | integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== 1646 | 1647 | xmlhttprequest-ssl@~1.6.2: 1648 | version "1.6.3" 1649 | resolved "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz" 1650 | integrity sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q== 1651 | 1652 | xtend@^4.0.2: 1653 | version "4.0.2" 1654 | resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" 1655 | integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== 1656 | 1657 | yaml@^1.10.0, yaml@^1.10.2: 1658 | version "1.10.2" 1659 | resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" 1660 | integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== 1661 | 1662 | yeast@0.1.2: 1663 | version "0.1.2" 1664 | resolved "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz" 1665 | integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= 1666 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "photos", 3 | "version": "1.0.0", 4 | "description": "Open Source Alternative to Google Photos", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/gavindsouza/photos.git" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "bugs": { 14 | "url": "https://github.com/gavindsouza/photos/issues" 15 | }, 16 | "homepage": "https://github.com/gavindsouza/photos#readme", 17 | "dependencies": {} 18 | } 19 | -------------------------------------------------------------------------------- /photos/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.0.1" 2 | -------------------------------------------------------------------------------- /photos/api.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import frappe 4 | 5 | from photos.utils import get_image_path, image_resize 6 | 7 | 8 | @frappe.whitelist(methods=["GET", "POST"]) 9 | def roi(name: str): 10 | import cv2 11 | 12 | location, img = frappe.db.get_value("ROI", name, ["location", "image"]) 13 | _file = frappe.get_doc("File", img) 14 | top, right, bottom, left = json.loads(location) 15 | image = cv2.rectangle( 16 | cv2.imread(get_image_path(_file.file_url)), 17 | (left, top), 18 | (right, bottom), 19 | (0, 0, 255), 20 | 6, 21 | ) 22 | resized_img = image_resize(image, width=800, height=600) 23 | _, img = cv2.imencode(".jpg", resized_img) 24 | 25 | frappe.response.filename = f"temp_{_file.file_name}" 26 | frappe.response.type = "download" 27 | frappe.response.display_content_as = "inline" 28 | frappe.response.filecontent = img.tobytes() 29 | 30 | 31 | @frappe.whitelist(methods=["GET"]) 32 | def photo(name: str, roi: bool = False): 33 | import cv2 34 | 35 | photo = frappe.get_doc("Photo", name) 36 | _file = frappe.get_doc("File", photo.photo) 37 | image = cv2.imread(get_image_path(_file.file_url)) 38 | 39 | if roi: 40 | # draw roi for all encodings, possibly with labels 41 | for _roi in photo.people: 42 | location, person = frappe.db.get_value( 43 | "ROI", _roi.face, ["location", "person"] 44 | ) 45 | top, right, bottom, left = json.loads(location) 46 | image = cv2.rectangle(image, (left, top), (right, bottom), (0, 0, 255), 6) 47 | 48 | resized_img = image_resize(image, height=400) 49 | _, img = cv2.imencode(".jpg", resized_img) 50 | 51 | frappe.response.filename = f"temp_{_file.file_name}" 52 | frappe.response.type = "download" 53 | frappe.response.display_content_as = "inline" 54 | frappe.response.filecontent = img.tobytes() 55 | 56 | 57 | @frappe.whitelist() 58 | def filter_photo(*args, **kwargs): 59 | return frappe.get_list( 60 | "File", 61 | filters={ 62 | "is_folder": False, 63 | "name": ("not in", frappe.get_all("Photo", pluck="photo")), 64 | }, 65 | fields=["name", "file_name"], 66 | as_list=True, 67 | ) 68 | -------------------------------------------------------------------------------- /photos/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavindsouza/photos/768bad51b91e072e4da0671c4c8838455b8b3ca4/photos/config/__init__.py -------------------------------------------------------------------------------- /photos/config/desktop.py: -------------------------------------------------------------------------------- 1 | from frappe import _ 2 | 3 | 4 | def get_data(): 5 | return [ 6 | { 7 | "module_name": "Photos", 8 | "color": "orange", 9 | "icon": "octicon octicon-file-directory", 10 | "type": "module", 11 | "label": _("Photos"), 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /photos/config/docs.py: -------------------------------------------------------------------------------- 1 | """ 2 | Configuration for docs 3 | """ 4 | 5 | source_link = "https://github.com/gavindsouza/photos" 6 | docs_base_url = "https://gavindsouza.in/photos" 7 | headline = "App that does everything" 8 | sub_heading = "Yes, you got that right the first time, everything" 9 | 10 | 11 | def get_context(context): 12 | context.brand_html = "Photos" 13 | -------------------------------------------------------------------------------- /photos/hooks.py: -------------------------------------------------------------------------------- 1 | app_name = "photos" 2 | app_title = "Photos" 3 | app_publisher = "Gavin D'souza" 4 | app_description = "Open Source Alternative to Google Photos" 5 | app_icon = "octicon octicon-file-directory" 6 | app_color = "orange" 7 | app_email = "gavin18d@gmail.com" 8 | app_license = "MIT" 9 | 10 | # Includes in 11 | # ------------------ 12 | 13 | # include js, css files in header of desk.html 14 | # app_include_css = "/assets/photos/css/photos.css" 15 | # app_include_js = "/assets/photos/js/photos.js" 16 | 17 | # include js, css files in header of web template 18 | # web_include_css = "/assets/photos/css/photos.css" 19 | # web_include_js = "/assets/photos/js/photos.js" 20 | 21 | # include js, css files in header of web form 22 | # webform_include_js = {"doctype": "public/js/doctype.js"} 23 | # webform_include_css = {"doctype": "public/css/doctype.css"} 24 | 25 | # include js in page 26 | # page_js = {"page" : "public/js/file.js"} 27 | 28 | # include js in doctype views 29 | # doctype_js = {"doctype" : "public/js/doctype.js"} 30 | # doctype_list_js = {"doctype" : "public/js/doctype_list.js"} 31 | # doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"} 32 | # doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"} 33 | 34 | # Home Pages 35 | # ---------- 36 | 37 | # application home page (will override Website Settings) 38 | # home_page = "login" 39 | 40 | # website user home page (by Role) 41 | # role_home_page = { 42 | # "Role": "home_page" 43 | # } 44 | 45 | # Website user home page (by function) 46 | # get_website_user_home_page = "photos.utils.get_home_page" 47 | 48 | # Generators 49 | # ---------- 50 | 51 | # automatically create page for each record of this doctype 52 | # website_generators = ["Web Page"] 53 | 54 | # Installation 55 | # ------------ 56 | 57 | # before_install = "photos.install.before_install" 58 | # after_install = "photos.install.after_install" 59 | 60 | # Desk Notifications 61 | # ------------------ 62 | # See frappe.core.notifications.get_notification_config 63 | 64 | # notification_config = "photos.notifications.get_notification_config" 65 | 66 | # Permissions 67 | # ----------- 68 | # Permissions evaluated in scripted ways 69 | 70 | # permission_query_conditions = { 71 | # "Event": "frappe.desk.doctype.event.event.get_permission_query_conditions", 72 | # } 73 | # 74 | # has_permission = { 75 | # "Event": "frappe.desk.doctype.event.event.has_permission", 76 | # } 77 | 78 | # Document Events 79 | # --------------- 80 | # Hook on document methods and events 81 | 82 | doc_events = {"File": {"after_insert": "photos.utils.process_file"}} 83 | 84 | # Scheduled Tasks 85 | # --------------- 86 | 87 | # scheduler_events = { 88 | # "all": [ 89 | # "photos.tasks.all" 90 | # ], 91 | # "daily": [ 92 | # "photos.tasks.daily" 93 | # ], 94 | # "hourly": [ 95 | # "photos.tasks.hourly" 96 | # ], 97 | # "weekly": [ 98 | # "photos.tasks.weekly" 99 | # ] 100 | # "monthly": [ 101 | # "photos.tasks.monthly" 102 | # ] 103 | # } 104 | 105 | # Testing 106 | # ------- 107 | 108 | # before_tests = "photos.install.before_tests" 109 | 110 | # Overriding Methods 111 | # ------------------------------ 112 | # 113 | # override_whitelisted_methods = { 114 | # "frappe.desk.doctype.event.event.get_events": "photos.event.get_events" 115 | # } 116 | # 117 | # each overriding function accepts a `data` argument; 118 | # generated from the base implementation of the doctype dashboard, 119 | # along with any modifications made in other Frappe apps 120 | override_doctype_dashboards = {"File": "photos.utils.get_file_dashboard"} 121 | 122 | # exempt linked doctypes from being automatically cancelled 123 | # 124 | # auto_cancel_exempted_doctypes = ["Auto Repeat"] 125 | 126 | app_logo_url = "/assets/photos/logo.svg" 127 | 128 | website_context = { 129 | "favicon": "/assets/photos/favicon.ico", 130 | "splash_image": "/assets/photos/logo.svg", 131 | } 132 | 133 | website_route_rules = [ 134 | {"from_route": "/gallery/", "to_route": "gallery"}, 135 | ] 136 | -------------------------------------------------------------------------------- /photos/modules.txt: -------------------------------------------------------------------------------- 1 | Photos -------------------------------------------------------------------------------- /photos/patches.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavindsouza/photos/768bad51b91e072e4da0671c4c8838455b8b3ca4/photos/patches.txt -------------------------------------------------------------------------------- /photos/photos/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavindsouza/photos/768bad51b91e072e4da0671c4c8838455b8b3ca4/photos/photos/__init__.py -------------------------------------------------------------------------------- /photos/photos/desk_page/photos/photos.json: -------------------------------------------------------------------------------- 1 | { 2 | "cards": [], 3 | "category": "Modules", 4 | "charts": [], 5 | "charts_label": "Photos", 6 | "creation": "2020-05-23 19:38:03.299510", 7 | "developer_mode_only": 0, 8 | "disable_user_customization": 0, 9 | "docstatus": 0, 10 | "doctype": "Desk Page", 11 | "extends_another_page": 0, 12 | "hide_custom": 0, 13 | "idx": 0, 14 | "is_standard": 1, 15 | "label": "Photos", 16 | "modified": "2020-11-15 02:54:38.256924", 17 | "modified_by": "Administrator", 18 | "module": "Photos", 19 | "name": "Photos", 20 | "owner": "Administrator", 21 | "pin_to_bottom": 0, 22 | "pin_to_top": 1, 23 | "shortcuts": [ 24 | { 25 | "doc_view": "List", 26 | "label": "Photos", 27 | "link_to": "Photo", 28 | "stats_filter": "{\"is_processed\":true}", 29 | "type": "DocType" 30 | }, 31 | { 32 | "doc_view": "List", 33 | "label": "People", 34 | "link_to": "Person", 35 | "stats_filter": "{}", 36 | "type": "DocType" 37 | } 38 | ], 39 | "shortcuts_label": "Photos" 40 | } -------------------------------------------------------------------------------- /photos/photos/doctype/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavindsouza/photos/768bad51b91e072e4da0671c4c8838455b8b3ca4/photos/photos/doctype/__init__.py -------------------------------------------------------------------------------- /photos/photos/doctype/person/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavindsouza/photos/768bad51b91e072e4da0671c4c8838455b8b3ca4/photos/photos/doctype/person/__init__.py -------------------------------------------------------------------------------- /photos/photos/doctype/person/person.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020, Gavin D'souza and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Person', { 5 | // refresh: function(frm) { 6 | 7 | // } 8 | }); 9 | -------------------------------------------------------------------------------- /photos/photos/doctype/person/person.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "creation": "2021-11-12 15:33:36.846024", 4 | "doctype": "DocType", 5 | "editable_grid": 1, 6 | "engine": "InnoDB", 7 | "field_order": [ 8 | "person_name", 9 | "user", 10 | "person_image" 11 | ], 12 | "fields": [ 13 | { 14 | "fieldname": "user", 15 | "fieldtype": "Link", 16 | "label": "User", 17 | "options": "User" 18 | }, 19 | { 20 | "fieldname": "person_name", 21 | "fieldtype": "Data", 22 | "label": "Person Name" 23 | }, 24 | { 25 | "fieldname": "person_image", 26 | "fieldtype": "Attach Image", 27 | "label": "Profile Pic" 28 | } 29 | ], 30 | "index_web_pages_for_search": 1, 31 | "links": [], 32 | "modified": "2022-07-29 21:48:01.714254", 33 | "modified_by": "Administrator", 34 | "module": "Photos", 35 | "name": "Person", 36 | "owner": "Administrator", 37 | "permissions": [ 38 | { 39 | "create": 1, 40 | "delete": 1, 41 | "email": 1, 42 | "export": 1, 43 | "print": 1, 44 | "read": 1, 45 | "report": 1, 46 | "role": "System Manager", 47 | "share": 1, 48 | "write": 1 49 | } 50 | ], 51 | "quick_entry": 1, 52 | "search_fields": "person_name", 53 | "show_title_field_in_link": 1, 54 | "sort_field": "modified", 55 | "sort_order": "DESC", 56 | "states": [], 57 | "title_field": "person_name", 58 | "track_changes": 1 59 | } -------------------------------------------------------------------------------- /photos/photos/doctype/person/person.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020, Gavin D'souza and contributors 2 | # For license information, please see license.txt 3 | import json 4 | 5 | import frappe 6 | from frappe.model.document import Document 7 | 8 | from photos.utils import get_image_path 9 | 10 | 11 | class Person(Document): 12 | def validate(self): 13 | if not self.get("person_image"): 14 | self.set_profile_pic(save=False) 15 | 16 | def set_profile_pic(self, save=True): 17 | if self.user: 18 | # get user profile pic 19 | self.person_image = frappe.db.get_value("User", self.user, "user_image") 20 | else: 21 | # get random pic from ROI and set the pic here 22 | self.person_image = self.generate_profile_pic() 23 | 24 | if save: 25 | self.save() 26 | 27 | def generate_profile_pic(self): 28 | result = frappe.get_all( 29 | "ROI", 30 | filters={"person": self.person_name}, 31 | limit_page_length=1, 32 | order_by="creation desc", 33 | fields=["image", "location"], 34 | as_list=True, 35 | ) 36 | if result: 37 | import cv2 38 | 39 | _image, location = result[0] 40 | image_path = get_image_path(frappe.db.get_value("File", _image, "file_url")) 41 | top, right, bottom, left = json.loads(location) 42 | # TODO: fix cropping logic 43 | img_cropped = cv2.imread(image_path)[left:right, top:bottom] 44 | # TODO: create a file doc for temp profile pic and link it to self.person_image 45 | # create and save image in the tmp dir? frappe will probs copy the contents inside? 46 | cv2.imwrite("./temp_crop.jpg", img_cropped) 47 | else: 48 | # random gravatar?? 49 | pass 50 | -------------------------------------------------------------------------------- /photos/photos/doctype/person/person_dashboard.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020, Gavin D'souza and Contributors 2 | # MIT License. See license.txt 3 | 4 | 5 | def get_data(): 6 | return { 7 | "fieldname": "person", 8 | "transactions": [ 9 | {"label": "People", "items": ["ROI"]}, 10 | ], 11 | } 12 | -------------------------------------------------------------------------------- /photos/photos/doctype/person/test_person.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020, Gavin D'souza and Contributors 2 | # See license.txt 3 | import unittest 4 | 5 | 6 | class TestPerson(unittest.TestCase): 7 | pass 8 | -------------------------------------------------------------------------------- /photos/photos/doctype/photo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavindsouza/photos/768bad51b91e072e4da0671c4c8838455b8b3ca4/photos/photos/doctype/photo/__init__.py -------------------------------------------------------------------------------- /photos/photos/doctype/photo/photo.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020, Gavin D'souza and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Photo', { 5 | onload: function(frm) { 6 | if (!frm.doc.photo) { 7 | frm.fields_dict["photo"].get_query = function(doc, dt, dn) { 8 | return { 9 | query:"photos.api.filter_photo", 10 | } 11 | }; 12 | } 13 | frappe.realtime.on("refresh_photo", () => { 14 | frm.reload_doc(); 15 | }); 16 | }, 17 | refresh: function(frm) { 18 | frm.add_custom_button("Process Photo", function () { 19 | frm.call("process_photo").then(r => { 20 | if (!r.exc) { 21 | frappe.show_alert({ 22 | message: "Photo processing queued successfully", 23 | indicator: "green" 24 | }); 25 | } else { 26 | console.error(r); 27 | } 28 | }); 29 | }); 30 | const wrapper = frm.get_field("preview").$wrapper; 31 | if (frm.is_new()) { 32 | wrapper.html(""); 33 | } else { 34 | wrapper.html(` 35 |
36 | 37 | 38 |
39 | `); 40 | } 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /photos/photos/doctype/photo/photo.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "creation": "2020-11-03 11:15:21.725820", 4 | "doctype": "DocType", 5 | "editable_grid": 1, 6 | "engine": "InnoDB", 7 | "field_order": [ 8 | "preview", 9 | "photo", 10 | "is_processed", 11 | "number_of_times_processed", 12 | "people", 13 | "objects" 14 | ], 15 | "fields": [ 16 | { 17 | "fieldname": "photo", 18 | "fieldtype": "Link", 19 | "label": "Photo", 20 | "options": "File", 21 | "set_only_once": 1, 22 | "unique": 1 23 | }, 24 | { 25 | "default": "0", 26 | "fieldname": "is_processed", 27 | "fieldtype": "Check", 28 | "in_list_view": 1, 29 | "label": "Is Processed" 30 | }, 31 | { 32 | "default": "0", 33 | "fieldname": "number_of_times_processed", 34 | "fieldtype": "Int", 35 | "in_list_view": 1, 36 | "label": "Number of times Processed" 37 | }, 38 | { 39 | "fieldname": "people", 40 | "fieldtype": "Table", 41 | "label": "People", 42 | "options": "ROI Person" 43 | }, 44 | { 45 | "fieldname": "objects", 46 | "fieldtype": "Table", 47 | "label": "Objects", 48 | "options": "ROI Object" 49 | }, 50 | { 51 | "fieldname": "preview", 52 | "fieldtype": "HTML", 53 | "label": "Preview", 54 | "read_only": 1 55 | } 56 | ], 57 | "index_web_pages_for_search": 1, 58 | "links": [], 59 | "modified": "2020-11-15 16:10:39.247669", 60 | "modified_by": "Administrator", 61 | "module": "Photos", 62 | "name": "Photo", 63 | "owner": "Administrator", 64 | "permissions": [ 65 | { 66 | "create": 1, 67 | "delete": 1, 68 | "email": 1, 69 | "export": 1, 70 | "print": 1, 71 | "read": 1, 72 | "report": 1, 73 | "role": "System Manager", 74 | "share": 1, 75 | "write": 1 76 | } 77 | ], 78 | "sort_field": "modified", 79 | "sort_order": "DESC", 80 | "track_changes": 1 81 | } -------------------------------------------------------------------------------- /photos/photos/doctype/photo/photo.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020, Gavin D'souza and contributors 2 | # For license information, please see license.txt 3 | from contextlib import suppress 4 | 5 | import frappe 6 | from frappe.exceptions import DuplicateEntryError 7 | from frappe.model.document import Document 8 | 9 | 10 | class Photo(Document): 11 | def validate(self): 12 | # TODO checklist: 13 | # - check if file type is supported 14 | # - extract and save image meta data 15 | # - probably parse and save as JSON which can be updated via the UI and written to the file 16 | pass 17 | 18 | def after_insert(self): 19 | # start processing etc, maybe via frappe.enqueue 20 | frappe.enqueue( 21 | "photos.photos.doctype.photo.photo.process_photo", queue="long", photo=self 22 | ) 23 | 24 | @frappe.whitelist() 25 | def process_photo(self): 26 | # re-run process photo for whatever reason 27 | frappe.enqueue( 28 | "photos.photos.doctype.photo.photo.process_photo", queue="long", photo=self 29 | ) 30 | 31 | 32 | def process_photo(photo: Photo): 33 | """Processes photo and searches Persons and Objects in them. 34 | 35 | TODO: 36 | - locating objects 37 | 38 | Args: 39 | photo (Photo): Photo document object 40 | """ 41 | import json 42 | 43 | import face_recognition 44 | import numpy as np 45 | from frappe.core.doctype.file.file import get_local_image 46 | 47 | people = [] 48 | image, filename, extn = get_local_image( 49 | frappe.db.get_value("File", photo.photo, "file_url") 50 | ) 51 | img = np.asarray(image) 52 | # TODO: make image smaller? check if necessary and x4 box sizes before saving them 53 | # img = cv2.resize(np.asarray(image), (0, 0), fx=0.25, fy=0.25) 54 | boxes = face_recognition.face_locations(img, model="hog") 55 | encodings = face_recognition.face_encodings(img, boxes) 56 | 57 | for (encoding, location) in zip(encodings, boxes): 58 | roi = frappe.new_doc("ROI") 59 | roi.image = photo.photo 60 | roi.location = json.dumps(location) 61 | roi.encoding = json.dumps(encoding.tolist()) 62 | with suppress(DuplicateEntryError): 63 | roi.insert() 64 | people.append(roi.name) 65 | 66 | for x in people: 67 | photo.append("people", {"face": x}) 68 | 69 | photo.number_of_times_processed += 1 70 | photo.is_processed = True 71 | photo.save() 72 | 73 | frappe.publish_realtime( 74 | "refresh_photo", user=frappe.session.user, after_commit=True 75 | ) 76 | # TODO: Show msgprint to user that photo has been processed 77 | 78 | return photo 79 | -------------------------------------------------------------------------------- /photos/photos/doctype/photo/test_photo.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020, Gavin D'souza and Contributors 2 | # See license.txt 3 | # import frappe 4 | import unittest 5 | 6 | 7 | class TestPhoto(unittest.TestCase): 8 | pass 9 | -------------------------------------------------------------------------------- /photos/photos/doctype/roi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavindsouza/photos/768bad51b91e072e4da0671c4c8838455b8b3ca4/photos/photos/doctype/roi/__init__.py -------------------------------------------------------------------------------- /photos/photos/doctype/roi/roi.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020, Gavin D'souza and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('ROI', { 5 | onload: function(frm) { 6 | frappe.realtime.on("refresh_roi", () => { 7 | frm.reload_doc(); 8 | }); 9 | }, 10 | refresh: function(frm) { 11 | const wrapper = frm.get_field("preview").$wrapper; 12 | wrapper.html(` 13 |
14 | 15 |
16 | `); 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /photos/photos/doctype/roi/roi.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "creation": "2020-05-23 21:01:33.727463", 4 | "doctype": "DocType", 5 | "editable_grid": 1, 6 | "engine": "InnoDB", 7 | "field_order": [ 8 | "preview", 9 | "person", 10 | "related_information_section", 11 | "image", 12 | "encoding", 13 | "location" 14 | ], 15 | "fields": [ 16 | { 17 | "fieldname": "image", 18 | "fieldtype": "Link", 19 | "in_list_view": 1, 20 | "label": "Image", 21 | "options": "File", 22 | "read_only": 1, 23 | "reqd": 1 24 | }, 25 | { 26 | "fieldname": "encoding", 27 | "fieldtype": "Code", 28 | "label": "Encoding", 29 | "read_only": 1 30 | }, 31 | { 32 | "fieldname": "location", 33 | "fieldtype": "Data", 34 | "label": "Location", 35 | "read_only": 1 36 | }, 37 | { 38 | "fieldname": "preview", 39 | "fieldtype": "HTML", 40 | "label": "Preview" 41 | }, 42 | { 43 | "collapsible": 1, 44 | "fieldname": "related_information_section", 45 | "fieldtype": "Section Break", 46 | "label": "Related Information" 47 | }, 48 | { 49 | "fieldname": "person", 50 | "fieldtype": "Link", 51 | "in_list_view": 1, 52 | "label": "Person Identified", 53 | "options": "Person" 54 | } 55 | ], 56 | "index_web_pages_for_search": 1, 57 | "links": [], 58 | "modified": "2020-11-15 01:25:08.620655", 59 | "modified_by": "Administrator", 60 | "module": "Photos", 61 | "name": "ROI", 62 | "owner": "Administrator", 63 | "permissions": [ 64 | { 65 | "create": 1, 66 | "delete": 1, 67 | "email": 1, 68 | "export": 1, 69 | "print": 1, 70 | "read": 1, 71 | "report": 1, 72 | "role": "System Manager", 73 | "share": 1, 74 | "write": 1 75 | } 76 | ], 77 | "quick_entry": 1, 78 | "sort_field": "modified", 79 | "sort_order": "DESC", 80 | "track_changes": 1 81 | } -------------------------------------------------------------------------------- /photos/photos/doctype/roi/roi.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020, Gavin D'souza and contributors 2 | # For license information, please see license.txt 3 | import json 4 | from random import choice 5 | 6 | import face_recognition 7 | import frappe 8 | import numpy as np 9 | from frappe.model.document import Document 10 | 11 | 12 | class ROI(Document): 13 | def validate(self): 14 | # don't let duplicate ROIs in 15 | doc_exists = frappe.db.exists( 16 | "ROI", 17 | { 18 | "encoding": self.encoding, 19 | "location": self.location, 20 | "image": self.image, 21 | }, 22 | ) 23 | 24 | if doc_exists and self.name != doc_exists: 25 | frappe.throw("ROI already exists!", frappe.DuplicateEntryError) 26 | 27 | def after_insert(self): 28 | self.process_roi() 29 | 30 | def process_roi(self): 31 | known_rois = frappe.get_all( 32 | "ROI", filters={"person": ("!=", "")}, fields=["person", "encoding"] 33 | ) 34 | if known_rois: 35 | known_face_names, known_face_encodings = zip( 36 | *[(x.person, json.loads(x.encoding)) for x in known_rois] 37 | ) 38 | unknown_encoding = json.loads(self.encoding) 39 | matches = face_recognition.compare_faces( 40 | np.asarray(known_face_encodings), np.asarray(unknown_encoding) 41 | ) 42 | # # If a match was found in known_face_encodings, just use the first one. 43 | if True in matches: 44 | first_match_index = matches.index(True) 45 | self.db_set("person", known_face_names[first_match_index]) 46 | frappe.publish_realtime("refresh_roi", user=frappe.session.user) 47 | 48 | 49 | def process_labelled_photos(): 50 | """Compares unlabelled photos with labelled ones to label more. 51 | 52 | Returns: 53 | list: unrecognized ROIs 54 | """ 55 | # frappe develop seems to have a bug which returns empty list if person is 56 | # filtered by None. So, using "" as replacement, since that works... 57 | known_rois = frappe.get_all( 58 | "ROI", filters={"person": ("!=", "")}, fields=["person", "encoding"] 59 | ) 60 | 61 | unrecognized_rois = frappe.get_all( 62 | "ROI", filters={"person": ""}, fields=["name", "encoding"] 63 | ) 64 | recognized_photos = {} 65 | 66 | # match unknown encodings with known ones 67 | if known_rois: 68 | known_face_names, known_face_encodings = zip( 69 | *[(x.person, json.loads(x.encoding)) for x in known_rois] 70 | ) 71 | for unknown in unrecognized_rois: 72 | unknown_encoding = json.loads(unknown.encoding) 73 | matches = face_recognition.compare_faces( 74 | np.asarray(known_face_encodings), np.asarray(unknown_encoding) 75 | ) 76 | # # If a match was found in known_face_encodings, just use the first one. 77 | if True in matches: 78 | first_match_index = matches.index(True) 79 | person = known_face_names[first_match_index] 80 | recognized_photos[unknown.name] = person 81 | 82 | # update records with newly discovered data 83 | for roi, person in recognized_photos.items(): 84 | frappe.db.set_value("ROI", roi, "person", person) 85 | 86 | # remove any newly found entries from the unrecognized photos 87 | unrecognized_rois = [ 88 | x for x in unrecognized_rois if x.name not in recognized_photos 89 | ] 90 | 91 | return unrecognized_rois 92 | 93 | 94 | def process_unlabelled_photos(unrecognized_rois=None): 95 | """Assigns one random label for an ROI and runs process_labelled_photos""" 96 | if not unrecognized_rois: 97 | unrecognized_rois = process_labelled_photos() 98 | # frappe.get_all("ROI", filters={"person": ""}, fields=["name", "encoding"]) 99 | 100 | # compare faces of recognized with unrecognized labels 101 | # if no recognized ROIs exist, start grouping similar faces..ie set a temporary name for them 102 | roi = choice(unrecognized_rois) 103 | 104 | person = frappe.new_doc("Person") 105 | person.person_name = frappe.mock("name") 106 | person.save() 107 | 108 | frappe.db.set_value("ROI", roi.name, "person", person.name) 109 | 110 | return process_labelled_photos() 111 | -------------------------------------------------------------------------------- /photos/photos/doctype/roi/test_roi.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020, Gavin D'souza and Contributors 2 | # See license.txt 3 | # import frappe 4 | import unittest 5 | 6 | 7 | class TestROI(unittest.TestCase): 8 | pass 9 | -------------------------------------------------------------------------------- /photos/photos/doctype/roi_object/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavindsouza/photos/768bad51b91e072e4da0671c4c8838455b8b3ca4/photos/photos/doctype/roi_object/__init__.py -------------------------------------------------------------------------------- /photos/photos/doctype/roi_object/roi_object.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "creation": "2020-11-03 11:15:03.804413", 4 | "doctype": "DocType", 5 | "editable_grid": 1, 6 | "engine": "InnoDB", 7 | "field_order": [ 8 | "object" 9 | ], 10 | "fields": [ 11 | { 12 | "fieldname": "object", 13 | "fieldtype": "Link", 14 | "in_list_view": 1, 15 | "label": "Object", 16 | "options": "ROI" 17 | } 18 | ], 19 | "index_web_pages_for_search": 1, 20 | "istable": 1, 21 | "links": [], 22 | "modified": "2020-11-14 14:46:27.197377", 23 | "modified_by": "Administrator", 24 | "module": "Photos", 25 | "name": "ROI Object", 26 | "owner": "Administrator", 27 | "permissions": [], 28 | "sort_field": "modified", 29 | "sort_order": "DESC", 30 | "track_changes": 1 31 | } -------------------------------------------------------------------------------- /photos/photos/doctype/roi_object/roi_object.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020, Gavin D'souza and contributors 2 | # For license information, please see license.txt 3 | # import frappe 4 | from frappe.model.document import Document 5 | 6 | 7 | class ROIObject(Document): 8 | pass 9 | -------------------------------------------------------------------------------- /photos/photos/doctype/roi_person/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavindsouza/photos/768bad51b91e072e4da0671c4c8838455b8b3ca4/photos/photos/doctype/roi_person/__init__.py -------------------------------------------------------------------------------- /photos/photos/doctype/roi_person/roi_person.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "creation": "2020-11-03 11:14:19.046159", 4 | "doctype": "DocType", 5 | "editable_grid": 1, 6 | "engine": "InnoDB", 7 | "field_order": [ 8 | "face" 9 | ], 10 | "fields": [ 11 | { 12 | "fieldname": "face", 13 | "fieldtype": "Link", 14 | "in_list_view": 1, 15 | "label": "Face", 16 | "options": "ROI" 17 | } 18 | ], 19 | "index_web_pages_for_search": 1, 20 | "istable": 1, 21 | "links": [], 22 | "modified": "2020-11-14 18:54:57.762608", 23 | "modified_by": "Administrator", 24 | "module": "Photos", 25 | "name": "ROI Person", 26 | "owner": "Administrator", 27 | "permissions": [], 28 | "sort_field": "modified", 29 | "sort_order": "DESC", 30 | "track_changes": 1 31 | } -------------------------------------------------------------------------------- /photos/photos/doctype/roi_person/roi_person.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020, Gavin D'souza and contributors 2 | # For license information, please see license.txt 3 | # import frappe 4 | from frappe.model.document import Document 5 | 6 | 7 | class ROIPerson(Document): 8 | pass 9 | -------------------------------------------------------------------------------- /photos/photos/workspace/photos/photos.json: -------------------------------------------------------------------------------- 1 | { 2 | "charts": [], 3 | "content": "[{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Photos\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"People\",\"col\":4}}]", 4 | "creation": "2020-05-23 19:38:03.299510", 5 | "docstatus": 0, 6 | "doctype": "Workspace", 7 | "hide_custom": 0, 8 | "icon": "scan", 9 | "idx": 0, 10 | "label": "Photos", 11 | "links": [], 12 | "modified": "2022-01-16 01:16:12.423276", 13 | "modified_by": "Administrator", 14 | "module": "Photos", 15 | "name": "Photos", 16 | "owner": "Administrator", 17 | "public": 1, 18 | "roles": [], 19 | "sequence_id": 0, 20 | "shortcuts": [ 21 | { 22 | "color": "Cyan", 23 | "doc_view": "List", 24 | "format": "{} Processed", 25 | "label": "Photos", 26 | "link_to": "Photo", 27 | "stats_filter": "{\"is_processed\":[\"=\",1]}", 28 | "type": "DocType" 29 | }, 30 | { 31 | "color": "Grey", 32 | "doc_view": "List", 33 | "format": "{} Faces", 34 | "label": "People", 35 | "link_to": "ROI", 36 | "stats_filter": "{\"person\":[\"is\",\"set\"]}", 37 | "type": "DocType" 38 | } 39 | ], 40 | "title": "Photos" 41 | } -------------------------------------------------------------------------------- /photos/public/css/font-awesome.min.css: -------------------------------------------------------------------------------- 1 | window.FontAwesomeKitConfig = {"asyncLoading":{"enabled":false},"autoA11y":{"enabled":true},"baseUrl":"https://ka-f.fontawesome.com","baseUrlKit":"https://kit.fontawesome.com","detectConflictsUntil":null,"iconUploads":{},"id":660416,"license":"free","method":"css","minify":{"enabled":true},"token":"RANDOM","v4FontFaceShim":{"enabled":true},"v4shim":{"enabled":true},"v5FontFaceShim":{"enabled":false},"version":"5.15.4"}; 2 | !function(t){"function"==typeof define&&define.amd?define("kit-loader",t):t()}((function(){"use strict";function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}function e(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function n(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);e&&(o=o.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,o)}return n}function o(t){for(var o=1;ot.length)&&(e=t.length);for(var n=0,o=new Array(e);n2&&void 0!==arguments[2]?arguments[2]:function(){},r=e.document||r,i=u.bind(u,r,["fa","fab","fas","far","fal","fad","fak"]),f=Object.keys(t.iconUploads||{}).length>0;t.autoA11y.enabled&&n(i);var s=[{id:"fa-main",addOn:void 0}];t.v4shim&&t.v4shim.enabled&&s.push({id:"fa-v4-shims",addOn:"-v4-shims"}),t.v5FontFaceShim&&t.v5FontFaceShim.enabled&&s.push({id:"fa-v5-font-face",addOn:"-v5-font-face"}),t.v4FontFaceShim&&t.v4FontFaceShim.enabled&&s.push({id:"fa-v4-font-face",addOn:"-v4-font-face"}),f&&s.push({id:"fa-kit-upload",customCss:!0});var d=s.map((function(n){return new _((function(r,i){F(n.customCss?a(t):c(t,{addOn:n.addOn,minify:t.minify.enabled}),e).then((function(i){r(U(i,o(o({},e),{},{baseUrl:t.baseUrl,version:t.version,id:n.id,contentFilter:function(t,e){return P(t,e.baseUrl,e.version)}})))})).catch(i)}))}));return _.all(d)}function U(t,e){var n=e.contentFilter||function(t,e){return t},o=document.createElement("style"),r=document.createTextNode(n(t,e));return o.appendChild(r),o.media="all",e.id&&o.setAttribute("id",e.id),e&&e.detectingConflicts&&e.detectionIgnoreAttr&&o.setAttributeNode(document.createAttribute(e.detectionIgnoreAttr)),o}function k(t,e){e.autoA11y=t.autoA11y.enabled,"pro"===t.license&&(e.autoFetchSvg=!0,e.fetchSvgFrom=t.baseUrl+"/releases/"+("latest"===t.version?"latest":"v".concat(t.version))+"/svgs",e.fetchUploadedSvgFrom=t.uploadsUrl);var n=[];return t.v4shim.enabled&&n.push(new _((function(n,r){F(c(t,{addOn:"-v4-shims",minify:t.minify.enabled}),e).then((function(t){n(I(t,o(o({},e),{},{id:"fa-v4-shims"})))})).catch(r)}))),n.push(new _((function(n,r){F(c(t,{minify:t.minify.enabled}),e).then((function(t){var r=I(t,o(o({},e),{},{id:"fa-main"}));n(function(t,e){var n=e&&void 0!==e.autoFetchSvg?e.autoFetchSvg:void 0,o=e&&void 0!==e.autoA11y?e.autoA11y:void 0;void 0!==o&&t.setAttribute("data-auto-a11y",o?"true":"false");n&&(t.setAttributeNode(document.createAttribute("data-auto-fetch-svg")),t.setAttribute("data-fetch-svg-from",e.fetchSvgFrom),t.setAttribute("data-fetch-uploaded-svg-from",e.fetchUploadedSvgFrom));return t}(r,e))})).catch(r)}))),_.all(n)}function I(t,e){var n=document.createElement("SCRIPT"),o=document.createTextNode(t);return n.appendChild(o),n.referrerPolicy="strict-origin",e.id&&n.setAttribute("id",e.id),e&&e.detectingConflicts&&e.detectionIgnoreAttr&&n.setAttributeNode(document.createAttribute(e.detectionIgnoreAttr)),n}function L(t){var e,n=[],o=document,r=o.documentElement.doScroll,i=(r?/^loaded|^c/:/^loaded|^i|^c/).test(o.readyState);i||o.addEventListener("DOMContentLoaded",e=function(){for(o.removeEventListener("DOMContentLoaded",e),i=1;e=n.shift();)e()}),i?setTimeout(t,0):n.push(t)}function T(t){"undefined"!=typeof MutationObserver&&new MutationObserver(t).observe(document,{childList:!0,subtree:!0})}try{if(window.FontAwesomeKitConfig){var x=window.FontAwesomeKitConfig,M={detectingConflicts:x.detectConflictsUntil&&new Date<=new Date(x.detectConflictsUntil),detectionIgnoreAttr:"data-fa-detection-ignore",fetch:window.fetch,token:x.token,XMLHttpRequest:window.XMLHttpRequest,document:document},D=document.currentScript,N=D?D.parentElement:document.head;(function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return"js"===t.method?k(t,e):"css"===t.method?C(t,e,(function(t){L(t),T(t)})):void 0})(x,M).then((function(t){t.map((function(t){try{N.insertBefore(t,D?D.nextSibling:null)}catch(e){N.appendChild(t)}})),M.detectingConflicts&&D&&L((function(){D.setAttributeNode(document.createAttribute(M.detectionIgnoreAttr));var t=function(t,e){var n=document.createElement("script");return e&&e.detectionIgnoreAttr&&n.setAttributeNode(document.createAttribute(e.detectionIgnoreAttr)),n.src=c(t,{baseFilename:"conflict-detection",fileSuffix:"js",subdir:"js",minify:t.minify.enabled}),n}(x,M);document.body.appendChild(t)}))})).catch((function(t){console.error("".concat("Font Awesome Kit:"," ").concat(t))}))}}catch(t){console.error("".concat("Font Awesome Kit:"," ").concat(t))}})); 3 | -------------------------------------------------------------------------------- /photos/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavindsouza/photos/768bad51b91e072e4da0671c4c8838455b8b3ca4/photos/public/favicon.ico -------------------------------------------------------------------------------- /photos/reference.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | import cv2 4 | import face_recognition 5 | import numpy as np 6 | 7 | # This is a demo of running face recognition on live video from your webcam. It's a little more complicated than the 8 | # other example, but it includes some basic performance tweaks to make things run a lot faster: 9 | # 1. Process each video frame at 1/4 resolution (though still display it at full resolution) 10 | # 2. Only detect faces in every other frame of video. 11 | # PLEASE NOTE: This example requires OpenCV (the `cv2` library) to be installed only to read from your webcam. 12 | # OpenCV is *not* required to use the face_recognition library. It's only required if you want to run this 13 | # specific demo. If you have trouble installing it, try any of the other demos that don't require it instead. 14 | 15 | start = time.time() 16 | # Load a sample picture and learn how to recognize it. 17 | gavin_image = face_recognition.load_image_file("/Users/gavin/pics/gavin5.jpg") 18 | gavin_face_encoding = face_recognition.face_encodings(gavin_image)[0] 19 | 20 | after = time.time() 21 | print(f"Gavin: {after - start}s") 22 | 23 | start = time.time() 24 | # Load a second sample picture and learn how to recognize it. 25 | sianne_image = face_recognition.load_image_file("/Users/gavin/pics/sianne3.jpg") 26 | sianne_face_encoding = face_recognition.face_encodings(sianne_image)[0] 27 | 28 | after = time.time() 29 | print(f"Sianne: {after - start}s") 30 | 31 | start = time.time() 32 | # Load a second sample picture and learn how to recognize it. 33 | mom_sianne_image = face_recognition.load_image_file("/Users/gavin/pics/mom.jpg") 34 | mom_sianne_face_encoding = face_recognition.face_encodings(mom_sianne_image) 35 | mom_encoding = mom_sianne_face_encoding[0] 36 | sianne_encoding = mom_sianne_face_encoding[1] 37 | 38 | after = time.time() 39 | print(f"Both: {after - start}s") 40 | 41 | start = time.time() 42 | # Load a second sample picture and learn how to recognize it. 43 | # dad_image = face_recognition.load_image_file("dad1.jpg") 44 | # dad_face_encoding = face_recognition.face_encodings(dad_image)[0] 45 | 46 | after = time.time() 47 | print(f"Dad: {after - start}s") 48 | 49 | # Create arrays of known face encodings and their names 50 | known_face_encodings = [ 51 | gavin_face_encoding, 52 | sianne_face_encoding, 53 | sianne_encoding, 54 | mom_encoding, 55 | # dad_encoding 56 | ] 57 | known_face_names = [ 58 | "Gavin", 59 | "Sianne", 60 | "Sianne", 61 | "Mom", 62 | # "Dad", 63 | ] 64 | 65 | # Get a reference to webcam #0 (the default one) 66 | video_capture = cv2.VideoCapture(0) 67 | 68 | # Initialize some variables 69 | face_locations = [] 70 | face_encodings = [] 71 | face_names = [] 72 | process_this_frame = True 73 | 74 | while True: 75 | # Grab a single frame of video 76 | ret, frame = video_capture.read() 77 | 78 | # Resize frame of video to 1/4 size for faster face recognition processing 79 | small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25) 80 | 81 | # Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses) 82 | rgb_small_frame = small_frame[:, :, ::-1] 83 | 84 | # Only process every other frame of video to save time 85 | if process_this_frame: 86 | # Find all the faces and face encodings in the current frame of video 87 | face_locations = face_recognition.face_locations(rgb_small_frame) 88 | face_encodings = face_recognition.face_encodings( 89 | rgb_small_frame, face_locations 90 | ) 91 | 92 | face_names = [] 93 | for face_encoding in face_encodings: 94 | # See if the face is a match for the known face(s) 95 | matches = face_recognition.compare_faces( 96 | known_face_encodings, face_encoding 97 | ) 98 | name = "Unknown" 99 | 100 | # # If a match was found in known_face_encodings, just use the first one. 101 | # if True in matches: 102 | # first_match_index = matches.index(True) 103 | # name = known_face_names[first_match_index] 104 | 105 | # Or instead, use the known face with the smallest distance to the new face 106 | face_distances = face_recognition.face_distance( 107 | known_face_encodings, face_encoding 108 | ) 109 | best_match_index = np.argmin(face_distances) 110 | if matches[best_match_index]: 111 | name = known_face_names[best_match_index] 112 | 113 | face_names.append(name) 114 | 115 | process_this_frame = not process_this_frame 116 | 117 | # Display the results 118 | for (top, right, bottom, left), name in zip(face_locations, face_names): 119 | # Scale back up face locations since the frame we detected in was scaled to 1/4 size 120 | top *= 4 121 | right *= 4 122 | bottom *= 4 123 | left *= 4 124 | 125 | # Draw a box around the face 126 | cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2) 127 | 128 | # Draw a label with a name below the face 129 | cv2.rectangle( 130 | frame, (left, bottom - 35), (right, bottom), (0, 0, 255), cv2.FILLED 131 | ) 132 | font = cv2.FONT_HERSHEY_DUPLEX 133 | cv2.putText(frame, name, (left + 6, bottom - 6), font, 1.0, (255, 255, 255), 1) 134 | 135 | # Display the resulting image 136 | cv2.imshow("Video", frame) 137 | 138 | # Hit 'q' on the keyboard to quit! 139 | if cv2.waitKey(1) & 0xFF == ord("q"): 140 | break 141 | 142 | # Release handle to the webcam 143 | video_capture.release() 144 | cv2.destroyAllWindows() 145 | -------------------------------------------------------------------------------- /photos/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavindsouza/photos/768bad51b91e072e4da0671c4c8838455b8b3ca4/photos/templates/__init__.py -------------------------------------------------------------------------------- /photos/templates/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavindsouza/photos/768bad51b91e072e4da0671c4c8838455b8b3ca4/photos/templates/pages/__init__.py -------------------------------------------------------------------------------- /photos/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020, Gavin D'souza and Contributors 2 | # See license.txt 3 | from collections.abc import Iterable 4 | from typing import TYPE_CHECKING 5 | 6 | import frappe 7 | 8 | if TYPE_CHECKING: 9 | from frappe.core.doctype.file.file import File 10 | 11 | from photos.photos.doctype.photo.photo import Photo 12 | 13 | 14 | def get_image_path(file_url: str): 15 | if file_url.startswith("/private"): 16 | file_url_path = (file_url.lstrip("/"),) 17 | else: 18 | file_url_path = ("public", file_url.lstrip("/")) 19 | return frappe.get_site_path(*file_url_path) 20 | 21 | 22 | def chunk(iterable: Iterable, chunk_size: int): 23 | """Creates list of elements split into groups of n.""" 24 | for i in range(0, len(iterable), chunk_size): 25 | yield iterable[i : i + chunk_size] 26 | 27 | 28 | def image_resize( 29 | image, width: int | None = None, height: int | None = None, inter: int | None = None 30 | ): 31 | import cv2 32 | 33 | if inter is None: 34 | inter = cv2.INTER_AREA 35 | # initialize the dimensions of the image to be resized and 36 | # grab the image size 37 | dim = None 38 | (h, w) = image.shape[:2] 39 | 40 | # if both the width and height are None, then return the 41 | # original image 42 | if width is None and height is None: 43 | return image 44 | 45 | # check to see if the width is None 46 | if width is None: 47 | # calculate the ratio of the height and construct the 48 | # dimensions 49 | r = height / float(h) 50 | dim = (int(w * r), height) 51 | 52 | # otherwise, the height is None 53 | else: 54 | # calculate the ratio of the width and construct the 55 | # dimensions 56 | r = width / float(w) 57 | dim = (width, int(h * r)) 58 | 59 | # resize the image 60 | resized = cv2.resize(image, dim, interpolation=inter) 61 | 62 | # return the resized image 63 | return resized 64 | 65 | 66 | def get_file_dashboard(*args, **kwargs): 67 | return { 68 | "fieldname": "photo", 69 | "transactions": [ 70 | {"label": "Photos", "items": ["Photo"], "fieldname": "photo"}, 71 | {"label": "People", "items": ["ROI"], "fieldname": "image"}, 72 | ], 73 | } 74 | 75 | 76 | def process_file(file: "File", event: str) -> "Photo": 77 | if event != "after_insert": 78 | raise NotImplementedError 79 | 80 | if file.is_folder or not file.content_type.startswith("image"): 81 | return 82 | 83 | photo = frappe.new_doc("Photo") 84 | photo.photo = file.name 85 | return photo.save() 86 | -------------------------------------------------------------------------------- /photos/www/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavindsouza/photos/768bad51b91e072e4da0671c4c8838455b8b3ca4/photos/www/__init__.py -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "hatchling>=1.6.0", 4 | ] 5 | build-backend = "hatchling.build" 6 | 7 | [project] 8 | name = "photos" 9 | description = "Open Source Alternative to Google Photos" 10 | readme = "README.md" 11 | license = "MIT" 12 | authors = [ 13 | { name = "Gavin Dsouza", email = "gavin18d@gmail.com" }, 14 | ] 15 | dependencies = [ 16 | # frappe (installed via bench) # MIT 17 | "face-recognition", # MIT 18 | "numpy", # BSD-3 19 | "opencv-python", # MIT 20 | ] 21 | dynamic = [ 22 | "version", 23 | ] 24 | 25 | [tool.hatch.version] 26 | path = "photos/__init__.py" 27 | 28 | [tool.hatch.build.targets.sdist] 29 | include = [ 30 | "/photos", 31 | ] 32 | 33 | [deploy.dependencies.apt] 34 | packages = [ 35 | "ffmpeg", 36 | "libsm6", 37 | "libxext6" 38 | ] 39 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | --------------------------------------------------------------------------------