diff --git a/.eslintrc b/.eslintrc index 0807290..9cf6225 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,23 +1,37 @@ { - "root": true, - "parser": "@typescript-eslint/parser", - "env": { "node": true }, - "plugins": [ - "@typescript-eslint" - ], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended" - ], - "parserOptions": { - "sourceType": "module" - }, - "rules": { - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }], - "@typescript-eslint/ban-ts-comment": "off", - "no-prototype-builtins": "off", - "@typescript-eslint/no-empty-function": "off" - } - } \ No newline at end of file + "root": true, + "parser": "@typescript-eslint/parser", + "env": { + "node": true + }, + "plugins": [ + "@typescript-eslint" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], + "parserOptions": { + "sourceType": "module" + }, + "rules": { + "no-unused-vars": 1, + "indent": [ + "error", + 4, + { + "SwitchCase": 1 + } + ], + "@typescript-eslint/no-unused-vars": [ + "error", + { + "args": "none" + } + ], + "@typescript-eslint/ban-ts-comment": "off", + "no-prototype-builtins": "off", + "@typescript-eslint/no-empty-function": "off" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..448189e --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1603 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@supabase/supabase-js': + specifier: ^2.33.1 + version: 2.38.5 + date-fns: + specifier: ^2.29.3 + version: 2.30.0 + tus-js-client: + specifier: ^3.1.1 + version: 3.1.1 + +devDependencies: + '@types/node': + specifier: ^16.11.6 + version: 16.18.64 + '@types/which': + specifier: ^2.0.1 + version: 2.0.2 + '@typescript-eslint/eslint-plugin': + specifier: 5.29.0 + version: 5.29.0(@typescript-eslint/parser@5.29.0)(eslint@8.54.0)(typescript@4.7.4) + '@typescript-eslint/parser': + specifier: 5.29.0 + version: 5.29.0(eslint@8.54.0)(typescript@4.7.4) + builtin-modules: + specifier: 3.3.0 + version: 3.3.0 + esbuild: + specifier: 0.14.47 + version: 0.14.47 + eslint: + specifier: ^8.48.0 + version: 8.54.0 + obsidian: + specifier: latest + version: 1.4.11(@codemirror/state@6.3.1)(@codemirror/view@6.22.0) + tslib: + specifier: 2.4.0 + version: 2.4.0 + typescript: + specifier: 4.7.4 + version: 4.7.4 + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@babel/runtime@7.23.4: + resolution: {integrity: sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.0 + dev: false + + /@codemirror/state@6.3.1: + resolution: {integrity: sha512-88e4HhMtKJyw6fKprGaN/yZfiaoGYOi2nM45YCUC6R/kex9sxFWBDGatS1vk4lMgnWmdIIB9tk8Gj1LmL8YfvA==} + dev: true + + /@codemirror/view@6.22.0: + resolution: {integrity: sha512-6zLj4YIoIpfTGKrDMTbeZRpa8ih4EymMCKmddEDcJWrCdp/N1D46B38YEz4creTb4T177AVS9EyXkLeC/HL2jA==} + dependencies: + '@codemirror/state': 6.3.1 + style-mod: 4.1.0 + w3c-keyname: 2.2.8 + dev: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.54.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.54.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.3: + resolution: {integrity: sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.23.0 + ignore: 5.3.0 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.54.0: + resolution: {integrity: sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@humanwhocodes/config-array@0.11.13: + resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 2.0.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@2.0.1: + resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@supabase/functions-js@2.1.5: + resolution: {integrity: sha512-BNzC5XhCzzCaggJ8s53DP+WeHHGT/NfTsx2wUSSGKR2/ikLFQTBCDzMvGz/PxYMqRko/LwncQtKXGOYp1PkPaw==} + dependencies: + '@supabase/node-fetch': 2.6.15 + dev: false + + /@supabase/gotrue-js@2.57.0: + resolution: {integrity: sha512-/CcAW40aPKgp9/w9WgXVUQFg1AOdvFR687ONOMjASPBuC6FsNbKlcXp4pc+rwKNtxyxDkBbR+x7zj/8g00r/Og==} + dependencies: + '@supabase/node-fetch': 2.6.15 + dev: false + + /@supabase/node-fetch@2.6.15: + resolution: {integrity: sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==} + engines: {node: 4.x || >=6.0.0} + dependencies: + whatwg-url: 5.0.0 + dev: false + + /@supabase/postgrest-js@1.9.0: + resolution: {integrity: sha512-axP6cU69jDrLbfihJKQ6vU27tklD0gzb9idkMN363MtTXeJVt5DQNT3JnJ58JVNBdL74hgm26rAsFNvHk+tnSw==} + dependencies: + '@supabase/node-fetch': 2.6.15 + dev: false + + /@supabase/realtime-js@2.8.4: + resolution: {integrity: sha512-5C9slLTGikHnYmAnIBOaPogAgbcNY68vnIyE6GpqIKjHElVb6LIi4clwNcjHSj4z6szuvvzj8T/+ePEgGEGekw==} + dependencies: + '@supabase/node-fetch': 2.6.15 + '@types/phoenix': 1.6.4 + '@types/websocket': 1.0.10 + websocket: 1.0.34 + transitivePeerDependencies: + - supports-color + dev: false + + /@supabase/storage-js@2.5.4: + resolution: {integrity: sha512-yspHD19I9uQUgfTh0J94+/r/g6hnhdQmw6Y7OWqr/EbnL6uvicGV1i1UDkkmeUHqfF9Mbt2sLtuxRycYyKv2ew==} + dependencies: + '@supabase/node-fetch': 2.6.15 + dev: false + + /@supabase/supabase-js@2.38.5: + resolution: {integrity: sha512-QTXld3AfwAJgeOGyOKsCcT7AjC3jJxN02iHy299Fw+qKX0lJ1tVVhMGlga101C1stUCvgzjcypmMSGiZ2oeKsw==} + dependencies: + '@supabase/functions-js': 2.1.5 + '@supabase/gotrue-js': 2.57.0 + '@supabase/node-fetch': 2.6.15 + '@supabase/postgrest-js': 1.9.0 + '@supabase/realtime-js': 2.8.4 + '@supabase/storage-js': 2.5.4 + transitivePeerDependencies: + - supports-color + dev: false + + /@types/codemirror@5.60.8: + resolution: {integrity: sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==} + dependencies: + '@types/tern': 0.23.9 + dev: true + + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: true + + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + dev: true + + /@types/node@16.18.64: + resolution: {integrity: sha512-TiY2gIDob8+QOPIcVpS0ZY+H1DVTfplBW6UgL2b4gOYbigIlKVIh6Lcv+7YDUciUTqhVLG91PrZBXW10IoBhtw==} + + /@types/phoenix@1.6.4: + resolution: {integrity: sha512-B34A7uot1Cv0XtaHRYDATltAdKx0BvVKNgYNqE4WjtPUa4VQJM7kxeXcVKaH+KS+kCmZ+6w+QaUdcljiheiBJA==} + dev: false + + /@types/tern@0.23.9: + resolution: {integrity: sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==} + dependencies: + '@types/estree': 1.0.5 + dev: true + + /@types/websocket@1.0.10: + resolution: {integrity: sha512-svjGZvPB7EzuYS94cI7a+qhwgGU1y89wUgjT6E2wVUfmAGIvRfT7obBvRtnhXCSsoMdlG4gBFGE7MfkIXZLoww==} + dependencies: + '@types/node': 16.18.64 + dev: false + + /@types/which@2.0.2: + resolution: {integrity: sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==} + dev: true + + /@typescript-eslint/eslint-plugin@5.29.0(@typescript-eslint/parser@5.29.0)(eslint@8.54.0)(typescript@4.7.4): + resolution: {integrity: sha512-kgTsISt9pM53yRFQmLZ4npj99yGl3x3Pl7z4eA66OuTzAGC4bQB5H5fuLwPnqTKU3yyrrg4MIhjF17UYnL4c0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/parser': 5.29.0(eslint@8.54.0)(typescript@4.7.4) + '@typescript-eslint/scope-manager': 5.29.0 + '@typescript-eslint/type-utils': 5.29.0(eslint@8.54.0)(typescript@4.7.4) + '@typescript-eslint/utils': 5.29.0(eslint@8.54.0)(typescript@4.7.4) + debug: 4.3.4 + eslint: 8.54.0 + functional-red-black-tree: 1.0.1 + ignore: 5.3.0 + regexpp: 3.2.0 + semver: 7.5.4 + tsutils: 3.21.0(typescript@4.7.4) + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@5.29.0(eslint@8.54.0)(typescript@4.7.4): + resolution: {integrity: sha512-ruKWTv+x0OOxbzIw9nW5oWlUopvP/IQDjB5ZqmTglLIoDTctLlAJpAQFpNPJP/ZI7hTT9sARBosEfaKbcFuECw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.29.0 + '@typescript-eslint/types': 5.29.0 + '@typescript-eslint/typescript-estree': 5.29.0(typescript@4.7.4) + debug: 4.3.4 + eslint: 8.54.0 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@5.29.0: + resolution: {integrity: sha512-etbXUT0FygFi2ihcxDZjz21LtC+Eps9V2xVx09zFoN44RRHPrkMflidGMI+2dUs821zR1tDS6Oc9IXxIjOUZwA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.29.0 + '@typescript-eslint/visitor-keys': 5.29.0 + dev: true + + /@typescript-eslint/type-utils@5.29.0(eslint@8.54.0)(typescript@4.7.4): + resolution: {integrity: sha512-JK6bAaaiJozbox3K220VRfCzLa9n0ib/J+FHIwnaV3Enw/TO267qe0pM1b1QrrEuy6xun374XEAsRlA86JJnyg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/utils': 5.29.0(eslint@8.54.0)(typescript@4.7.4) + debug: 4.3.4 + eslint: 8.54.0 + tsutils: 3.21.0(typescript@4.7.4) + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@5.29.0: + resolution: {integrity: sha512-X99VbqvAXOMdVyfFmksMy3u8p8yoRGITgU1joBJPzeYa0rhdf5ok9S56/itRoUSh99fiDoMtarSIJXo7H/SnOg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree@5.29.0(typescript@4.7.4): + resolution: {integrity: sha512-mQvSUJ/JjGBdvo+1LwC+GY2XmSYjK1nAaVw2emp/E61wEVYEyibRHCqm1I1vEKbXCpUKuW4G7u9ZCaZhJbLoNQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.29.0 + '@typescript-eslint/visitor-keys': 5.29.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + tsutils: 3.21.0(typescript@4.7.4) + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@5.29.0(eslint@8.54.0)(typescript@4.7.4): + resolution: {integrity: sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@types/json-schema': 7.0.15 + '@typescript-eslint/scope-manager': 5.29.0 + '@typescript-eslint/types': 5.29.0 + '@typescript-eslint/typescript-estree': 5.29.0(typescript@4.7.4) + eslint: 8.54.0 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0(eslint@8.54.0) + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@5.29.0: + resolution: {integrity: sha512-Hpb/mCWsjILvikMQoZIE3voc9wtQcS0A9FUw3h8bhr9UxBdtI/tw1ZDZUOXHXLOVMedKCH5NxyzATwnU78bWCQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.29.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: true + + /acorn-jsx@5.3.2(acorn@8.11.2): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.11.2 + dev: true + + /acorn@8.11.2: + resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: false + + /bufferutil@4.0.8: + resolution: {integrity: sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==} + engines: {node: '>=6.14.2'} + requiresBuild: true + dependencies: + node-gyp-build: 4.7.0 + dev: false + + /builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /combine-errors@3.0.3: + resolution: {integrity: sha512-C8ikRNRMygCwaTx+Ek3Yr+OuZzgZjduCOfSQBjbM8V3MfgcjSTeto/GXP6PAwKvJz/v15b7GHZvx5rOlczFw/Q==} + dependencies: + custom-error-instance: 2.1.1 + lodash.uniqby: 4.5.0 + dev: false + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /custom-error-instance@2.1.1: + resolution: {integrity: sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg==} + dev: false + + /d@1.0.1: + resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==} + dependencies: + es5-ext: 0.10.62 + type: 1.2.0 + dev: false + + /date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + dependencies: + '@babel/runtime': 7.23.4 + dev: false + + /debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: false + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /es5-ext@0.10.62: + resolution: {integrity: sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==} + engines: {node: '>=0.10'} + requiresBuild: true + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.3 + next-tick: 1.1.0 + dev: false + + /es6-iterator@2.0.3: + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} + dependencies: + d: 1.0.1 + es5-ext: 0.10.62 + es6-symbol: 3.1.3 + dev: false + + /es6-symbol@3.1.3: + resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==} + dependencies: + d: 1.0.1 + ext: 1.7.0 + dev: false + + /esbuild-android-64@0.14.47: + resolution: {integrity: sha512-R13Bd9+tqLVFndncMHssZrPWe6/0Kpv2/dt4aA69soX4PRxlzsVpCvoJeFE8sOEoeVEiBkI0myjlkDodXlHa0g==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /esbuild-android-arm64@0.14.47: + resolution: {integrity: sha512-OkwOjj7ts4lBp/TL6hdd8HftIzOy/pdtbrNA4+0oVWgGG64HrdVzAF5gxtJufAPOsEjkyh1oIYvKAUinKKQRSQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /esbuild-darwin-64@0.14.47: + resolution: {integrity: sha512-R6oaW0y5/u6Eccti/TS6c/2c1xYTb1izwK3gajJwi4vIfNs1s8B1dQzI1UiC9T61YovOQVuePDcfqHLT3mUZJA==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /esbuild-darwin-arm64@0.14.47: + resolution: {integrity: sha512-seCmearlQyvdvM/noz1L9+qblC5vcBrhUaOoLEDDoLInF/VQ9IkobGiLlyTPYP5dW1YD4LXhtBgOyevoIHGGnw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /esbuild-freebsd-64@0.14.47: + resolution: {integrity: sha512-ZH8K2Q8/Ux5kXXvQMDsJcxvkIwut69KVrYQhza/ptkW50DC089bCVrJZZ3sKzIoOx+YPTrmsZvqeZERjyYrlvQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-freebsd-arm64@0.14.47: + resolution: {integrity: sha512-ZJMQAJQsIOhn3XTm7MPQfCzEu5b9STNC+s90zMWe2afy9EwnHV7Ov7ohEMv2lyWlc2pjqLW8QJnz2r0KZmeAEQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-32@0.14.47: + resolution: {integrity: sha512-FxZOCKoEDPRYvq300lsWCTv1kcHgiiZfNrPtEhFAiqD7QZaXrad8LxyJ8fXGcWzIFzRiYZVtB3ttvITBvAFhKw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-64@0.14.47: + resolution: {integrity: sha512-nFNOk9vWVfvWYF9YNYksZptgQAdstnDCMtR6m42l5Wfugbzu11VpMCY9XrD4yFxvPo9zmzcoUL/88y0lfJZJJw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-arm64@0.14.47: + resolution: {integrity: sha512-ywfme6HVrhWcevzmsufjd4iT3PxTfCX9HOdxA7Hd+/ZM23Y9nXeb+vG6AyA6jgq/JovkcqRHcL9XwRNpWG6XRw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-arm@0.14.47: + resolution: {integrity: sha512-ZGE1Bqg/gPRXrBpgpvH81tQHpiaGxa8c9Rx/XOylkIl2ypLuOcawXEAo8ls+5DFCcRGt/o3sV+PzpAFZobOsmA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-mips64le@0.14.47: + resolution: {integrity: sha512-mg3D8YndZ1LvUiEdDYR3OsmeyAew4MA/dvaEJxvyygahWmpv1SlEEnhEZlhPokjsUMfRagzsEF/d/2XF+kTQGg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-ppc64le@0.14.47: + resolution: {integrity: sha512-WER+f3+szmnZiWoK6AsrTKGoJoErG2LlauSmk73LEZFQ/iWC+KhhDsOkn1xBUpzXWsxN9THmQFltLoaFEH8F8w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-riscv64@0.14.47: + resolution: {integrity: sha512-1fI6bP3A3rvI9BsaaXbMoaOjLE3lVkJtLxsgLHqlBhLlBVY7UqffWBvkrX/9zfPhhVMd9ZRFiaqXnB1T7BsL2g==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-s390x@0.14.47: + resolution: {integrity: sha512-eZrWzy0xFAhki1CWRGnhsHVz7IlSKX6yT2tj2Eg8lhAwlRE5E96Hsb0M1mPSE1dHGpt1QVwwVivXIAacF/G6mw==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-netbsd-64@0.14.47: + resolution: {integrity: sha512-Qjdjr+KQQVH5Q2Q1r6HBYswFTToPpss3gqCiSw2Fpq/ua8+eXSQyAMG+UvULPqXceOwpnPo4smyZyHdlkcPppQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-openbsd-64@0.14.47: + resolution: {integrity: sha512-QpgN8ofL7B9z8g5zZqJE+eFvD1LehRlxr25PBkjyyasakm4599iroUpaj96rdqRlO2ShuyqwJdr+oNqWwTUmQw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-sunos-64@0.14.47: + resolution: {integrity: sha512-uOeSgLUwukLioAJOiGYm3kNl+1wJjgJA8R671GYgcPgCx7QR73zfvYqXFFcIO93/nBdIbt5hd8RItqbbf3HtAQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-32@0.14.47: + resolution: {integrity: sha512-H0fWsLTp2WBfKLBgwYT4OTfFly4Im/8B5f3ojDv1Kx//kiubVY0IQunP2Koc/fr/0wI7hj3IiBDbSrmKlrNgLQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-64@0.14.47: + resolution: {integrity: sha512-/Pk5jIEH34T68r8PweKRi77W49KwanZ8X6lr3vDAtOlH5EumPE4pBHqkCUdELanvsT14yMXLQ/C/8XPi1pAtkQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-arm64@0.14.47: + resolution: {integrity: sha512-HFSW2lnp62fl86/qPQlqw6asIwCnEsEoNIL1h2uVMgakddf+vUuMcCbtUY1i8sst7KkgHrVKCJQB33YhhOweCQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild@0.14.47: + resolution: {integrity: sha512-wI4ZiIfFxpkuxB8ju4MHrGwGLyp1+awEHAHVpx6w7a+1pmYIq8T9FGEVVwFo0iFierDoMj++Xq69GXWYn2EiwA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + esbuild-android-64: 0.14.47 + esbuild-android-arm64: 0.14.47 + esbuild-darwin-64: 0.14.47 + esbuild-darwin-arm64: 0.14.47 + esbuild-freebsd-64: 0.14.47 + esbuild-freebsd-arm64: 0.14.47 + esbuild-linux-32: 0.14.47 + esbuild-linux-64: 0.14.47 + esbuild-linux-arm: 0.14.47 + esbuild-linux-arm64: 0.14.47 + esbuild-linux-mips64le: 0.14.47 + esbuild-linux-ppc64le: 0.14.47 + esbuild-linux-riscv64: 0.14.47 + esbuild-linux-s390x: 0.14.47 + esbuild-netbsd-64: 0.14.47 + esbuild-openbsd-64: 0.14.47 + esbuild-sunos-64: 0.14.47 + esbuild-windows-32: 0.14.47 + esbuild-windows-64: 0.14.47 + esbuild-windows-arm64: 0.14.47 + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-utils@3.0.0(eslint@8.54.0): + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 8.54.0 + eslint-visitor-keys: 2.1.0 + dev: true + + /eslint-visitor-keys@2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.54.0: + resolution: {integrity: sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.54.0) + '@eslint-community/regexpp': 4.10.0 + '@eslint/eslintrc': 2.1.3 + '@eslint/js': 8.54.0 + '@humanwhocodes/config-array': 0.11.13 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.23.0 + graphemer: 1.4.0 + ignore: 5.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.11.2 + acorn-jsx: 5.3.2(acorn@8.11.2) + eslint-visitor-keys: 3.4.3 + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + dependencies: + type: 2.7.2 + dev: false + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.2.0 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.9 + keyv: 4.5.4 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.9: + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /functional-red-black-tree@1.0.1: + resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals@13.23.0: + resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.0 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: false + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /ignore@5.3.0: + resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: false + + /is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + dev: false + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /js-base64@3.7.5: + resolution: {integrity: sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==} + dev: false + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash._baseiteratee@4.7.0: + resolution: {integrity: sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ==} + dependencies: + lodash._stringtopath: 4.8.0 + dev: false + + /lodash._basetostring@4.12.0: + resolution: {integrity: sha512-SwcRIbyxnN6CFEEK4K1y+zuApvWdpQdBHM/swxP962s8HIxPO3alBH5t3m/dl+f4CMUug6sJb7Pww8d13/9WSw==} + dev: false + + /lodash._baseuniq@4.6.0: + resolution: {integrity: sha512-Ja1YevpHZctlI5beLA7oc5KNDhGcPixFhcqSiORHNsp/1QTv7amAXzw+gu4YOvErqVlMVyIJGgtzeepCnnur0A==} + dependencies: + lodash._createset: 4.0.3 + lodash._root: 3.0.1 + dev: false + + /lodash._createset@4.0.3: + resolution: {integrity: sha512-GTkC6YMprrJZCYU3zcqZj+jkXkrXzq3IPBcF/fIPpNEAB4hZEtXU8zp/RwKOvZl43NUmwDbyRk3+ZTbeRdEBXA==} + dev: false + + /lodash._root@3.0.1: + resolution: {integrity: sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ==} + dev: false + + /lodash._stringtopath@4.8.0: + resolution: {integrity: sha512-SXL66C731p0xPDC5LZg4wI5H+dJo/EO4KTqOMwLYCH3+FmmfAKJEZCm6ohGpI+T1xwsDsJCfL4OnhorllvlTPQ==} + dependencies: + lodash._basetostring: 4.12.0 + dev: false + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash.throttle@4.1.1: + resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} + dev: false + + /lodash.uniqby@4.5.0: + resolution: {integrity: sha512-IRt7cfTtHy6f1aRVA5n7kT8rgN3N1nH6MOWLcHfpWG2SH19E3JksLK38MktLxZDhlAjCP9jpIXkOnRXlu6oByQ==} + dependencies: + lodash._baseiteratee: 4.7.0 + lodash._baseuniq: 4.6.0 + dev: false + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /moment@2.29.4: + resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} + dev: true + + /ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: false + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /next-tick@1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + dev: false + + /node-gyp-build@4.7.0: + resolution: {integrity: sha512-PbZERfeFdrHQOOXiAKOY0VPbykZy90ndPKk0d+CFDegTKmWp1VgOTz2xACVbr1BjCWxrQp68CXtvNsveFhqDJg==} + hasBin: true + dev: false + + /obsidian@1.4.11(@codemirror/state@6.3.1)(@codemirror/view@6.22.0): + resolution: {integrity: sha512-BCVYTvaXxElJMl6MMbDdY/CGK+aq18SdtDY/7vH8v6BxCBQ6KF4kKxL0vG9UZ0o5qh139KpUoJHNm+6O5dllKA==} + peerDependencies: + '@codemirror/state': ^6.0.0 + '@codemirror/view': ^6.0.0 + dependencies: + '@codemirror/state': 6.3.1 + '@codemirror/view': 6.22.0 + '@types/codemirror': 5.60.8 + moment: 2.29.4 + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /proper-lockfile@4.1.2: + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + dependencies: + graceful-fs: 4.2.11 + retry: 0.12.0 + signal-exit: 3.0.7 + dev: false + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: true + + /querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + dev: false + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /regenerator-runtime@0.14.0: + resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} + dev: false + + /regexpp@3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + dev: true + + /requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + dev: false + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + dev: false + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: false + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /style-mod@4.1.0: + resolution: {integrity: sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==} + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: false + + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib@2.4.0: + resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + dev: true + + /tsutils@3.21.0(typescript@4.7.4): + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 4.7.4 + dev: true + + /tus-js-client@3.1.1: + resolution: {integrity: sha512-SZzWP62jEFLmROSRZx+uoGLKqsYWMGK/m+PiNehPVWbCm7/S9zRIMaDxiaOcKdMnFno4luaqP5E+Y1iXXPjP0A==} + dependencies: + buffer-from: 1.1.2 + combine-errors: 3.0.3 + is-stream: 2.0.1 + js-base64: 3.7.5 + lodash.throttle: 4.1.1 + proper-lockfile: 4.1.2 + url-parse: 1.5.10 + dev: false + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type@1.2.0: + resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==} + dev: false + + /type@2.7.2: + resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} + dev: false + + /typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + dependencies: + is-typedarray: 1.0.0 + dev: false + + /typescript@4.7.4: + resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: true + + /url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + dev: false + + /utf-8-validate@5.0.10: + resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} + engines: {node: '>=6.14.2'} + requiresBuild: true + dependencies: + node-gyp-build: 4.7.0 + dev: false + + /w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + dev: true + + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: false + + /websocket@1.0.34: + resolution: {integrity: sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==} + engines: {node: '>=4.0.0'} + dependencies: + bufferutil: 4.0.8 + debug: 2.6.9 + es5-ext: 0.10.62 + typedarray-to-buffer: 3.1.5 + utf-8-validate: 5.0.10 + yaeti: 0.0.6 + transitivePeerDependencies: + - supports-color + dev: false + + /whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: false + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /yaeti@0.0.6: + resolution: {integrity: sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==} + engines: {node: '>=0.10.32'} + dev: false + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/src/main.ts b/src/main.ts index 09debac..387052b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,417 +1,414 @@ import { ChildProcess } from "child_process"; import { - Editor, - MarkdownView, - Plugin, - TFile, - Notice, - Platform, - FuzzySuggestModal, + Editor, + MarkdownView, + Plugin, + TFile, + Notice, + Platform, + FuzzySuggestModal, } from "obsidian"; import { TranscriptionEngine } from "./transcribe"; import { StatusBar } from "./status"; import { createClient, User } from "@supabase/supabase-js"; import { - TranscriptionSettings, - DEFAULT_SETTINGS, - TranscriptionSettingTab, + TranscriptionSettings, + DEFAULT_SETTINGS, + TranscriptionSettingTab, } from "./settings"; export default class Transcription extends Plugin { - settings: TranscriptionSettings; - public static plugin: Plugin; - public static children: Array = []; - private static transcribeFileExtensions: string[] = [ - "mp3", - "wav", - "webm", - "ogg", - "flac", - "m4a", - "aac", - "amr", - "opus", - "aiff", - "m3gp", - "mp4", - "m4v", - "mov", - "avi", - "wmv", - "flv", - "mpeg", - "mpg", - "mkv", - ]; - public transcription_engine: TranscriptionEngine; - statusBar: StatusBar; - public supabase = createClient( - "https://auth.swiftink.io", - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZjZGVxZ3JzcWFleHBub2dhdWx5Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2ODU2OTM4NDUsImV4cCI6MjAwMTI2OTg0NX0.BBxpvuejw_E-Q_g6SU6G6sGP_6r4KnrP-vHV2JZpAho", - { - auth: { - detectSessionInUrl: false, - autoRefreshToken: true, - persistSession: true, - }, - }, - ); - public user: User | null; - - async onload() { - await this.loadSettings(); - - Transcription.plugin = this; - console.log("Loading Obsidian Transcription"); - if (this.settings.debug) console.log("Debug mode enabled"); - - this.transcription_engine = new TranscriptionEngine( - this.settings, - this.app.vault, - this.statusBar, - this.supabase, - ); - - // Prompt the user to sign in if the have Swiftink selected and are not signed in - if (this.settings.transcription_engine == "swiftink") { - this.user = await this.supabase.auth.getUser().then((res) => { - return res.data.user || null; - }); - if (this.user == null) { - // First try setting the access token and refresh token from the settings - if (this.settings.debug) - console.log( - "Trying to set access token and refresh token from settings", - ); - if ( - this.settings.swiftink_access_token != null && + settings: TranscriptionSettings; + public static plugin: Plugin; + public static children: Array = []; + private static transcribeFileExtensions: string[] = [ + "mp3", + "wav", + "webm", + "ogg", + "flac", + "m4a", + "aac", + "amr", + "opus", + "aiff", + "m3gp", + "mp4", + "m4v", + "mov", + "avi", + "wmv", + "flv", + "mpeg", + "mpg", + "mkv", + ]; + public transcription_engine: TranscriptionEngine; + statusBar: StatusBar; + public supabase = createClient( + "https://auth.swiftink.io", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZjZGVxZ3JzcWFleHBub2dhdWx5Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2ODU2OTM4NDUsImV4cCI6MjAwMTI2OTg0NX0.BBxpvuejw_E-Q_g6SU6G6sGP_6r4KnrP-vHV2JZpAho", + { + auth: { + detectSessionInUrl: false, + autoRefreshToken: true, + persistSession: true, + }, + }, + ); + public user: User | null; + + async onload() { + await this.loadSettings(); + + Transcription.plugin = this; + console.log("Loading Obsidian Transcription"); + if (this.settings.debug) console.log("Debug mode enabled"); + + this.transcription_engine = new TranscriptionEngine( + this.settings, + this.app.vault, + this.statusBar, + this.supabase, + ); + + // Prompt the user to sign in if the have Swiftink selected and are not signed in + if (this.settings.transcription_engine == "swiftink") { + this.user = await this.supabase.auth.getUser().then((res) => { + return res.data.user || null; + }); + if (this.user == null) { + // First try setting the access token and refresh token from the settings + if (this.settings.debug) + console.log( + "Trying to set access token and refresh token from settings", + ); + if ( + this.settings.swiftink_access_token != null && this.settings.swiftink_refresh_token != null - ) { - await this.supabase.auth.setSession({ - access_token: this.settings.swiftink_access_token, - refresh_token: this.settings.swiftink_refresh_token, - }); - this.user = await this.supabase.auth - .getUser() - .then((res) => { - return res.data.user || null; - }); - } - - // If the user is still null, prompt them to sign in - if (this.user == null) - new Notice( - "Transcription: Please sign in to Swiftink.io via the settings tab", - 4000, - ); - } - } - - if (!Platform.isMobileApp) { - this.statusBar = new StatusBar(this.addStatusBarItem()); - this.registerInterval( - window.setInterval(() => this.statusBar.display(), 1000), - ); - } - - const getTranscribeableFiles = (file: TFile) => { - // Get all linked files in the markdown file - const filesLinked = Object.keys( - this.app.metadataCache.resolvedLinks[file.path], - ); - - // Now that we have all the files linked in the markdown file, we need to filter them by the file extensions we want to transcribe - const filesToTranscribe: TFile[] = []; - for (const linkedFilePath of filesLinked) { - const linkedFileExtension = linkedFilePath.split(".").pop(); - if ( - linkedFileExtension === undefined || - !Transcription.transcribeFileExtensions.includes( - linkedFileExtension.toLowerCase(), + ) { + await this.supabase.auth.setSession({ + access_token: this.settings.swiftink_access_token, + refresh_token: this.settings.swiftink_refresh_token, + }); + this.user = await this.supabase.auth + .getUser() + .then((res) => { + return res.data.user || null; + }); + } + + // If the user is still null, prompt them to sign in + if (this.user == null) + new Notice( + "Transcription: Please sign in to Swiftink.io via the settings tab", + 4000, + ); + } + } + + if (!Platform.isMobileApp) { + this.statusBar = new StatusBar(this.addStatusBarItem()); + this.registerInterval( + window.setInterval(() => this.statusBar.display(), 1000), + ); + } + + const getTranscribeableFiles = (file: TFile) => { + // Get all linked files in the markdown file + const filesLinked = Object.keys( + this.app.metadataCache.resolvedLinks[file.path], + ); + + // Now that we have all the files linked in the markdown file, we need to filter them by the file extensions we want to transcribe + const filesToTranscribe: TFile[] = []; + for (const linkedFilePath of filesLinked) { + const linkedFileExtension = linkedFilePath.split(".").pop(); + if ( + linkedFileExtension === undefined || + !Transcription.transcribeFileExtensions.includes(linkedFileExtension.toLowerCase(), ) - ) { - if (this.settings.debug) - console.log( - "Skipping " + - linkedFilePath + - " because the file extension is not in the list of transcribeable file extensions", - ); - continue; - } - - // We now know that the file extension is in the list of transcribeable file extensions - const linkedFile = + ) { + if (this.settings.debug) + console.log( + "Skipping " + + linkedFilePath + + " because the file extension is not in the list of transcribeable file extensions", + ); + continue; + } + + // We now know that the file extension is in the list of transcribeable file extensions + const linkedFile = this.app.vault.getAbstractFileByPath(linkedFilePath); - // Validate that we are dealing with a file and add it to the list of verified files to transcribe - if (linkedFile instanceof TFile) - filesToTranscribe.push(linkedFile); - else { - if (this.settings.debug) - console.log("Could not find file " + linkedFilePath); - continue; - } - } - return filesToTranscribe; - }; - - const transcribeAndWrite = async (parent_file: TFile, file: TFile) => { - if (this.settings.debug) console.log("Transcribing " + file.path); - - this.transcription_engine - .getTranscription(file) - .then(async (transcription) => { - let fileText = await this.app.vault.read(parent_file); - const fileLinkString = - this.app.metadataCache.fileToLinktext( - file, - parent_file.path, + // Validate that we are dealing with a file and add it to the list of verified files to transcribe + if (linkedFile instanceof TFile) + filesToTranscribe.push(linkedFile); + else { + if (this.settings.debug) + console.log("Could not find file " + linkedFilePath); + continue; + } + } + return filesToTranscribe; + }; + + const transcribeAndWrite = async (parent_file: TFile, file: TFile) => { + if (this.settings.debug) console.log("Transcribing " + file.path); + + this.transcription_engine + .getTranscription(file) + .then(async (transcription) => { + let fileText = await this.app.vault.read(parent_file); + const fileLinkString = + this.app.metadataCache.fileToLinktext(file, parent_file.path, ); // This is the string that is used to link the audio file in the markdown file. If files are moved this potentially breaks, but Obsidian has built-in handlers for this, and handling that is outside the scope of this plugin - const fileLinkStringTagged = `[[${fileLinkString}]]`; // This is the string that is used to link the audio file in the markdown file. + const fileLinkStringTagged = `[[${fileLinkString}]]`; // This is the string that is used to link the audio file in the markdown file. - // Perform a string replacement, add the transcription to the next line after the file link - const startReplacementIndex = + // Perform a string replacement, add the transcription to the next line after the file link + const startReplacementIndex = fileText.indexOf(fileLinkStringTagged) + fileLinkStringTagged.length; - fileText = [ - fileText.slice(0, startReplacementIndex), - `\n${transcription}`, - fileText.slice(startReplacementIndex), - ].join(""); - - // Now that we have the file lines with the transcription, we can write the file - await this.app.vault.modify(parent_file, fileText); - }) - .catch((error) => { - // First check if 402 is in the error message, if so alert the user that they need to pay - if ( - error && + fileText = [ + fileText.slice(0, startReplacementIndex), + `\n${transcription}`, + fileText.slice(startReplacementIndex), + ].join(""); + + // Now that we have the file lines with the transcription, we can write the file + await this.app.vault.modify(parent_file, fileText); + }) + .catch((error) => { + // First check if 402 is in the error message, if so alert the user that they need to pay + if ( + error && error.message && error.message.includes("402") - ) { - new Notice( - "You have exceeded the free tier. Please upgrade to a paid plan at swiftink.io/pricing to continue transcribing files. Thanks for using Swiftink!", - 10000, - ); - } else { - if (this.settings.debug) console.log(error); - new Notice(`Error transcribing file: ${error}`); - } - }); - }; - - this.addCommand({ - id: "obsidian-transcription-transcribe-all-in-view", - name: "Transcribe all files in view", - editorCallback: async (editor: Editor, view: MarkdownView) => { - if (view.file === null) return; - const filesToTranscribe = getTranscribeableFiles(view.file); - new Notice( - "Transcribing all audio files in " + view.file.name, - 3000, - ); - - // Now that we have all the files to transcribe, we can transcribe them - for (const fileToTranscribe of filesToTranscribe) { - transcribeAndWrite(view.file, fileToTranscribe); - } - }, - }); - - this.addCommand({ - id: "obsidian-transcription-transcribe-specific-file-in-view", - name: "Transcribe file in view", - editorCallback: async (editor: Editor, view: MarkdownView) => { - // Get the current filepath - if (view.file === null) return; - const filesToTranscribe = getTranscribeableFiles(view.file); - - // Now that we have all the files to transcribe, we can prompt the user to choose which one they want to transcribe - - class FileSelectionModal extends FuzzySuggestModal { - getItems(): TFile[] { - return filesToTranscribe; - } - - getItemText(file: TFile): string { - return file.name; - } - - onChooseItem(file: TFile) { - if (view.file === null) return; - new Notice(`Transcribing ${file.name}`); - transcribeAndWrite(view.file, file); - } - } - - new FileSelectionModal(this.app).open(); - }, - }); - - // Register a command to transcribe a media file when right-clicking on it - // this.registerEvent( - // // if (!Transcription.transcribeFileExtensions.includes(view.file.extension.toLowerCase())) return; - // this.app.workspace.on("file-menu", (menu: Menu, file) => { - // if (file instanceof TFolder) return; - // // if (file.parent instanceof TFolder) return; - // if (!(file instanceof TFile)) return; - // console.log(file) - // menu.addItem((item) => { - // item - // .setTitle("Transcribe File 🖊️") - // .setIcon("document") - // .onClick(async () => { - // if (!Transcription.transcribeFileExtensions.includes(file.extension.toLowerCase())) return; - // // transcribeAndWrite(file.parent, file) - // new Notice(file.path); - // }); - // }); - // }) - // ); - - // Kill child processes when the plugin is unloaded - this.app.workspace.on("quit", () => { - Transcription.children.forEach((child) => { - child.kill(); - }); - }); - - // This adds a settings tab so the user can configure various aspects of the plugin - this.addSettingTab(new TranscriptionSettingTab(this.app, this)); - - this.registerObsidianProtocolHandler( - "swiftink_auth", - async (callback) => { - const params = new URLSearchParams(callback.hash); - const access_token = params.get("access_token"); - const refresh_token = params.get("refresh_token"); - - if (!access_token || !refresh_token) { - new Notice( - "Transcription: Error authenticating with Swiftink.io", - ); - return; - } - - await this.supabase.auth.setSession({ - access_token: access_token, - refresh_token: refresh_token, - }); - this.user = await this.supabase.auth.getUser().then((res) => { - return res.data.user || null; - }); - new Notice("Successfully authenticated with Swiftink.io"); - - // Save to settings - this.settings.swiftink_access_token = access_token; - this.settings.swiftink_refresh_token = refresh_token; - await this.saveSettings(); - - // Show the settings for user auth/unauth based on whether the user is signed in - if (this.user == null) { - document - .querySelectorAll(".swiftink-unauthed-only") - .forEach((element) => { - element.setAttribute( - "style", - "display: block !important", - ); - }); - document - .querySelectorAll(".swiftink-authed-only") - .forEach((element) => { - element.setAttribute( - "style", - "display: none !important", - ); - }); - } else { - document - .querySelectorAll(".swiftink-unauthed-only") - .forEach((element) => { - element.setAttribute( - "style", - "display: none !important", - ); - }); - document - .querySelectorAll(".swiftink-authed-only") - .forEach((element) => { - element.setAttribute( - "style", - "display: block !important", - ); - }); - // Also set the user's email in the settings tab - document - .querySelectorAll(".swiftink-manage-account-btn") - .forEach((element) => { - element.innerHTML = `Manage ${this.user?.email}`; - }); - } - return; - }, - ); - - this.registerObsidianProtocolHandler( - "swiftink_transcript_functions", - async (callback) => { - const id = callback.id; - console.log(id); - - const functions = [ - "View on Swiftink.io", - // "Delete from Swiftink.io", - // "Download .txt", - // "Download .srt", - // "Copy text to clipboard", - // "Copy summary to clipboard", - // "Copy outline to clipboard", - // "Copy keywords to clipboard", - ]; - - class SwiftinkTranscriptFunctionsModal extends FuzzySuggestModal { - getItems(): string[] { - return functions; - } - - getItemText(function_name: string): string { - return function_name; - } - - onChooseItem(function_name: string) { - // new Notice(`Running ${function_name} on ${id}`); - if (function_name == "View on Swiftink.io") { - window.open( - "https://swiftink.io/dashboard/", - "_blank", - ); - } - } - } - - new SwiftinkTranscriptFunctionsModal(this.app).open(); - }, - ); - } - - onunload() { - if (this.settings.debug) - console.log("Unloading Obsidian Transcription"); - } - - async loadSettings() { - this.settings = Object.assign( - {}, - DEFAULT_SETTINGS, - await this.loadData(), - ); - } - - async saveSettings() { - await this.saveData(this.settings); - } + ) { + new Notice( + "You have exceeded the free tier. Please upgrade to a paid plan at swiftink.io/pricing to continue transcribing files. Thanks for using Swiftink!", + 10000, + ); + } else { + if (this.settings.debug) console.log(error); + new Notice(`Error transcribing file: ${error}`); + } + }); + }; + + this.addCommand({ + id: "obsidian-transcription-transcribe-all-in-view", + name: "Transcribe all files in view", + editorCallback: async (editor: Editor, view: MarkdownView) => { + if (view.file === null) return; + const filesToTranscribe = getTranscribeableFiles(view.file); + new Notice( + "Files Selected " + view.file.name, + 3000, + ); + + // Now that we have all the files to transcribe, we can transcribe them + for (const fileToTranscribe of filesToTranscribe) { + transcribeAndWrite(view.file, fileToTranscribe); + } + }, + }); + + this.addCommand({ + id: "obsidian-transcription-transcribe-specific-file-in-view", + name: "Transcribe file in view", + editorCallback: async (editor: Editor, view: MarkdownView) => { + // Get the current filepath + if (view.file === null) return; + const filesToTranscribe = getTranscribeableFiles(view.file); + + // Now that we have all the files to transcribe, we can prompt the user to choose which one they want to transcribe + + class FileSelectionModal extends FuzzySuggestModal { + getItems(): TFile[] { + return filesToTranscribe; + } + + getItemText(file: TFile): string { + return file.name; + } + + onChooseItem(file: TFile) { + if (view.file === null) return; + new Notice(`File Selected: ${file.name}`); + transcribeAndWrite(view.file, file); + } + } + + new FileSelectionModal(this.app).open(); + }, + }); + + // Register a command to transcribe a media file when right-clicking on it + // this.registerEvent( + // // if (!Transcription.transcribeFileExtensions.includes(view.file.extension.toLowerCase())) return; + // this.app.workspace.on("file-menu", (menu: Menu, file) => { + // if (file instanceof TFolder) return; + // // if (file.parent instanceof TFolder) return; + // if (!(file instanceof TFile)) return; + // console.log(file) + // menu.addItem((item) => { + // item + // .setTitle("Transcribe File 🖊️") + // .setIcon("document") + // .onClick(async () => { + // if (!Transcription.transcribeFileExtensions.includes(file.extension.toLowerCase())) return; + // // transcribeAndWrite(file.parent, file) + // new Notice(file.path); + // }); + // }); + // }) + // ); + + // Kill child processes when the plugin is unloaded + this.app.workspace.on("quit", () => { + Transcription.children.forEach((child) => { + child.kill(); + }); + }); + + // This adds a settings tab so the user can configure various aspects of the plugin + this.addSettingTab(new TranscriptionSettingTab(this.app, this)); + + this.registerObsidianProtocolHandler( + "swiftink_auth", + async (callback) => { + const params = new URLSearchParams(callback.hash); + const access_token = params.get("access_token"); + const refresh_token = params.get("refresh_token"); + + if (!access_token || !refresh_token) { + new Notice( + "Transcription: Error authenticating with Swiftink.io", + ); + return; + } + + await this.supabase.auth.setSession({ + access_token: access_token, + refresh_token: refresh_token, + }); + this.user = await this.supabase.auth.getUser().then((res) => { + return res.data.user || null; + }); + new Notice("Successfully authenticated with Swiftink.io"); + + // Save to settings + this.settings.swiftink_access_token = access_token; + this.settings.swiftink_refresh_token = refresh_token; + await this.saveSettings(); + + // Show the settings for user auth/unauth based on whether the user is signed in + if (this.user == null) { + document + .querySelectorAll(".swiftink-unauthed-only") + .forEach((element) => { + element.setAttribute( + "style", + "display: block !important", + ); + }); + document + .querySelectorAll(".swiftink-authed-only") + .forEach((element) => { + element.setAttribute( + "style", + "display: none !important", + ); + }); + } else { + document + .querySelectorAll(".swiftink-unauthed-only") + .forEach((element) => { + element.setAttribute( + "style", + "display: none !important", + ); + }); + document + .querySelectorAll(".swiftink-authed-only") + .forEach((element) => { + element.setAttribute( + "style", + "display: block !important", + ); + }); + // Also set the user's email in the settings tab + document + .querySelectorAll(".swiftink-manage-account-btn") + .forEach((element) => { + element.innerHTML = `Manage ${this.user?.email}`; + }); + } + return; + }, + ); + + this.registerObsidianProtocolHandler( + "swiftink_transcript_functions", + async (callback) => { + const id = callback.id; + console.log(id); + + const functions = [ + "View on Swiftink.io", + // "Delete from Swiftink.io", + // "Download .txt", + // "Download .srt", + // "Copy text to clipboard", + // "Copy summary to clipboard", + // "Copy outline to clipboard", + // "Copy keywords to clipboard", + ]; + + class SwiftinkTranscriptFunctionsModal extends FuzzySuggestModal { + getItems(): string[] { + return functions; + } + + getItemText(function_name: string): string { + return function_name; + } + + onChooseItem(function_name: string) { + // new Notice(`Running ${function_name} on ${id}`); + if (function_name == "View on Swiftink.io") { + window.open( + "https://swiftink.io/dashboard/", + "_blank", + ); + } + } + } + + new SwiftinkTranscriptFunctionsModal(this.app).open(); + }, + ); + } + + onunload() { + if (this.settings.debug) + console.log("Unloading Obsidian Transcription"); + } + + async loadSettings() { + this.settings = Object.assign( + {}, + DEFAULT_SETTINGS, + await this.loadData(), + ); + } + + async saveSettings() { + await this.saveData(this.settings); + } } export { Transcription }; diff --git a/src/settings.ts b/src/settings.ts index f1afd44..423b6b6 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -22,487 +22,487 @@ const SWIFTINK_AUTH_CALLBACK = "https://swiftink.io/login/?callback=obsidian://swiftink_auth"; const DEFAULT_SETTINGS: TranscriptionSettings = { - timestamps: false, - timestampFormat: "HH:mm:ss", - translate: false, - language: "auto", - verbosity: 1, - whisperASRUrl: "http://localhost:9000", - debug: false, - transcription_engine: "swiftink", - embedAdditionalFunctionality: true, - embedSummary: true, - embedOutline: true, - embedKeywords: true, - swiftink_access_token: null, - swiftink_refresh_token: null, + timestamps: false, + timestampFormat: "HH:mm:ss", + translate: false, + language: "auto", + verbosity: 1, + whisperASRUrl: "http://localhost:9000", + debug: false, + transcription_engine: "swiftink", + embedAdditionalFunctionality: true, + embedSummary: true, + embedOutline: true, + embedKeywords: true, + swiftink_access_token: null, + swiftink_refresh_token: null, }; const LANGUAGES = { - AFRIKAANS: "af", - ALBANIAN: "sq", - AMHARIC: "am", - ARABIC: "ar", - ARMENIAN: "hy", - ASSAMESE: "as", - AZERBAIJANI: "az", - BASHKIR: "ba", - BASQUE: "eu", - BELARUSIAN: "be", - BENGALI: "bn", - BOSNIAN: "bs", - BRETON: "br", - BULGARIAN: "bg", - BURMESE: "my", - CATALAN: "ca", - CHINESE: "zh", - CROATIAN: "hr", - CZECH: "cs", - DANISH: "da", - DUTCH: "nl", - ENGLISH: "en", - ESTONIAN: "et", - FAROESE: "fo", - FINNISH: "fi", - FRENCH: "fr", - GALICIAN: "gl", - GEORGIAN: "ka", - GERMAN: "de", - GREEK: "el", - GUJARATI: "gu", - HAITIAN: "ht", - HAUSA: "ha", - HEBREW: "he", - HINDI: "hi", - HUNGARIAN: "hu", - ICELANDIC: "is", - INDONESIAN: "id", - ITALIAN: "it", - JAPANESE: "ja", - JAVANESE: "jv", - KANNADA: "kn", - KAZAKH: "kk", - KOREAN: "ko", - LAO: "lo", - LATIN: "la", - LATVIAN: "lv", - LINGALA: "ln", - LITHUANIAN: "lt", - LUXEMBOURGISH: "lb", - MACEDONIAN: "mk", - MALAGASY: "mg", - MALAY: "ms", - MALAYALAM: "ml", - MALTESE: "mt", - MAORI: "mi", - MARATHI: "mr", - MONGOLIAN: "mn", - NEPALI: "ne", - NORWEGIAN: "nb", - OCCITAN: "oc", - PANJABI: "pa", - PERSIAN: "fa", - POLISH: "pl", - PORTUGUESE: "pt", - PUSHTO: "ps", - ROMANIAN: "ro", - RUSSIAN: "ru", - SANSKRIT: "sa", - SERBIAN: "sr", - SHONA: "sn", - SINDHI: "sd", - SINHALA: "si", - SLOVAK: "sk", - SLOVENIAN: "sl", - SOMALI: "so", - SPANISH: "es", - SUNDANESE: "su", - SWAHILI: "sw", - SWEDISH: "sv", - TAGALOG: "tl", - TAJIK: "tg", - TAMIL: "ta", - TATAR: "tt", - TELUGU: "te", - THAI: "th", + AFRIKAANS: "af", + ALBANIAN: "sq", + AMHARIC: "am", + ARABIC: "ar", + ARMENIAN: "hy", + ASSAMESE: "as", + AZERBAIJANI: "az", + BASHKIR: "ba", + BASQUE: "eu", + BELARUSIAN: "be", + BENGALI: "bn", + BOSNIAN: "bs", + BRETON: "br", + BULGARIAN: "bg", + BURMESE: "my", + CATALAN: "ca", + CHINESE: "zh", + CROATIAN: "hr", + CZECH: "cs", + DANISH: "da", + DUTCH: "nl", + ENGLISH: "en", + ESTONIAN: "et", + FAROESE: "fo", + FINNISH: "fi", + FRENCH: "fr", + GALICIAN: "gl", + GEORGIAN: "ka", + GERMAN: "de", + GREEK: "el", + GUJARATI: "gu", + HAITIAN: "ht", + HAUSA: "ha", + HEBREW: "he", + HINDI: "hi", + HUNGARIAN: "hu", + ICELANDIC: "is", + INDONESIAN: "id", + ITALIAN: "it", + JAPANESE: "ja", + JAVANESE: "jv", + KANNADA: "kn", + KAZAKH: "kk", + KOREAN: "ko", + LAO: "lo", + LATIN: "la", + LATVIAN: "lv", + LINGALA: "ln", + LITHUANIAN: "lt", + LUXEMBOURGISH: "lb", + MACEDONIAN: "mk", + MALAGASY: "mg", + MALAY: "ms", + MALAYALAM: "ml", + MALTESE: "mt", + MAORI: "mi", + MARATHI: "mr", + MONGOLIAN: "mn", + NEPALI: "ne", + NORWEGIAN: "nb", + OCCITAN: "oc", + PANJABI: "pa", + PERSIAN: "fa", + POLISH: "pl", + PORTUGUESE: "pt", + PUSHTO: "ps", + ROMANIAN: "ro", + RUSSIAN: "ru", + SANSKRIT: "sa", + SERBIAN: "sr", + SHONA: "sn", + SINDHI: "sd", + SINHALA: "si", + SLOVAK: "sk", + SLOVENIAN: "sl", + SOMALI: "so", + SPANISH: "es", + SUNDANESE: "su", + SWAHILI: "sw", + SWEDISH: "sv", + TAGALOG: "tl", + TAJIK: "tg", + TAMIL: "ta", + TATAR: "tt", + TELUGU: "te", + THAI: "th", }; class TranscriptionSettingTab extends PluginSettingTab { - plugin: Transcription; - - constructor(app: App, plugin: Transcription) { - super(app, plugin); - this.plugin = plugin; - } - - display(): void { - const { containerEl } = this; - - containerEl.empty(); - - containerEl.createEl("h2", { - text: "Settings for Obsidian Transcription", - }); - - new Setting(containerEl).setName("General Settings").setHeading(); - - new Setting(containerEl) - .setName("Transcription engine") - .setDesc("The transcription engine to use") - .setTooltip( - "Swiftink is a free cloud based transcription engine (no local set up, additional AI features). Whisper ASR is a self-hosted local transcription engine that uses a Python app (requires local setup).", - ) - .setClass("transcription-engine-setting") - .addDropdown((dropdown) => - dropdown - .addOption("swiftink", "Swiftink") - .addOption("whisper_asr", "Whisper ASR") - .setValue(this.plugin.settings.transcription_engine) - .onChange(async (value) => { - this.plugin.settings.transcription_engine = value; - await this.plugin.saveSettings(); - // Hide the settings for the other transcription engine - if (value == "swiftink") { - containerEl - .findAll(".swiftink-settings") - .forEach((element) => { - element.style.display = "block"; - }); - containerEl - .findAll(".whisper-asr-settings") - .forEach((element) => { - element.style.display = "none"; - }); - } else if (value == "whisper_asr") { - containerEl - .findAll(".swiftink-settings") - .forEach((element) => { - element.style.display = "none"; - }); - containerEl - .findAll(".whisper-asr-settings") - .forEach((element) => { - element.style.display = "block"; - }); - } - }), - ); - - new Setting(containerEl) - .setName("Notice verbosity") - .setDesc("How granularly notices should be displayed") - .setTooltip( - "Verbose will display a notice for every event in the backend. Normal will display a notice for every major event, such as successful transcription or file upload. Silent will not display any notices.", - ) - .addDropdown((dropdown) => - dropdown - .addOption("0", "Silent") - .addOption("1", "Normal") - .addOption("2", "Verbose") - .setValue(this.plugin.settings.verbosity.toString()) - .onChange(async (value) => { - this.plugin.settings.verbosity = parseInt(value); - await this.plugin.saveSettings(); - }), - ); - - new Setting(containerEl) - .setName("Language") - .setDesc("The language to transcribe the audio in") - .setTooltip("Automatically detected if not specified") - .addDropdown((dropdown) => { - dropdown.addOption("auto", "Auto-detect"); - for (const [key, value] of Object.entries(LANGUAGES)) { - dropdown.addOption( - value, - key.charAt(0).toUpperCase() + + plugin: Transcription; + + constructor(app: App, plugin: Transcription) { + super(app, plugin); + this.plugin = plugin; + } + + display(): void { + const { containerEl } = this; + + containerEl.empty(); + + containerEl.createEl("h2", { + text: "Settings for Obsidian Transcription", + }); + + new Setting(containerEl).setName("General Settings").setHeading(); + + new Setting(containerEl) + .setName("Transcription engine") + .setDesc("The transcription engine to use") + .setTooltip( + "Swiftink is a free cloud based transcription engine (no local set up, additional AI features). Whisper ASR is a self-hosted local transcription engine that uses a Python app (requires local setup).", + ) + .setClass("transcription-engine-setting") + .addDropdown((dropdown) => + dropdown + .addOption("swiftink", "Swiftink") + .addOption("whisper_asr", "Whisper ASR") + .setValue(this.plugin.settings.transcription_engine) + .onChange(async (value) => { + this.plugin.settings.transcription_engine = value; + await this.plugin.saveSettings(); + // Hide the settings for the other transcription engine + if (value == "swiftink") { + containerEl + .findAll(".swiftink-settings") + .forEach((element) => { + element.style.display = "block"; + }); + containerEl + .findAll(".whisper-asr-settings") + .forEach((element) => { + element.style.display = "none"; + }); + } else if (value == "whisper_asr") { + containerEl + .findAll(".swiftink-settings") + .forEach((element) => { + element.style.display = "none"; + }); + containerEl + .findAll(".whisper-asr-settings") + .forEach((element) => { + element.style.display = "block"; + }); + } + }), + ); + + new Setting(containerEl) + .setName("Notice verbosity") + .setDesc("How granularly notices should be displayed") + .setTooltip( + "Verbose will display a notice for every event in the backend. Normal will display a notice for every major event, such as successful transcription or file upload. Silent will not display any notices.", + ) + .addDropdown((dropdown) => + dropdown + .addOption("0", "Silent") + .addOption("1", "Normal") + .addOption("2", "Verbose") + .setValue(this.plugin.settings.verbosity.toString()) + .onChange(async (value) => { + this.plugin.settings.verbosity = parseInt(value); + await this.plugin.saveSettings(); + }), + ); + + new Setting(containerEl) + .setName("Language") + .setDesc("The language to transcribe the audio in") + .setTooltip("Automatically detected if not specified") + .addDropdown((dropdown) => { + dropdown.addOption("auto", "Auto-detect"); + for (const [key, value] of Object.entries(LANGUAGES)) { + dropdown.addOption( + value, + key.charAt(0).toUpperCase() + key.slice(1).toLowerCase(), - ); - } - dropdown.setValue(this.plugin.settings.language); - dropdown.onChange(async (value) => { - this.plugin.settings.language = value; - await this.plugin.saveSettings(); - }); - }); - - new Setting(containerEl) - .setName("Swiftink Settings") - .setClass("swiftink-settings") - .setHeading(); - - new Setting(containerEl) - .setClass("swiftink-settings") - .setName("Swiftink Account") - .addButton((bt) => { - bt.setButtonText("Sign in with Email"); - bt.setClass("swiftink-unauthed-only"); - bt.onClick(async () => { - window.open(SWIFTINK_AUTH_CALLBACK, "_blank"); - }); - }) - .addButton((bt) => { - bt.setButtonText("Sign in with Google"); - bt.setClass("swiftink-unauthed-only"); - bt.onClick(async () => { - this.plugin.supabase.auth.signInWithOAuth({ - provider: "google", - options: { redirectTo: "obsidian://swiftink_auth" }, - }); - }); - }) - .addButton((bt) => { - bt.setButtonText("Sign in with GitHub"); - bt.setClass("swiftink-unauthed-only"); - bt.onClick(async () => { - this.plugin.supabase.auth.signInWithOAuth({ - provider: "github", - options: { redirectTo: "obsidian://swiftink_auth" }, - }); - }); - }) - .addButton((bt) => { - bt.setButtonText("Log out"); - bt.setClass("swiftink-authed-only"); - bt.onClick(async () => { - await this.plugin.supabase.auth.signOut(); - this.plugin.user = null; - this.plugin.settings.swiftink_access_token = null; - this.plugin.settings.swiftink_refresh_token = null; - await this.plugin.saveSettings(); - containerEl - .findAll(".swiftink-unauthed-only") - .forEach((element) => { - element.style.display = "block"; - }); - containerEl - .findAll(".swiftink-authed-only") - .forEach((element) => { - element.style.display = "none"; - }); - new Notice("Successfully logged out"); - }); - }) - .addButton((bt) => { - bt.setButtonText(`Manage ${this.plugin.user?.email}`); - bt.setClass("swiftink-authed-only"); - bt.setClass("swiftink-manage-account-btn"); - bt.onClick(() => { - window.open( - "https://swiftink.io/dashboard/account", - "_blank", - ); - }); - }); - - new Setting(containerEl) - .setName("Enable timestamps") - .setDesc("Add timestamps to the beginning of each line") - .setClass("swiftink-settings") - .addToggle((toggle) => - toggle - .setValue(this.plugin.settings.timestamps) - .onChange(async (value) => { - this.plugin.settings.timestamps = value; - await this.plugin.saveSettings(); - }), - ); - - new Setting(containerEl) - .setName("Timestamp format") - .setDesc( - "Your choice of hours, minutes, and/or seconds in the timestamp", - ) - .setClass("swiftink-settings") - .addDropdown((dropdown) => - dropdown - .addOption("HH:mm:ss", "HH:mm:ss") - .addOption("mm:ss", "mm:ss") - .addOption("ss", "ss") - .setValue(this.plugin.settings.timestampFormat) - .onChange(async (value) => { - this.plugin.settings.timestampFormat = value; - await this.plugin.saveSettings(); - }), - ); - - new Setting(containerEl) - .setName("Embed summary") - .setDesc("Embed the generated transcription summary in the note") - .setTooltip( - "This will only work if you have a Swiftink Pro account", - ) - .setClass("swiftink-settings") - .addToggle((toggle) => - toggle - .setValue(this.plugin.settings.embedSummary) - .onChange(async (value) => { - this.plugin.settings.embedSummary = value; - await this.plugin.saveSettings(); - }), - ); - - new Setting(containerEl) - .setName("Embed outline") - .setDesc("Embed the generated trancription outline in the note") - .setTooltip( - "This will only work if you have a Swiftink Pro account", - ) - .setClass("swiftink-settings") - .addToggle((toggle) => - toggle - .setValue(this.plugin.settings.embedOutline) - .onChange(async (value) => { - this.plugin.settings.embedOutline = value; - await this.plugin.saveSettings(); - }), - ); - - new Setting(containerEl) - .setName("Embed keywords") - .setDesc("Embed the extracted keywords in the note") - .setTooltip( - "This will only work if you have a Swiftink Pro account", - ) - .setClass("swiftink-settings") - .addToggle((toggle) => - toggle - .setValue(this.plugin.settings.embedKeywords) - .onChange(async (value) => { - this.plugin.settings.embedKeywords = value; - await this.plugin.saveSettings(); - }), - ); - - new Setting(containerEl) - .setName("Embed function link") - .setDesc( - "(Recommended) Include an embedded link to the transcript function modal in the transcribed note", - ) - .setTooltip( - "If you disable this, you will not be able to import your additional transcript data or view the transcript on the Swiftink.io from within Obsidian.", - ) - .setClass("swiftink-settings") - .addToggle((toggle) => - toggle - .setValue(this.plugin.settings.embedAdditionalFunctionality) - .onChange(async (value) => { - this.plugin.settings.embedAdditionalFunctionality = + ); + } + dropdown.setValue(this.plugin.settings.language); + dropdown.onChange(async (value) => { + this.plugin.settings.language = value; + await this.plugin.saveSettings(); + }); + }); + + new Setting(containerEl) + .setName("Swiftink Settings") + .setClass("swiftink-settings") + .setHeading(); + + new Setting(containerEl) + .setClass("swiftink-settings") + .setName("Swiftink Account") + .addButton((bt) => { + bt.setButtonText("Sign in with Email"); + bt.setClass("swiftink-unauthed-only"); + bt.onClick(async () => { + window.open(SWIFTINK_AUTH_CALLBACK, "_blank"); + }); + }) + .addButton((bt) => { + bt.setButtonText("Sign in with Google"); + bt.setClass("swiftink-unauthed-only"); + bt.onClick(async () => { + this.plugin.supabase.auth.signInWithOAuth({ + provider: "google", + options: { redirectTo: "obsidian://swiftink_auth" }, + }); + }); + }) + .addButton((bt) => { + bt.setButtonText("Sign in with GitHub"); + bt.setClass("swiftink-unauthed-only"); + bt.onClick(async () => { + this.plugin.supabase.auth.signInWithOAuth({ + provider: "github", + options: { redirectTo: "obsidian://swiftink_auth" }, + }); + }); + }) + .addButton((bt) => { + bt.setButtonText("Log out"); + bt.setClass("swiftink-authed-only"); + bt.onClick(async () => { + await this.plugin.supabase.auth.signOut(); + this.plugin.user = null; + this.plugin.settings.swiftink_access_token = null; + this.plugin.settings.swiftink_refresh_token = null; + await this.plugin.saveSettings(); + containerEl + .findAll(".swiftink-unauthed-only") + .forEach((element) => { + element.style.display = "block"; + }); + containerEl + .findAll(".swiftink-authed-only") + .forEach((element) => { + element.style.display = "none"; + }); + new Notice("Successfully logged out"); + }); + }) + .addButton((bt) => { + bt.setButtonText(`Manage ${this.plugin.user?.email}`); + bt.setClass("swiftink-authed-only"); + bt.setClass("swiftink-manage-account-btn"); + bt.onClick(() => { + window.open( + "https://swiftink.io/dashboard/account", + "_blank", + ); + }); + }); + + new Setting(containerEl) + .setName("Enable timestamps") + .setDesc("Add timestamps to the beginning of each line") + .setClass("swiftink-settings") + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.timestamps) + .onChange(async (value) => { + this.plugin.settings.timestamps = value; + await this.plugin.saveSettings(); + }), + ); + + new Setting(containerEl) + .setName("Timestamp format") + .setDesc( + "Your choice of hours, minutes, and/or seconds in the timestamp", + ) + .setClass("swiftink-settings") + .addDropdown((dropdown) => + dropdown + .addOption("HH:mm:ss", "HH:mm:ss") + .addOption("mm:ss", "mm:ss") + .addOption("ss", "ss") + .setValue(this.plugin.settings.timestampFormat) + .onChange(async (value) => { + this.plugin.settings.timestampFormat = value; + await this.plugin.saveSettings(); + }), + ); + + new Setting(containerEl) + .setName("Embed summary") + .setDesc("Embed the generated transcription summary in the note") + .setTooltip( + "This will only work if you have a Swiftink Pro account", + ) + .setClass("swiftink-settings") + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.embedSummary) + .onChange(async (value) => { + this.plugin.settings.embedSummary = value; + await this.plugin.saveSettings(); + }), + ); + + new Setting(containerEl) + .setName("Embed outline") + .setDesc("Embed the generated trancription outline in the note") + .setTooltip( + "This will only work if you have a Swiftink Pro account", + ) + .setClass("swiftink-settings") + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.embedOutline) + .onChange(async (value) => { + this.plugin.settings.embedOutline = value; + await this.plugin.saveSettings(); + }), + ); + + new Setting(containerEl) + .setName("Embed keywords") + .setDesc("Embed the extracted keywords in the note") + .setTooltip( + "This will only work if you have a Swiftink Pro account", + ) + .setClass("swiftink-settings") + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.embedKeywords) + .onChange(async (value) => { + this.plugin.settings.embedKeywords = value; + await this.plugin.saveSettings(); + }), + ); + + new Setting(containerEl) + .setName("Embed function link") + .setDesc( + "(Recommended) Include an embedded link to the transcript function modal in the transcribed note", + ) + .setTooltip( + "If you disable this, you will not be able to import your additional transcript data or view the transcript on the Swiftink.io from within Obsidian.", + ) + .setClass("swiftink-settings") + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.embedAdditionalFunctionality) + .onChange(async (value) => { + this.plugin.settings.embedAdditionalFunctionality = value; - await this.plugin.saveSettings(); - }), - ); - - new Setting(containerEl) - .setName("Whisper ASR Settings") - .setClass("whisper-asr-settings") - .setHeading(); - - new Setting(containerEl) - .setName("Whisper ASR URL") - .setDesc( - "The URL of the Whisper ASR server: https://github.com/ahmetoner/whisper-asr-webservice", - ) - .setClass("whisper-asr-settings") - .addText((text) => - text - .setPlaceholder(DEFAULT_SETTINGS.whisperASRUrl) - .setValue(this.plugin.settings.whisperASRUrl) - .onChange(async (value) => { - this.plugin.settings.whisperASRUrl = value; - await this.plugin.saveSettings(); - }), - ); - - new Setting(containerEl).setName("Advanced Settings").setHeading(); - - new Setting(containerEl) - .setName("Debug mode") - .setDesc("Enable debug mode to see more console logs") - .addToggle((toggle) => - toggle - .setValue(this.plugin.settings.debug) - .onChange(async (value) => { - this.plugin.settings.debug = value; - await this.plugin.saveSettings(); - }), - ); - - // Swiftnk.io links - containerEl.createEl("hr"); - - const logoLink = containerEl.createEl("a"); - logoLink.href = "https://www.swiftink.io"; - logoLink.style.display = "block"; - logoLink.style.marginLeft = "auto"; - logoLink.style.marginRight = "auto"; - logoLink.style.width = "30%"; - - const logo = logoLink.createEl("img"); - logo.src = "https://www.swiftink.io/assets/img/logos/swiftink.svg"; - logo.alt = "Swiftink Logo"; - logo.style.display = "block"; - logo.style.width = "100%"; - - const name = containerEl.createEl("p"); - name.classList.add("swiftink-settings"); - name.innerHTML = "Swiftink.io"; - name.style.textAlign = "center"; - - const help = containerEl.createEl("p"); - help.classList.add("swiftink-settings"); - help.innerHTML = + await this.plugin.saveSettings(); + }), + ); + + new Setting(containerEl) + .setName("Whisper ASR Settings") + .setClass("whisper-asr-settings") + .setHeading(); + + new Setting(containerEl) + .setName("Whisper ASR URL") + .setDesc( + "The URL of the Whisper ASR server: https://github.com/ahmetoner/whisper-asr-webservice", + ) + .setClass("whisper-asr-settings") + .addText((text) => + text + .setPlaceholder(DEFAULT_SETTINGS.whisperASRUrl) + .setValue(this.plugin.settings.whisperASRUrl) + .onChange(async (value) => { + this.plugin.settings.whisperASRUrl = value; + await this.plugin.saveSettings(); + }), + ); + + new Setting(containerEl).setName("Advanced Settings").setHeading(); + + new Setting(containerEl) + .setName("Debug mode") + .setDesc("Enable debug mode to see more console logs") + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.debug) + .onChange(async (value) => { + this.plugin.settings.debug = value; + await this.plugin.saveSettings(); + }), + ); + + // Swiftnk.io links + containerEl.createEl("hr"); + + const logoLink = containerEl.createEl("a"); + logoLink.href = "https://www.swiftink.io"; + logoLink.style.display = "block"; + logoLink.style.marginLeft = "auto"; + logoLink.style.marginRight = "auto"; + logoLink.style.width = "30%"; + + const logo = logoLink.createEl("img"); + logo.src = "https://www.swiftink.io/assets/img/logos/swiftink.svg"; + logo.alt = "Swiftink Logo"; + logo.style.display = "block"; + logo.style.width = "100%"; + + const name = containerEl.createEl("p"); + name.classList.add("swiftink-settings"); + name.innerHTML = "Swiftink.io"; + name.style.textAlign = "center"; + + const help = containerEl.createEl("p"); + help.classList.add("swiftink-settings"); + help.innerHTML = "Questions? Please see our Documentation or email us at support@swiftink.io 🙂"; - help.style.textAlign = "center"; - help.style.fontSize = "0.85em"; + help.style.textAlign = "center"; + help.style.fontSize = "0.85em"; - const disclaimer = containerEl.createEl("p"); - disclaimer.classList.add("swiftink-settings"); - disclaimer.innerHTML = + const disclaimer = containerEl.createEl("p"); + disclaimer.classList.add("swiftink-settings"); + disclaimer.innerHTML = "By proceeding you agree to our Terms of Service and Privacy Policy."; - disclaimer.style.textAlign = "center"; - disclaimer.style.fontSize = "0.85em"; - - // Logic! (the incredible true story) - - // Initially hide the settings for the other transcription engine - if (this.plugin.settings.transcription_engine == "swiftink") { - containerEl.findAll(".swiftink-settings").forEach((element) => { - element.style.display = "block"; - }); - containerEl.findAll(".whisper-asr-settings").forEach((element) => { - element.style.display = "none"; - }); - } else if (this.plugin.settings.transcription_engine == "whisper_asr") { - containerEl.findAll(".swiftink-settings").forEach((element) => { - element.style.display = "none"; - }); - containerEl.findAll(".whisper-asr-settings").forEach((element) => { - element.style.display = "block"; - }); - } - - // Initially hide the settings for user auth/unauth based on whether the user is signed in - if (this.plugin.user == null) { - containerEl - .findAll(".swiftink-unauthed-only") - .forEach((element) => { - element.style.display = "block"; - }); - containerEl.findAll(".swiftink-authed-only").forEach((element) => { - element.style.display = "none"; - }); - } else { - containerEl - .findAll(".swiftink-unauthed-only") - .forEach((element) => { - element.style.display = "none"; - }); - containerEl.findAll(".swiftink-authed-only").forEach((element) => { - element.style.display = "block"; - }); - } - } + disclaimer.style.textAlign = "center"; + disclaimer.style.fontSize = "0.85em"; + + // Logic! (the incredible true story) + + // Initially hide the settings for the other transcription engine + if (this.plugin.settings.transcription_engine == "swiftink") { + containerEl.findAll(".swiftink-settings").forEach((element) => { + element.style.display = "block"; + }); + containerEl.findAll(".whisper-asr-settings").forEach((element) => { + element.style.display = "none"; + }); + } else if (this.plugin.settings.transcription_engine == "whisper_asr") { + containerEl.findAll(".swiftink-settings").forEach((element) => { + element.style.display = "none"; + }); + containerEl.findAll(".whisper-asr-settings").forEach((element) => { + element.style.display = "block"; + }); + } + + // Initially hide the settings for user auth/unauth based on whether the user is signed in + if (this.plugin.user == null) { + containerEl + .findAll(".swiftink-unauthed-only") + .forEach((element) => { + element.style.display = "block"; + }); + containerEl.findAll(".swiftink-authed-only").forEach((element) => { + element.style.display = "none"; + }); + } else { + containerEl + .findAll(".swiftink-unauthed-only") + .forEach((element) => { + element.style.display = "none"; + }); + containerEl.findAll(".swiftink-authed-only").forEach((element) => { + element.style.display = "block"; + }); + } + } } export type { TranscriptionSettings }; diff --git a/src/status.ts b/src/status.ts index ebc9e2d..dd0297a 100644 --- a/src/status.ts +++ b/src/status.ts @@ -2,108 +2,108 @@ // Inspired by // https://github.com/renehernandez/obsidian-readwise/blob/eee5676524962ebfa7eaf1084e018dafe3c2f394/src/status.ts export class StatusBar { - private messages: StatusBarMessage[] = []; - private currentMessage: StatusBarMessage | undefined; - private statusBarEl: HTMLElement; + private messages: StatusBarMessage[] = []; + private currentMessage: StatusBarMessage | undefined; + private statusBarEl: HTMLElement; - constructor(statusBarEl: HTMLElement) { - this.statusBarEl = statusBarEl; - } + constructor(statusBarEl: HTMLElement) { + this.statusBarEl = statusBarEl; + } - // Return true if there is a message in the queue that is forced - hasForcedMessage() { - return this.messages.some((message) => message.force); - } + // Return true if there is a message in the queue that is forced + hasForcedMessage() { + return this.messages.some((message) => message.force); + } - setText(message: StatusBarMessage) { - this.statusBarEl.setText(message.message); - } + setText(message: StatusBarMessage) { + this.statusBarEl.setText(message.message); + } - clearText() { - this.statusBarEl.setText(""); - } + clearText() { + this.statusBarEl.setText(""); + } - displayMessage(message: string, timeout: number, force = false, kek_mode = false) { - // Don't show the same message twice - if (this.messages[0] && this.messages[0].message === message) return; + displayMessage(message: string, timeout: number, force = false, kek_mode = false) { + // Don't show the same message twice + if (this.messages[0] && this.messages[0].message === message) return; - this.messages.push(new StatusBarMessage(`Transcribe: ${message.slice(0, 100)}`, timeout, force, kek_mode)); - this.display(); - } + this.messages.push(new StatusBarMessage(`Transcribe: ${message.slice(0, 100)}`, timeout, force, kek_mode)); + this.display(); + } - display() { - // First check if there are any forced messages, if so, clear the queue and queue the last forced message - if (this.hasForcedMessage()) { - const lastForced = this.messages.filter((message) => message.force).pop(); - if (lastForced) this.messages = [lastForced]; + display() { + // First check if there are any forced messages, if so, clear the queue and queue the last forced message + if (this.hasForcedMessage()) { + const lastForced = this.messages.filter((message) => message.force).pop(); + if (lastForced) this.messages = [lastForced]; - if (this.currentMessage !== lastForced) { - this.currentMessage = lastForced; - if (!this.currentMessage) return; - this.currentMessage.timeShown = Date.now(); - this.setText(this.currentMessage); - } - else if (this.currentMessage == lastForced && this.currentMessage && this.currentMessage.messageTimedOut()) { - this.clearText(); - } - } + if (this.currentMessage !== lastForced) { + this.currentMessage = lastForced; + if (!this.currentMessage) return; + this.currentMessage.timeShown = Date.now(); + this.setText(this.currentMessage); + } + else if (this.currentMessage == lastForced && this.currentMessage && this.currentMessage.messageTimedOut()) { + this.clearText(); + } + } - // Otherwise check if we need to do anything to the queue - else { - // If the current message has timed out, display the next message if there is one - if (this.currentMessage && this.currentMessage.messageTimedOut()) { - if (this.messages.length > 0) { - const currentMessage = this.messages.shift() - this.currentMessage = currentMessage; - if (this.currentMessage) { - this.setText(this.currentMessage); - this.currentMessage.timeShown = Date.now(); - } - else { - this.currentMessage = undefined; - this.clearText() - } - } else { - this.currentMessage = undefined; - this.clearText() - } - } - // If there is no current message, display the next message if there is one - else if (!this.currentMessage && this.messages.length > 0) { - const currentMessage = this.messages.shift(); - this.currentMessage = currentMessage; - if (!this.currentMessage) return; - this.setText(this.currentMessage); - this.currentMessage.timeShown = Date.now(); - } - else if (!this.currentMessage) { - this.clearText(); - } - } - } + // Otherwise check if we need to do anything to the queue + else { + // If the current message has timed out, display the next message if there is one + if (this.currentMessage && this.currentMessage.messageTimedOut()) { + if (this.messages.length > 0) { + const currentMessage = this.messages.shift() + this.currentMessage = currentMessage; + if (this.currentMessage) { + this.setText(this.currentMessage); + this.currentMessage.timeShown = Date.now(); + } + else { + this.currentMessage = undefined; + this.clearText() + } + } else { + this.currentMessage = undefined; + this.clearText() + } + } + // If there is no current message, display the next message if there is one + else if (!this.currentMessage && this.messages.length > 0) { + const currentMessage = this.messages.shift(); + this.currentMessage = currentMessage; + if (!this.currentMessage) return; + this.setText(this.currentMessage); + this.currentMessage.timeShown = Date.now(); + } + else if (!this.currentMessage) { + this.clearText(); + } + } + } } class StatusBarMessage { - message: string; - timeout: number; - force: boolean; - timeShown: number; - kek_mode: boolean; + message: string; + timeout: number; + force: boolean; + timeShown: number; + kek_mode: boolean; - messageAge = function() { - if (!this.timeShown) return 0; - return Date.now() - this.timeShown; - } + messageAge = function() { + if (!this.timeShown) return 0; + return Date.now() - this.timeShown; + } - messageTimedOut = function() { - return this.messageAge() >= this.timeout; - } + messageTimedOut = function() { + return this.messageAge() >= this.timeout; + } - constructor(message: string, timeout: number, force = false, kek_mode = false) { - this.message = message; - this.timeout = timeout; - this.force = force; - this.kek_mode = kek_mode; - } + constructor(message: string, timeout: number, force = false, kek_mode = false) { + this.message = message; + this.timeout = timeout; + this.force = force; + this.kek_mode = kek_mode; + } } diff --git a/src/transcribe.ts b/src/transcribe.ts index ee9f5b7..74c67a2 100644 --- a/src/transcribe.ts +++ b/src/transcribe.ts @@ -7,347 +7,382 @@ import { StatusBar } from "./status"; import { SupabaseClient } from "@supabase/supabase-js"; import * as tus from "tus-js-client"; -// This class is the parent for transcription engines. It takes settings and a file as an input and returns a transcription as a string - type TranscriptionBackend = (file: TFile) => Promise; export class TranscriptionEngine { - settings: TranscriptionSettings; - vault: Vault; - status_bar: StatusBar | null; - supabase: SupabaseClient; - - transcriptionEngine: TranscriptionBackend; - - transcription_engines: { [key: string]: TranscriptionBackend } = { - swiftink: this.getTranscriptionSwiftink, - whisper_asr: this.getTranscriptionWhisperASR, - }; - - constructor( - settings: TranscriptionSettings, - vault: Vault, - statusBar: StatusBar | null, - supabase: SupabaseClient, - ) { - this.settings = settings; - this.vault = vault; - this.status_bar = statusBar; - this.supabase = supabase; - } - - segmentsToTimestampedString( - segments: components["schemas"]["TimestampedTextSegment"][], - timestampFormat: string, - ): string { - let transcription = ""; - for (const segment of segments) { - // Start and end are second floats with 2 decimal places - // Convert to milliseconds and then to a date object - let start = new Date(segment.start * 1000); - let end = new Date(segment.end * 1000); - - // Subtract timezone to get UTC - start = new Date( - start.getTime() + start.getTimezoneOffset() * 60000, - ); - end = new Date(end.getTime() + end.getTimezoneOffset() * 60000); - - // Format the date objects using the timestamp format - const start_formatted = format(start, timestampFormat); - const end_formatted = format(end, timestampFormat); - - const segment_string = `${start_formatted} - ${end_formatted}: ${segment.text}\n`; - transcription += segment_string; - } - return transcription; - } - - /** - * - * @param {TFile} file - * @returns {Promise} promise that resolves to a string containing the transcription - */ - async getTranscription(file: TFile): Promise { - if (this.settings.debug) - console.log( - `Transcription engine: ${this.settings.transcription_engine}`, - ); - const start = new Date(); - this.transcriptionEngine = + settings: TranscriptionSettings; + vault: Vault; + status_bar: StatusBar | null; + supabase: SupabaseClient; + + transcriptionEngine: TranscriptionBackend; + + transcription_engines: { [key: string]: TranscriptionBackend } = { + swiftink: this.getTranscriptionSwiftink, + whisper_asr: this.getTranscriptionWhisperASR, + }; + + constructor( + settings: TranscriptionSettings, + vault: Vault, + statusBar: StatusBar | null, + supabase: SupabaseClient, + ) { + this.settings = settings; + this.vault = vault; + this.status_bar = statusBar; + this.supabase = supabase; + } + + segmentsToTimestampedString( + segments: components["schemas"]["TimestampedTextSegment"][], + timestampFormat: string, + ): string { + let transcription = ""; + for (const segment of segments) { + let start = new Date(segment.start * 1000); + let end = new Date(segment.end * 1000); + + start = new Date( + start.getTime() + start.getTimezoneOffset() * 60000, + ); + end = new Date(end.getTime() + end.getTimezoneOffset() * 60000); + + const start_formatted = format(start, timestampFormat); + const end_formatted = format(end, timestampFormat); + + const segment_string = `${start_formatted} - ${end_formatted}: ${segment.text}\n`; + transcription += segment_string; + } + return transcription; + } + + async getTranscription(file: TFile): Promise { + if (this.settings.debug) + console.log( + `Transcription engine: ${this.settings.transcription_engine}`, + ); + const start = new Date(); + this.transcriptionEngine = this.transcription_engines[this.settings.transcription_engine]; - return this.transcriptionEngine(file).then((transcription) => { - if (this.settings.debug) - console.log(`Transcription: ${transcription}`); - if (this.settings.debug) - console.log( - `Transcription took ${ - new Date().getTime() - start.getTime() - } ms`, - ); - return transcription; - }); - } - - async getTranscriptionWhisperASR(file: TFile): Promise { - // Now that we have the form data payload as an array buffer, we can pass it to requestURL - // We also need to set the content type to multipart/form-data and pass in the Boundary string - - const payload_data: PayloadData = {}; - payload_data["audio_file"] = new Blob([ - await this.vault.readBinary(file), - ]); - const [request_body, boundary_string] = + return this.transcriptionEngine(file).then((transcription) => { + if (this.settings.debug) + console.log(`Transcription: ${transcription}`); + if (this.settings.debug) + console.log( + `Transcription took ${new Date().getTime() - start.getTime() + } ms`, + ); + return transcription; + }); + } + + async getTranscriptionWhisperASR(file: TFile): Promise { + const payload_data: PayloadData = {}; + payload_data["audio_file"] = new Blob([ + await this.vault.readBinary(file), + ]); + const [request_body, boundary_string] = await payloadGenerator(payload_data); - let args = "task=transcribe"; - if (this.settings.language != "auto") - args += `&language=${this.settings.language}`; - - const url = `${this.settings.whisperASRUrl}/asr?${args}`; - console.log("URL:", url); - - const options: RequestUrlParam = { - method: "POST", - url: url, - contentType: `multipart/form-data; boundary=----${boundary_string}`, - body: request_body, - }; - console.log("Options:", options); - - return requestUrl(options) - .then(async (response) => { - if (this.settings.debug) console.log(response); - // WhisperASR returns a JSON object with a text field containing the transcription and segments field - - // Pull transcription from either response.text or response.json.text - if (typeof response.text === "string") return response.text; - else return response.json.text; - }) - .catch((error) => { - if (this.settings.debug) console.error(error); - return Promise.reject(error); - }); - } - - async getTranscriptionSwiftink(file: TFile): Promise { - // Declare constants for the API - // const api_base = 'http://localhost:8000' - const api_base = "https://api.swiftink.io"; - - const session = await this.supabase.auth.getSession().then((res) => { - return res.data; - }); - - if (session == null || session.session == null) { - window.open(SWIFTINK_AUTH_CALLBACK, "_blank"); - return Promise.reject( - "No user session found. Please log in and try again.", - ); - } - - const token = session.session.access_token; - const id = session.session.user.id; - - const fileStream = await this.vault.readBinary(file); - const filename = file.name.replace(/[^a-zA-Z0-9.]+/g, "-"); - - const uploadPromise = new Promise((resolve, reject) => { - const upload = new tus.Upload(new Blob([fileStream]), { - endpoint: `https://auth.swiftink.io/storage/v1/upload/resumable`, - retryDelays: [0, 3000, 5000, 10000, 20000], - headers: { - authorization: `Bearer ${token}`, - "x-upsert": "true", // set upsert to true to overwrite existing files - }, - uploadDataDuringCreation: true, - metadata: { - bucketName: "swiftink-upload", - objectName: `${id}/${filename}`, - }, - chunkSize: 6 * 1024 * 1024, // Supabase only supports 6MB chunks - onError: (error) => { - if (this.settings.debug) - console.log("Failed because: " + error); - reject(error); - }, - onProgress: (bytesUploaded, bytesTotal) => { - const percentage = ( - (bytesUploaded / bytesTotal) * + let args = "task=transcribe"; + if (this.settings.language != "auto") + args += `&language=${this.settings.language}`; + + const url = `${this.settings.whisperASRUrl}/asr?${args}`; + console.log("URL:", url); + + const options: RequestUrlParam = { + method: "POST", + url: url, + contentType: `multipart/form-data; boundary=----${boundary_string}`, + body: request_body, + }; + console.log("Options:", options); + + return requestUrl(options) + .then(async (response) => { + if (this.settings.debug) console.log(response); + if (typeof response.text === "string") return response.text; + else return response.json.text; + }) + .catch((error) => { + if (this.settings.debug) console.error(error); + return Promise.reject(error); + }); + } + + async getTranscriptionSwiftink(file: TFile): Promise { + const api_base = "https://api.swiftink.io"; + + const session = await this.supabase.auth.getSession().then((res) => { + return res.data; + }); + + if (session == null || session.session == null) { + window.open(SWIFTINK_AUTH_CALLBACK, "_blank"); + return Promise.reject( + "No user session found. Please log in and try again.", + ); + } + + const token = session.session.access_token; + const id = session.session.user.id; + + const fileStream = await this.vault.readBinary(file); + const filename = file.name.replace(/[^a-zA-Z0-9.]+/g, "-"); + + // Declare progress notice for uploading + let uploadProgressNotice: Notice | null = null; + + const uploadPromise = new Promise((resolve, reject) => { + const upload = new tus.Upload(new Blob([fileStream]), { + endpoint: `https://auth.swiftink.io/storage/v1/upload/resumable`, + retryDelays: [0, 3000, 5000, 10000, 20000], + headers: { + authorization: `Bearer ${token}`, + "x-upsert": "true", + }, + uploadDataDuringCreation: true, + metadata: { + bucketName: "swiftink-upload", + objectName: `${id}/${filename}`, + }, + chunkSize: 6 * 1024 * 1024, + onProgress: (bytesUploaded, bytesTotal) => { + const percentage = ( + (bytesUploaded / bytesTotal) * 100 - ).toFixed(2); - if (this.settings.debug) - console.log( - bytesUploaded, - bytesTotal, - percentage + "%", - ); - }, - onSuccess: () => { - if (this.settings.debug) - console.log( - `Successfully uploaded ${filename} to Swiftink`, - ); - resolve(upload); - }, - }); - - upload.start(); - }); - - try { - await uploadPromise; - new Notice(`Successfully uploaded ${filename} to Swiftink`); - } catch (error) { - if (this.settings.debug) - console.log("Failed to upload to Swiftink: ", error); - return Promise.reject(error); - } - - // Now lets create the transcription job - - // Lets first construct a fake public URL for the file, Swiftink can parse this URL to get the bucket and object name - const fileUrl = `https://auth.swiftink.io/storage/v1/object/public/swiftink-upload/${id}/${filename}`; - - const url = `${api_base}/transcripts/`; - const headers = { Authorization: `Bearer ${token}` }; - let body: paths["/transcripts/"]["post"]["requestBody"]["content"]["application/json"] = - { - name: filename, - url: fileUrl, - }; - - if (this.settings.language != "auto") - body.language = this.settings - .language as components["schemas"]["CreateTranscriptionRequest"]["language"]; - - if (this.settings.debug) console.log(body); - - const options: RequestUrlParam = { - method: "POST", - url: url, - headers: headers, - body: JSON.stringify(body), - }; - - let transcript_create_res; - try { - transcript_create_res = await requestUrl(options); - } catch (error) { - if (this.settings.debug) - console.log("Failed to create transcript: ", error); - return Promise.reject(error); - } - - let transcript: components["schemas"]["TranscriptSchema"] = + ).toFixed(2); + + // Create a notice message with the progress + const noticeMessage = `Uploading: ${percentage}%`; + + // Check if a notice has already been created + if (!uploadProgressNotice) { + // If not, create a new notice + uploadProgressNotice = new Notice(noticeMessage, 800 * 100); + } else { + // If the notice exists, update its content + uploadProgressNotice.setMessage(noticeMessage); + //uploadProgressNotice.hide(); + } + + if (this.settings.debug) { + console.log( + bytesUploaded, + bytesTotal, + percentage + "%", + ); + } + }, + onSuccess: () => { + if (this.settings.debug) { + console.log( + `Successfully uploaded ${filename} to Swiftink`, + ); + } + + // Close the progress notice on successful upload + if (uploadProgressNotice) { + uploadProgressNotice.hide(); + } + + resolve(upload); + }, + }); + + upload.start(); + }); + + try { + await uploadPromise; + new Notice(`Successfully uploaded ${filename} to Swiftink`); + } catch (error) { + if (this.settings.debug) { + console.log("Failed to upload to Swiftink: ", error); + } + + // // Close the progress notice on upload failure + // if (uploadProgressNotice) { + // uploadProgressNotice.hide(); + // } + + return Promise.reject(error); + } + + // Declare progress notice for transcription + let transcriptionProgressNotice: Notice | null = null; + + const fileUrl = `https://auth.swiftink.io/storage/v1/object/public/swiftink-upload/${id}/${filename}`; + const url = `${api_base}/transcripts/`; + const headers = { Authorization: `Bearer ${token}` }; + const body: paths["/transcripts/"]["post"]["requestBody"]["content"]["application/json"] = + { name: filename, url: fileUrl, }; + + if (this.settings.language != "auto") + body.language = this.settings + .language as components["schemas"]["CreateTranscriptionRequest"]["language"]; + + if (this.settings.debug) console.log(body); + + const options: RequestUrlParam = { + method: "POST", + url: url, + headers: headers, + body: JSON.stringify(body), + }; + + let transcript_create_res; + try { + transcript_create_res = await requestUrl(options); + } catch (error) { + if (this.settings.debug) + console.log("Failed to create transcript: ", error); + return Promise.reject(error); + } + + let transcript: components["schemas"]["TranscriptSchema"] = transcript_create_res.json; - if (this.settings.debug) console.log(transcript); - - let completed_statuses = ["transcribed", "complete"]; - - // If the user has any of the embed options enabled, we need to wait for the transcription to complete - if ( - (this.settings.embedSummary || this.settings.embedOutline, - this.settings.embedKeywords) - ) { - completed_statuses = ["complete"]; - } - - // Poll the API until the transcription is complete - return new Promise((resolve, reject) => { - let tries = 0; - const poll = setInterval(async () => { - const options: RequestUrlParam = { - method: "GET", - url: `${api_base}/transcripts/${transcript.id}`, - headers: headers, - }; - const transcript_res = await requestUrl(options); - transcript = transcript_res.json; - if (this.settings.debug) console.log(transcript); - if ( - transcript.status && - completed_statuses.contains(transcript.status) - ) { - clearInterval(poll); - new Notice( - `Successfully transcribed ${filename} with Swiftink`, - ); - resolve(this.formatSwiftinkResults(transcript)); - } else if (transcript.status == "failed") { - if (this.settings.debug) - console.error("Swiftink failed to transcribe the file"); - clearInterval(poll); - reject("Swiftink failed to transcribe the file"); - } else if (transcript.status == "validation_failed") { - if (this.settings.debug) - console.error("Swiftink has detected an invalid file"); - clearInterval(poll); - reject("Swiftink has detected an invalid file"); - } else if (tries > 20) { - if (this.settings.debug) - console.error( - "Swiftink took too long to transcribe the file", - ); - clearInterval(poll); - reject("Swiftink took too long to transcribe the file"); - } - tries++; - }, 3000); - }); - } - - formatSwiftinkResults( - transcript: components["schemas"]["TranscriptSchema"], - ): string { - let transcript_text: string = "## Transcript\n"; - - // Format the text into a string, the main body of the transcription - if (this.settings.timestamps) - transcript_text += this.segmentsToTimestampedString( - transcript.text_segments, - this.settings.timestampFormat, - ); - else transcript_text += transcript.text ? transcript.text : ""; - - if (transcript_text.slice(-1) !== "\n") transcript_text += "\n"; - - // Append the summary if the user has enabled it - if ( - this.settings.embedSummary && + if (this.settings.debug) console.log(transcript); + + let completed_statuses = ["transcribed", "complete"]; + + if ( + this.settings.embedSummary || + this.settings.embedOutline || + this.settings.embedKeywords + ) { + completed_statuses = ["complete"]; + } + + return new Promise((resolve, reject) => { + let tries = 0; + + // Function to update the transcription progress notice + const updateTranscriptionNotice = (percentage: number) => { + const noticeMessage = `Transcribing ${transcript.name}...`; + if (!transcriptionProgressNotice) { + transcriptionProgressNotice = new Notice(noticeMessage, 800 * 100); + } else { + transcriptionProgressNotice.setMessage(noticeMessage); + + } + }; + + const poll = setInterval(async () => { + const options: RequestUrlParam = { + method: "GET", + url: `${api_base}/transcripts/${transcript.id}`, + headers: headers, + }; + const transcript_res = await requestUrl(options); + transcript = transcript_res.json; + if (this.settings.debug) console.log(transcript); + + if ( + transcript.status && + completed_statuses.includes(transcript.status) + ) { + clearInterval(poll); + + //Close the transcription progress notice on completion + if (transcriptionProgressNotice) { + transcriptionProgressNotice.hide(); + } + + new Notice( + `Successfully transcribed ${filename} with Swiftink`, + ); + resolve(this.formatSwiftinkResults(transcript)); + } else if (transcript.status == "failed") { + if (this.settings.debug) + console.error( + "Swiftink failed to transcribe the file" + ); + clearInterval(poll); + reject("Swiftink failed to transcribe the file"); + } else if (transcript.status == "validation_failed") { + if (this.settings.debug) + console.error( + "Swiftink has detected an invalid file" + ); + clearInterval(poll); + reject("Swiftink has detected an invalid file"); + } else if (tries > 20) { + if (this.settings.debug) + console.error( + "Swiftink took too long to transcribe the file" + ); + clearInterval(poll); + reject( + "Swiftink took too long to transcribe the file" + ); + } else { + // Update the transcription progress notice + updateTranscriptionNotice( + (tries / 20) * 100 + ); + } + tries++; + }, 3000); + }); + } + + formatSwiftinkResults( + transcript: components["schemas"]["TranscriptSchema"] + ): string { + let transcript_text = "## Transcript\n"; + + if (this.settings.timestamps) + transcript_text += this.segmentsToTimestampedString( + transcript.text_segments, + this.settings.timestampFormat + ); + else transcript_text += transcript.text ? transcript.text : ""; + + if (transcript_text.slice(-1) !== "\n") + transcript_text += "\n"; + + if ( + this.settings.embedSummary && transcript.summary && - transcript.summary !== "Insufficient information for a summary." - ) - transcript_text += `## Summary\n${transcript.summary}`; + transcript.summary !== + "Insufficient information for a summary." + ) + transcript_text += `## Summary\n${transcript.summary}`; - // If there isnt a \n at the end of the output add one - if (transcript_text.slice(-1) !== "\n") transcript_text += "\n"; + if (transcript_text.slice(-1) !== "\n") + transcript_text += "\n"; - // Append the outline if the user has enabled it - if ( - this.settings.embedOutline && + if ( + this.settings.embedOutline && transcript.heading_segments.length > 0 - ) - transcript_text += `## Outline\n${this.segmentsToTimestampedString( - transcript.heading_segments, - this.settings.timestampFormat, - )}`; - - // If there isnt a \n at the end of the output add one - if (transcript_text.slice(-1) !== "\n") transcript_text += "\n"; - - // Append the keywords if the user has enabled it - if (this.settings.embedKeywords && transcript.keywords.length > 0) - transcript_text += `## Keywords\n${transcript.keywords.join(", ")}`; - - // If there isnt a \n at the end of the output add one - if (transcript_text.slice(-1) !== "\n") transcript_text += "\n"; - - // Append a link to the Swiftink transcript if the user has enabled it - if (this.settings.embedAdditionalFunctionality) { - transcript_text += `[...](obsidian://swiftink_transcript_functions?id=${transcript.id})`; - } - - return transcript_text; - } + ) + transcript_text += `## Outline\n${this.segmentsToTimestampedString( + transcript.heading_segments, + this.settings.timestampFormat + )}`; + + if (transcript_text.slice(-1) !== "\n") + transcript_text += "\n"; + + if ( + this.settings.embedKeywords && + transcript.keywords.length > 0 + ) + transcript_text += `## Keywords\n${transcript.keywords.join( + ", " + )}`; + + if (transcript_text.slice(-1) !== "\n") + transcript_text += "\n"; + + if (this.settings.embedAdditionalFunctionality) { + transcript_text += `[...](obsidian://swiftink_transcript_functions?id=${transcript.id})`; + } + + return transcript_text; + } } +