diff --git a/package.json b/package.json index 807469c..94bf9a3 100644 --- a/package.json +++ b/package.json @@ -41,9 +41,13 @@ }, "dependencies": { "@ag-grid-community/theming": "^32.3.3", + "@codemirror/language": "^6.10.8", + "@codemirror/state": "^6.5.1", + "@codemirror/view": "^6.36.2", "@hypersphere/omnibus": "^0.1.6", "@jlongster/sql.js": "^1.6.7", "@types/jsonpath": "^0.2.4", + "@vanakat/plugin-api": "^0.2.1", "absurd-sql": "^0.0.54", "ag-grid-community": "^32.3.3", "comlink": "^4.4.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 61ea522..6d54463 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,15 @@ importers: '@ag-grid-community/theming': specifier: ^32.3.3 version: 32.3.3 + '@codemirror/language': + specifier: ^6.10.8 + version: 6.10.8 + '@codemirror/state': + specifier: ^6.5.1 + version: 6.5.1 + '@codemirror/view': + specifier: ^6.36.2 + version: 6.36.2 '@hypersphere/omnibus': specifier: ^0.1.6 version: 0.1.6 @@ -20,6 +29,9 @@ importers: '@types/jsonpath': specifier: ^0.2.4 version: 0.2.4 + '@vanakat/plugin-api': + specifier: ^0.2.1 + version: 0.2.1(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)(eslint@9.15.0) absurd-sql: specifier: ^0.0.54 version: 0.0.54 @@ -95,7 +107,7 @@ importers: version: 29.7.0(@types/node@22.9.3) obsidian: specifier: ^1.7.2 - version: 1.7.2(@codemirror/state@6.4.1)(@codemirror/view@6.35.0) + version: 1.7.2(@codemirror/state@6.5.1)(@codemirror/view@6.36.2) prettier: specifier: 3.3.3 version: 3.3.3 @@ -360,11 +372,14 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@codemirror/state@6.4.1': - resolution: {integrity: sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==} + '@codemirror/language@6.10.8': + resolution: {integrity: sha512-wcP8XPPhDH2vTqf181U8MbZnW+tDyPYy0UzVOa+oHORjyT+mhhom9vBd7dApJwoDz9Nb/a8kHjJIsuA/t8vNFw==} - '@codemirror/view@6.35.0': - resolution: {integrity: sha512-I0tYy63q5XkaWsJ8QRv5h6ves7kvtrBWjBcnf/bzohFJQc5c14a1AQRdE8QpPF9eMp5Mq2FMm59TCj1gDfE7kw==} + '@codemirror/state@6.5.1': + resolution: {integrity: sha512-3rA9lcwciEB47ZevqvD8qgbzhM9qMb8vCcQCNmDfVRPQG4JT9mSb0Jg8H7YjKGGQcFnLN323fj9jdnG59Kx6bg==} + + '@codemirror/view@6.36.2': + resolution: {integrity: sha512-DZ6ONbs8qdJK0fdN7AB82CgI6tYXf4HWk1wSVa0+9bhVznCuuvhQtX8bFBoy3dv8rZSQqUd8GvhVAcielcidrA==} '@docsearch/css@3.8.0': resolution: {integrity: sha512-pieeipSOW4sQ0+bE5UFC51AOZp9NGxg89wAlZ1BAQFaiRAGK1IKUaPQ0UGZeNctJXyqZ1UvBtOQh2HH+U5GtmA==} @@ -835,10 +850,22 @@ packages: '@jspm/core@2.1.0': resolution: {integrity: sha512-3sRl+pkyFY/kLmHl0cgHiFp2xEqErA8N3ECjMs7serSUBmoJ70lBa0PG5t0IM6WJgdZNyyI0R8YFfi5wM8+mzg==} + '@lezer/common@1.2.3': + resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} + + '@lezer/highlight@1.2.1': + resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==} + + '@lezer/lr@1.4.2': + resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==} + '@malept/cross-spawn-promise@2.0.0': resolution: {integrity: sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==} engines: {node: '>= 12.13.0'} + '@marijn/find-cluster-break@1.0.2': + resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1088,6 +1115,17 @@ packages: '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + '@typescript-eslint/eslint-plugin@7.18.0': + resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + '@typescript-eslint/eslint-plugin@8.15.0': resolution: {integrity: sha512-+zkm9AR1Ds9uLWN3fkoeXgFppaQ+uEVtfOV62dDmsy9QCNqlRHWNEck4yarvRNrvRcHQLGfqBNui3cimoz8XAg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1099,6 +1137,16 @@ packages: typescript: optional: true + '@typescript-eslint/parser@7.18.0': + resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + '@typescript-eslint/parser@8.15.0': resolution: {integrity: sha512-7n59qFpghG4uazrF9qtGKBZXn7Oz4sOMm8dwNWDQY96Xlm2oX67eipqcblDj+oY1lLCbf1oltMZFpUso66Kl1A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1109,10 +1157,24 @@ packages: typescript: optional: true + '@typescript-eslint/scope-manager@7.18.0': + resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} + engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/scope-manager@8.15.0': resolution: {integrity: sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/type-utils@7.18.0': + resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + '@typescript-eslint/type-utils@8.15.0': resolution: {integrity: sha512-UU6uwXDoI3JGSXmcdnP5d8Fffa2KayOhUUqr/AiBnG1Gl7+7ut/oyagVeSkh7bxQ0zSXV9ptRh/4N15nkCqnpw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1123,10 +1185,23 @@ packages: typescript: optional: true + '@typescript-eslint/types@7.18.0': + resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} + engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/types@8.15.0': resolution: {integrity: sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@7.18.0': + resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + '@typescript-eslint/typescript-estree@8.15.0': resolution: {integrity: sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1136,6 +1211,12 @@ packages: typescript: optional: true + '@typescript-eslint/utils@7.18.0': + resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + '@typescript-eslint/utils@8.15.0': resolution: {integrity: sha512-k82RI9yGhr0QM3Dnq+egEpz9qB6Un+WLYhmoNcvl8ltMEededhh7otBVVIDDsEEttauwdY/hQoSsOv13lxrFzQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1146,6 +1227,10 @@ packages: typescript: optional: true + '@typescript-eslint/visitor-keys@7.18.0': + resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} + engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/visitor-keys@8.15.0': resolution: {integrity: sha512-h8vYOulWec9LhpwfAdZf2bjr8xIp0KNKnpgqSz0qqYYKAW/QZKw3ktRndbiAtUz4acH4QLQavwZBYCc0wulA/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1153,6 +1238,9 @@ packages: '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@vanakat/plugin-api@0.2.1': + resolution: {integrity: sha512-m2gVJFTPoIG+fUDAWYPPW/XoywBXrGtYOZFZdQHZmL62Zoc9NntCID76/Wu17DAanQ4aiZN8yRHHXlfq6sPvsQ==} + '@vitejs/plugin-vue@5.2.0': resolution: {integrity: sha512-7n7KdUEtx/7Yl7I/WVAMZ1bEb0eVvXF3ummWTeLcs/9gvo9pJhuLdouSXGjdZ/MKD1acf1I272+X0RMua4/R3g==} engines: {node: ^18.0.0 || >=20.0.0} @@ -1323,6 +1411,10 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} @@ -1591,6 +1683,10 @@ packages: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + ejs@3.1.10: resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} engines: {node: '>=0.10.0'} @@ -1876,6 +1972,10 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} @@ -2589,6 +2689,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} @@ -2967,6 +3071,11 @@ packages: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} + typescript@5.4.5: + resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + engines: {node: '>=14.17'} + hasBin: true + typescript@5.7.2: resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} engines: {node: '>=14.17'} @@ -3466,11 +3575,22 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@codemirror/state@6.4.1': {} - - '@codemirror/view@6.35.0': + '@codemirror/language@6.10.8': dependencies: - '@codemirror/state': 6.4.1 + '@codemirror/state': 6.5.1 + '@codemirror/view': 6.36.2 + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + style-mod: 4.1.2 + + '@codemirror/state@6.5.1': + dependencies: + '@marijn/find-cluster-break': 1.0.2 + + '@codemirror/view@6.36.2': + dependencies: + '@codemirror/state': 6.5.1 style-mod: 4.1.2 w3c-keyname: 2.2.8 @@ -3894,10 +4014,22 @@ snapshots: '@jspm/core@2.1.0': {} + '@lezer/common@1.2.3': {} + + '@lezer/highlight@1.2.1': + dependencies: + '@lezer/common': 1.2.3 + + '@lezer/lr@1.4.2': + dependencies: + '@lezer/common': 1.2.3 + '@malept/cross-spawn-promise@2.0.0': dependencies: cross-spawn: 7.0.6 + '@marijn/find-cluster-break@1.0.2': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4140,6 +4272,24 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.15.0)(typescript@5.4.5))(eslint@9.15.0)(typescript@5.4.5)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 7.18.0(eslint@9.15.0)(typescript@5.4.5) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/type-utils': 7.18.0(eslint@9.15.0)(typescript@5.4.5) + '@typescript-eslint/utils': 7.18.0(eslint@9.15.0)(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 7.18.0 + eslint: 9.15.0 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 1.4.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/eslint-plugin@8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.7.2))(eslint@9.15.0)(typescript@5.7.2)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -4158,6 +4308,19 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/parser@7.18.0(eslint@9.15.0)(typescript@5.4.5)': + dependencies: + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.3.7 + eslint: 9.15.0 + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.7.2)': dependencies: '@typescript-eslint/scope-manager': 8.15.0 @@ -4171,11 +4334,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/scope-manager@7.18.0': + dependencies: + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + '@typescript-eslint/scope-manager@8.15.0': dependencies: '@typescript-eslint/types': 8.15.0 '@typescript-eslint/visitor-keys': 8.15.0 + '@typescript-eslint/type-utils@7.18.0(eslint@9.15.0)(typescript@5.4.5)': + dependencies: + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.4.5) + '@typescript-eslint/utils': 7.18.0(eslint@9.15.0)(typescript@5.4.5) + debug: 4.3.7 + eslint: 9.15.0 + ts-api-utils: 1.4.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/type-utils@8.15.0(eslint@9.15.0)(typescript@5.7.2)': dependencies: '@typescript-eslint/typescript-estree': 8.15.0(typescript@5.7.2) @@ -4188,8 +4368,25 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/types@7.18.0': {} + '@typescript-eslint/types@8.15.0': {} + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.4.5)': + dependencies: + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.3.7 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 1.4.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/typescript-estree@8.15.0(typescript@5.7.2)': dependencies: '@typescript-eslint/types': 8.15.0 @@ -4205,6 +4402,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@7.18.0(eslint@9.15.0)(typescript@5.4.5)': + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.15.0) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.4.5) + eslint: 9.15.0 + transitivePeerDependencies: + - supports-color + - typescript + '@typescript-eslint/utils@8.15.0(eslint@9.15.0)(typescript@5.7.2)': dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.15.0) @@ -4217,6 +4425,11 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/visitor-keys@7.18.0': + dependencies: + '@typescript-eslint/types': 7.18.0 + eslint-visitor-keys: 3.4.3 + '@typescript-eslint/visitor-keys@8.15.0': dependencies: '@typescript-eslint/types': 8.15.0 @@ -4224,6 +4437,18 @@ snapshots: '@ungap/structured-clone@1.2.0': {} + '@vanakat/plugin-api@0.2.1(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)(eslint@9.15.0)': + dependencies: + '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.15.0)(typescript@5.4.5))(eslint@9.15.0)(typescript@5.4.5) + '@typescript-eslint/parser': 7.18.0(eslint@9.15.0)(typescript@5.4.5) + obsidian: 1.7.2(@codemirror/state@6.5.1)(@codemirror/view@6.36.2) + typescript: 5.4.5 + transitivePeerDependencies: + - '@codemirror/state' + - '@codemirror/view' + - eslint + - supports-color + '@vitejs/plugin-vue@5.2.0(vite@5.4.11(@types/node@22.9.3))(vue@3.5.13(typescript@5.7.2))': dependencies: vite: 5.4.11(@types/node@22.9.3) @@ -4417,6 +4642,8 @@ snapshots: argparse@2.0.1: {} + array-union@2.1.0: {} + async@3.2.6: {} available-typed-arrays@1.0.7: @@ -4706,6 +4933,10 @@ snapshots: diff-sequences@29.6.3: {} + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + ejs@3.1.10: dependencies: jake: 10.9.2 @@ -5075,6 +5306,15 @@ snapshots: globals@14.0.0: {} + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + gopd@1.0.1: dependencies: get-intrinsic: 1.2.4 @@ -5902,10 +6142,10 @@ snapshots: gauge: 4.0.4 set-blocking: 2.0.0 - obsidian@1.7.2(@codemirror/state@6.4.1)(@codemirror/view@6.35.0): + obsidian@1.7.2(@codemirror/state@6.5.1)(@codemirror/view@6.36.2): dependencies: - '@codemirror/state': 6.4.1 - '@codemirror/view': 6.35.0 + '@codemirror/state': 6.5.1 + '@codemirror/view': 6.36.2 '@types/codemirror': 5.60.8 moment: 2.29.4 @@ -5998,6 +6238,8 @@ snapshots: path-parse@1.0.7: {} + path-type@4.0.0: {} + perfect-debounce@1.0.0: {} picocolors@1.1.1: {} @@ -6308,6 +6550,10 @@ snapshots: trim-lines@3.0.1: {} + ts-api-utils@1.4.0(typescript@5.4.5): + dependencies: + typescript: 5.4.5 + ts-api-utils@1.4.0(typescript@5.7.2): dependencies: typescript: 5.7.2 @@ -6346,6 +6592,8 @@ snapshots: type-fest@0.21.3: {} + typescript@5.4.5: {} + typescript@5.7.2: {} underscore@1.12.1: {} diff --git a/src/cellParser.ts b/src/cellParser.ts new file mode 100644 index 0000000..228467d --- /dev/null +++ b/src/cellParser.ts @@ -0,0 +1,56 @@ +import { Database } from "sql.js"; +import { parse } from 'json5' +import { SqlSealDatabase } from "./database/database"; + +export interface CellParser { + render: (content: string) => Node | string; +} + +export class CellParserRegistar implements CellParser { + functions: Map = new Map() + constructor(private db: SqlSealDatabase) { + + } + + register(name: string, fn: any, argsCount = 1) { + if (this.functions.has(name)) { + throw new Error('Function with that name already exists: ' + name) + } + this.functions.set(name, fn) + this.db.registerCustomFunction(name, argsCount) + } + + unregister(name: string) { + this.functions.delete(name) + } + + private renderCustomElement(el: any): Node | string { + if (this.functions.has(el.type)) { + return this.functions.get(el.type)!(el.values) + } else { + throw new Error(`Custom function processor ${el.type} is not registered`) + } + } + + render(content: string) { + try { + if (!content) { + return content + } + if (typeof content !== 'string') { + return content + } + if (content.startsWith('SQLSEALCUSTOM')) { + const parsedData = parse(content.slice('SQLSEALCUSTOM('.length, -1)) + return this.renderCustomElement(parsedData) + } + return content + } catch (e) { + console.error('Error parsing cell with data:', content, e) + return createDiv({ + text: 'Parsing error', + cls: 'sqlseal-parse-error' + }) + } + } +} diff --git a/src/codeblockHandler/CodeblockProcessor.ts b/src/codeblockHandler/CodeblockProcessor.ts index 09afe36..f1201d9 100644 --- a/src/codeblockHandler/CodeblockProcessor.ts +++ b/src/codeblockHandler/CodeblockProcessor.ts @@ -1,13 +1,13 @@ import { OmnibusRegistrator } from "@hypersphere/omnibus"; import { App, MarkdownPostProcessorContext, MarkdownRenderChild, TFile } from "obsidian"; -import { SqlSealDatabase } from "src/database/database"; -import { Sync } from "src/datamodel/sync"; -import { ParserTableDefinition } from "src/datamodel/syncStrategy/types"; -import { parseLanguage } from "src/grammar/newParser"; -import { RendererRegistry, RenderReturn } from "src/renderer/rendererRegistry"; -import { transformQuery } from "src/sql/sqlTransformer"; -import { registerObservers } from "src/utils/registerObservers"; -import { displayError, displayNotice } from "src/utils/ui"; +import { SqlSealDatabase } from "../database/database"; +import { Sync } from "../datamodel/sync"; +import { ParserTableDefinition } from "../datamodel/syncStrategy/types"; +import { parseLanguage } from "../grammar/newParser"; +import { RendererRegistry, RenderReturn } from "../renderer/rendererRegistry"; +import { transformQuery } from "../sql/sqlTransformer"; +import { registerObservers } from "../utils/registerObservers"; +import { displayError, displayNotice } from "../utils/ui"; export class CodeblockProcessor extends MarkdownRenderChild { diff --git a/src/codeblockHandler/inline/InlineCodeHandler.ts b/src/codeblockHandler/inline/InlineCodeHandler.ts index 078ba43..ea28051 100644 --- a/src/codeblockHandler/inline/InlineCodeHandler.ts +++ b/src/codeblockHandler/inline/InlineCodeHandler.ts @@ -1,7 +1,7 @@ import { App, MarkdownPostProcessorContext } from "obsidian"; import { InlineProcessor } from "./InlineProcessor"; -import { SqlSealDatabase } from "src/database/database"; -import { Sync } from "src/datamodel/sync"; +import { SqlSealDatabase } from "../../database/database"; +import { Sync } from "../../datamodel/sync"; export class SqlSealInlineHandler { constructor( diff --git a/src/codeblockHandler/inline/InlineProcessor.ts b/src/codeblockHandler/inline/InlineProcessor.ts index df33da8..bc3cb87 100644 --- a/src/codeblockHandler/inline/InlineProcessor.ts +++ b/src/codeblockHandler/inline/InlineProcessor.ts @@ -1,11 +1,11 @@ import { OmnibusRegistrator } from "@hypersphere/omnibus"; import { App, MarkdownRenderChild } from "obsidian"; -import { SqlSealDatabase } from "src/database/database"; -import { Sync } from "src/datamodel/sync"; -import { RenderReturn } from "src/renderer/rendererRegistry"; -import { transformQuery } from "src/sql/sqlTransformer"; -import { registerObservers } from "src/utils/registerObservers"; -import { displayError } from "src/utils/ui"; +import { SqlSealDatabase } from "../../database/database"; +import { Sync } from "../../datamodel/sync"; +import { RenderReturn } from "../../renderer/rendererRegistry"; +import { transformQuery } from "../../sql/sqlTransformer"; +import { registerObservers } from "../../utils/registerObservers"; +import { displayError } from "../../utils/ui"; export class InlineProcessor extends MarkdownRenderChild { private registrator: OmnibusRegistrator; diff --git a/src/database/database.ts b/src/database/database.ts index abba5c5..ed8a319 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -1,5 +1,6 @@ import { App } from "obsidian" import * as Comlink from 'comlink' +// @ts-ignore import workerCode from 'virtual:worker-code' import { WorkerDatabase } from "./worker/database"; @@ -11,6 +12,10 @@ export class SqlSealDatabase { } + registerCustomFunction(name: string, argsCount = 1) { + return this.db.registerCustomFunction(name, argsCount) + } + async connect() { if (this.isConnected) { return Promise.resolve() diff --git a/src/database/worker/database.ts b/src/database/worker/database.ts index dca8154..3ab8a80 100644 --- a/src/database/worker/database.ts +++ b/src/database/worker/database.ts @@ -1,12 +1,12 @@ import * as Comlink from "comlink" import initSqlJs from '@jlongster/sql.js'; +// @ts-ignore import wasmBinary from '../../../node_modules/@jlongster/sql.js/dist/sql-wasm.wasm' import { SQLiteFS } from 'absurd-sql'; import IndexedDBBackend from '../../../node_modules/absurd-sql/dist/indexeddb-backend.js'; import type { BindParams, Database, Statement } from "sql.js"; import { sanitise } from "../../utils/sanitiseColumn"; import { uniq } from "lodash"; -import { createLoggingArrayProxy } from "src/utils/logArrayProxy"; function toObjectArray(stmt: Statement) { @@ -60,49 +60,28 @@ export class WorkerDatabase { } - defineCustomFunctions() { - this.db.create_function('a', (href: string, name: string) => { - const linkObject = { - type: 'link', - href: href, - name: name || href - }; - return `SQLSEALCUSTOM(${JSON.stringify(linkObject)})`; - }); - - this.db.create_function('a', (href: string) => { - const linkObject = { - type: 'link', - href: href, - name: href - }; - return `SQLSEALCUSTOM(${JSON.stringify(linkObject)})`; - }); - - this.db.create_function('img', (href: string) => { - const imgObject = { - type: 'img', - href: href + registerCustomFunction(name: string, argsCount = 1) { + const fn = (...arg: string[]) => { + const data = { + type: name, + values: arg } - return `SQLSEALCUSTOM(${JSON.stringify(imgObject)})` - }) + return `SQLSEALCUSTOM(${JSON.stringify(data)})` + } - this.db.create_function('img', (href: string, path: string) => { - const imgObject = { - type: 'img', - path, - href - } - return `SQLSEALCUSTOM(${JSON.stringify(imgObject)})` - }) - - this.db.create_function('checkbox', (val: string) => { - const imgObject = { - type: 'checkbox', - value: val - } - return `SQLSEALCUSTOM(${JSON.stringify(imgObject)})` - }) + // This is such a stupid solution but number of arguments needs to be static so SQLite understands which overload to use. + if (argsCount >= 1) { + this.db.create_function(name, (a: string) => fn(a)) + } + if (argsCount >= 2) { + this.db.create_function(name, (a: string, b: string) => fn(a, b)) + } + if (argsCount >= 3) { + this.db.create_function(name, (a: string, b: string, c: string) => fn(a, b, c)) + } + if (argsCount >= 4) { + throw new Error('Too many arguments, only up to 3 arguments are supported at the moment.') + } } createTableText(tableName: string, fields: string[]) { @@ -227,7 +206,6 @@ export class WorkerDatabase { VACUUM; `); this.db = db - this.defineCustomFunctions() return } catch (e) { console.error('Error while setting up database', e); diff --git a/src/datamodel/repository/abstractRepository.ts b/src/datamodel/repository/abstractRepository.ts index b60ed6b..bfddf9c 100644 --- a/src/datamodel/repository/abstractRepository.ts +++ b/src/datamodel/repository/abstractRepository.ts @@ -1,4 +1,4 @@ -import { SqlSealDatabase } from "src/database/database"; +import { SqlSealDatabase } from "../../database/database"; export abstract class Repository { constructor(protected readonly db: SqlSealDatabase) { } diff --git a/src/datamodel/sync.ts b/src/datamodel/sync.ts index d0d7852..2662f53 100644 --- a/src/datamodel/sync.ts +++ b/src/datamodel/sync.ts @@ -1,4 +1,4 @@ -import { SqlSealDatabase } from "src/database/database"; +import { SqlSealDatabase } from "../database/database"; import { TableAliasesRepository } from "./repository/tableAliases"; import { App, TAbstractFile, TFile, Vault } from "obsidian"; import { FilepathHasher } from "../utils/hasher"; diff --git a/src/editorExtension/inlineCodeBlock.ts b/src/editorExtension/inlineCodeBlock.ts index 734e35d..c6a0875 100644 --- a/src/editorExtension/inlineCodeBlock.ts +++ b/src/editorExtension/inlineCodeBlock.ts @@ -4,8 +4,8 @@ import { syntaxTree } from "@codemirror/language"; import { App } from "obsidian"; import { SqlSealDatabase } from "../database/database"; import { Sync } from "../datamodel/sync"; -import { SqlSealInlineHandler } from "src/codeblockHandler/inline/InlineCodeHandler"; -import { InlineProcessor } from "src/codeblockHandler/inline/InlineProcessor"; +import { SqlSealInlineHandler } from "../codeblockHandler/inline/InlineCodeHandler"; +import { InlineProcessor } from "../codeblockHandler/inline/InlineProcessor"; export function createSqlSealEditorExtension( app: App, diff --git a/src/main.ts b/src/main.ts index 735a7e7..31e1fea 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,26 +1,26 @@ import { Menu, Plugin, TAbstractFile, Tasks, TFile } from 'obsidian'; -import { GridRenderer } from 'src/renderer/GridRenderer'; -import { MarkdownRenderer } from 'src/renderer/MarkdownRenderer'; -import { TableRenderer } from 'src/renderer/TableRenderer'; -import { RendererConfig, RendererRegistry } from 'src/renderer/rendererRegistry'; -import { DEFAULT_SETTINGS, SQLSealSettings, SQLSealSettingsTab } from 'src/settings/SQLSealSettingsTab'; -import { SqlSeal } from 'src/sqlSeal'; -import { SealFileSync } from 'src/vaultSync/SealFileSync'; -import { FilesFileSyncTable } from 'src/vaultSync/tables/filesTable'; -import { TagsFileSyncTable } from 'src/vaultSync/tables/tagsTable'; -import { TasksFileSyncTable } from 'src/vaultSync/tables/tasksTable'; -import { CSV_VIEW_EXTENSIONS, CSV_VIEW_TYPE, CSVView } from 'src/view/CSVView'; +import { GridRenderer } from './renderer/GridRenderer'; +import { MarkdownRenderer } from './renderer/MarkdownRenderer'; +import { TableRenderer } from './renderer/TableRenderer'; +import { RendererConfig, RendererRegistry } from './renderer/rendererRegistry'; +import { DEFAULT_SETTINGS, SQLSealSettings, SQLSealSettingsTab } from './settings/SQLSealSettingsTab'; +import { SqlSeal } from './sqlSeal'; +import { SealFileSync } from './vaultSync/SealFileSync'; +import { CSV_VIEW_EXTENSIONS, CSV_VIEW_TYPE, CSVView } from './view/CSVView'; import { createSqlSealEditorExtension } from './editorExtension/inlineCodeBlock'; import { ListRenderer } from './renderer/ListRenderer'; import { JSON_VIEW_EXTENSIONS, JSON_VIEW_TYPE, JsonView } from './view/JsonView'; - -const GLOBAL_KEY = 'sqlSealApi' +import { CellParserRegistar } from './cellParser'; +import { registerAPI } from '@vanakat/plugin-api'; +import { SQLSealRegisterApi } from './pluginApi/sqlSealApi'; +import { registerDefaultFunctions } from './utils/ui'; export default class SqlSealPlugin extends Plugin { settings: SQLSealSettings; fileSync: SealFileSync; sqlSeal: SqlSeal; rendererRegistry: RendererRegistry = new RendererRegistry(); + cellParserRegistar: CellParserRegistar; async onload() { await this.loadSettings(); @@ -35,16 +35,21 @@ export default class SqlSealPlugin extends Plugin { await this.registerViews(); await this.registerSQLSealExtensions() - this.rendererRegistry.register('sql-seal-internal-table', new TableRenderer(this.app)) - this.rendererRegistry.register('sql-seal-internal-grid', new GridRenderer(this.app)) - this.rendererRegistry.register('sql-seal-internal-markdown', new MarkdownRenderer(this.app)) - this.rendererRegistry.register('sql-seal-internal-list', new ListRenderer(this.app)) this.registerGlobalApi(); const sqlSeal = new SqlSeal(this.app, false, this.rendererRegistry) // FIXME: set verbose based on the env. this.sqlSeal = sqlSeal await this.sqlSeal.db.connect() + this.cellParserRegistar = new CellParserRegistar(this.sqlSeal.db) + registerDefaultFunctions(this.cellParserRegistar, this.app) + + // REGISTERING VIEWS + + this.rendererRegistry.register('sql-seal-internal-table', new TableRenderer(this.app, this.cellParserRegistar)) + this.rendererRegistry.register('sql-seal-internal-grid', new GridRenderer(this.app, this.cellParserRegistar)) + this.rendererRegistry.register('sql-seal-internal-markdown', new MarkdownRenderer(this.app)) + this.rendererRegistry.register('sql-seal-internal-list', new ListRenderer(this.app, this.cellParserRegistar)) // start syncing when files are loaded this.app.workspace.onLayoutReady(async () => { @@ -158,7 +163,7 @@ export default class SqlSealPlugin extends Plugin { async registerViews() { this.registerView( CSV_VIEW_TYPE, - (leaf) => new CSVView(leaf, this.settings.enableEditing) + (leaf) => new CSVView(leaf, this.settings.enableEditing, this.cellParserRegistar) ); this.registerView( JSON_VIEW_TYPE, @@ -166,26 +171,29 @@ export default class SqlSealPlugin extends Plugin { ) } - registerGlobalApi() { - (window as any)[GLOBAL_KEY] = { - registerRenderer: (uniqueName: string, config: RendererConfig) => { - this.rendererRegistry.register(uniqueName, config) - }, - - unregisterRenderer: (uniqueName: string) => { - this.rendererRegistry.unregister(uniqueName) - } - } + registerSQLSealView(name: string, config: RendererConfig) { + this.rendererRegistry.register(name, config) } - unregisterGlobalApi() { - (window as any)[GLOBAL_KEY] = undefined + unregisterSQLSealView(name: string) { + this.rendererRegistry.unregister(name) + } + + registerSQLSealFunction(name: string, fn: any, argumentsCount: number = 1) { + this.cellParserRegistar.register(name, fn, argumentsCount) + } + + unregisterSQLSealFunction(name: string) { + this.cellParserRegistar.unregister(name) + } + + registerGlobalApi() { + registerAPI('sqlseal', new SQLSealRegisterApi(this), this) } onunload() { this.sqlSeal.db.disconect(); this.unregisterSQLSealExtensions(); - this.unregisterGlobalApi(); } async loadSettings() { diff --git a/src/pluginApi/sqlSealApi.ts b/src/pluginApi/sqlSealApi.ts new file mode 100644 index 0000000..07bb092 --- /dev/null +++ b/src/pluginApi/sqlSealApi.ts @@ -0,0 +1,80 @@ +import { Plugin } from "obsidian" +import SqlSealPlugin from "../main" +import { version } from '../../package.json' +import { RendererConfig } from "../renderer/rendererRegistry"; + +const API_VERSION = 1; + +export class SQLSealRegisterApi { + registeredApis: Array = [] + constructor(private sqlSealPlugin: SqlSealPlugin) { + sqlSealPlugin.register(() => { + this.registeredApis.forEach(p => { + p.unregister() + }) + this.registeredApis = [] + }) + } + + get sqlSealVersion() { + return version + } + + get apiVersion() { + return API_VERSION + } + + registerForPlugin(plugin: Plugin) { + const api = new SQLSealApi(plugin, this.sqlSealPlugin) + this.registeredApis.push(api) + return api + } +} + +interface RegisteredView { + name: string; + viewClass: any; +} + +interface RegisteredFunction { + name: string, + fn: CallableFunction +} + +export class SQLSealApi { + private views: Array = [] + private functions: Array = [] + + constructor(private readonly plugin: Plugin, private sqlSealPlugin: SqlSealPlugin) { + plugin.register(() => { + this.unregister() + }) + } + + registerView(name: string, viewClass: RendererConfig) { + this.views.push({ + name, + viewClass + }) + this.sqlSealPlugin.registerSQLSealView(name, viewClass) + } + + registerCustomFunction(name: string, fn: CallableFunction, argumentsCount: number = 1) { + this.functions.push({ + name, + fn, + }) + this.sqlSealPlugin.registerSQLSealFunction(name, fn, argumentsCount) + } + + unregister() { + for(const view of this.views) { + this.sqlSealPlugin.unregisterSQLSealView(view.name) + } + this.views = [] + + for(const fn of this.functions) { + this.sqlSealPlugin.unregisterSQLSealFunction(fn.name) + } + } +} \ No newline at end of file diff --git a/src/renderer/GridRenderer.ts b/src/renderer/GridRenderer.ts index 275b330..bdbfc5d 100644 --- a/src/renderer/GridRenderer.ts +++ b/src/renderer/GridRenderer.ts @@ -1,9 +1,9 @@ import { createGrid, GridApi, GridOptions, themeQuartz } from "ag-grid-community"; import { merge } from "lodash"; import { App } from "obsidian"; -import { RendererConfig } from "src/renderer/rendererRegistry"; +import { RendererConfig } from "./rendererRegistry"; import { parse } from 'json5' -import { parseCell } from "src/utils/ui"; +import { CellParser } from "../cellParser"; const getCurrentTheme = () => { return document.body.classList.contains('theme-dark') ? 'dark' : 'light'; @@ -24,7 +24,12 @@ const getAgGridTheme = (theme: 'dark' | 'light') => { } class GridRendererCommunicator { - constructor(private el: HTMLElement, private config: Partial, private app: App) { + constructor( + private el: HTMLElement, + private config: Partial, + private app: App, + private cellParser: CellParser + ) { this.initialize() } @@ -62,7 +67,7 @@ class GridRendererCommunicator { resizable: false, cellRendererSelector: () => { return { - component: ({ value }: { value: string }) => parseCell(value, this.app) + component: ({ value }: { value: string }) => this.cellParser.render(value) } }, autoHeight: true @@ -109,7 +114,7 @@ class GridRendererCommunicator { } export class GridRenderer implements RendererConfig { - constructor(private app: App) { } + constructor(private app: App, private readonly cellParser: CellParser) { } get rendererKey() { return 'grid' } @@ -125,7 +130,7 @@ export class GridRenderer implements RendererConfig { render(config: Partial, el: HTMLElement) { - const communicator = new GridRendererCommunicator(el, config, this.app) + const communicator = new GridRendererCommunicator(el, config, this.app, this.cellParser) return { render: (data: any) => { // FIXME: we need to update that. diff --git a/src/renderer/ListRenderer.ts b/src/renderer/ListRenderer.ts index f458134..a31cb7e 100644 --- a/src/renderer/ListRenderer.ts +++ b/src/renderer/ListRenderer.ts @@ -1,7 +1,8 @@ // This is renderer for a very basic List view. import { App } from "obsidian"; -import { RendererConfig } from "src/renderer/rendererRegistry"; -import { displayError, parseCell } from "src/utils/ui"; +import { CellParser } from "../cellParser"; +import { RendererConfig } from "../renderer/rendererRegistry"; +import { displayError } from "../utils/ui"; interface ListRendererConfig { classNames: string[] @@ -9,7 +10,7 @@ interface ListRendererConfig { export class ListRenderer implements RendererConfig { - constructor(private readonly app: App) { } + constructor(private readonly app: App, private readonly cellParser: CellParser) { } get rendererKey() { return 'list' @@ -47,7 +48,7 @@ export class ListRenderer implements RendererConfig { text: createEl('span', { text: c, cls: 'sqlseal-column-name' }) as any, // FIXME: this should be properly typed cls: ['sqlseal-list-element-single'] }) - const val = parseCell(d[c], this.app) + const val = this.cellParser.render(d[c]) el.append(val) el.dataset.sqlsealColumn = c }) diff --git a/src/renderer/MarkdownRenderer.ts b/src/renderer/MarkdownRenderer.ts index fc28f81..7ef6127 100644 --- a/src/renderer/MarkdownRenderer.ts +++ b/src/renderer/MarkdownRenderer.ts @@ -1,8 +1,8 @@ // This is renderer for a very basic Table view. import { getMarkdownTable } from "markdown-table-ts"; import { App } from "obsidian"; -import { RendererConfig } from "src/renderer/rendererRegistry"; -import { displayError, parseCell } from "src/utils/ui"; +import { RendererConfig } from "../renderer/rendererRegistry"; +import { displayError } from "../utils/ui"; const mapDataFromHeaders = (columns: string[], data: Record[]) => { return data.map(d => columns.map(c => String(d[c]))) diff --git a/src/renderer/TableRenderer.ts b/src/renderer/TableRenderer.ts index 53607e9..5da7759 100644 --- a/src/renderer/TableRenderer.ts +++ b/src/renderer/TableRenderer.ts @@ -1,7 +1,8 @@ // This is renderer for a very basic Table view. import { App } from "obsidian"; -import { RendererConfig } from "src/renderer/rendererRegistry"; -import { displayError, parseCell } from "src/utils/ui"; +import { CellParser } from "../cellParser"; +import { RendererConfig } from "../renderer/rendererRegistry"; +import { displayError } from "../utils/ui"; interface HTMLRendererConfig { classNames: string[] @@ -9,7 +10,7 @@ interface HTMLRendererConfig { export class TableRenderer implements RendererConfig { - constructor(private readonly app: App) { } + constructor(private readonly app: App, private cellParser: CellParser) { } get rendererKey() { return 'html' @@ -51,7 +52,7 @@ export class TableRenderer implements RendererConfig { data.forEach((d: any) => { const row = body.createEl("tr") columns.forEach((c: any) => { - row.createEl("td", { text: parseCell(d[c], this.app) as string }) + row.createEl("td", { text: this.cellParser.render(d[c]) as string }) }) }) diff --git a/src/renderer/rendererRegistry.ts b/src/renderer/rendererRegistry.ts index 8544d93..9cc284a 100644 --- a/src/renderer/rendererRegistry.ts +++ b/src/renderer/rendererRegistry.ts @@ -1,3 +1,5 @@ +import { App } from "obsidian"; + export interface DataFormat { data: Record[], columns: string[] diff --git a/src/utils/ui.ts b/src/utils/ui.ts index 5bae428..6a19685 100644 --- a/src/utils/ui.ts +++ b/src/utils/ui.ts @@ -1,56 +1,37 @@ import { App } from "obsidian" +import { CellParserRegistar } from "src/cellParser" export const displayNotice = (el: HTMLElement, text: string) => { el.empty() el.createDiv({ text: text, cls: 'sqlseal-notice' }) } -export const displayError= (el: HTMLElement, text: string) => { +export const displayError = (el: HTMLElement, text: string) => { el.empty() el.createDiv({ text: text, cls: 'sqlseal-error' }) } -export const parseCell = (data: string, app: App) => { - try { - if (data && typeof data === 'string' && data.startsWith('SQLSEALCUSTOM')) { - const parsedData = JSON.parse(data.slice('SQLSEALCUSTOM('.length, -1)) - return renderSqlSealCustomElement(parsedData, app) - } - return data - } catch (e) { - console.error('Error parsing cell with data:', data, e) - return createDiv({ - text: 'Parsing error', - cls: 'sqlseal-parse-error' - }) - } -} - -type SqlSealAnchorElement = { - type: 'link', - href: string, - name: string -} - -type SQLSealImgElement = { - type: 'img', - path?: string, - href: string -} - -type SQLSealCheckboxElement = { - type: 'checkbox', - value: number -} - -type SqlSealCustomElement = SqlSealAnchorElement | SQLSealImgElement | SQLSealCheckboxElement; - const isLinkLocal = (link: string) => !link?.trim().startsWith('http') -const generateLink = (config: SqlSealAnchorElement, app: App) => { - let href = config.href - if (isLinkLocal(config.href)) { - let fileHref = config.href + +const renderCheckbox = (app: App) => ([value]: [string]) => { + const el = createEl('input', { + type: 'checkbox', + attr: { + disabled: true + }, + }) + if (!!value) { + el.checked = true + } + return el +} +const renderLink = (app: App) => ([name, href]: [string, string]) => { + if (!href) { + href = name + } + if (isLinkLocal(href)) { + let fileHref = href if (fileHref.endsWith('.md')) { fileHref = fileHref.slice(0, -3) } @@ -58,41 +39,35 @@ const generateLink = (config: SqlSealAnchorElement, app: App) => { const encodedPath = encodeURIComponent(fileHref); href = `obsidian://open?vault=${vaultName}&file=${encodedPath}`; } - const el = createEl('a', { text: config.name, href: href }) + const el = createEl('a', { text: name, href: href }) return el } -const renderSqlSealCustomElement = (customConfig: SqlSealCustomElement, app: App) => { - switch (customConfig.type) { - case 'link': - return generateLink(customConfig, app) - case 'img': - if (!isLinkLocal(customConfig.href)) { - return createEl('img', { attr: { src: customConfig.href } }); - } - let href = (customConfig.href ?? '').trim() - if (href.startsWith('![[')) { - href = href.slice(3, -2) - } - let parentPath = '' - if (customConfig.path) { - const parent = app.vault.getFileByPath(customConfig.path) - if (parent) { - parentPath = parent.parent?.path ?? '' - } - } - const path = (customConfig?.path ? parentPath + '/' : '') + href - const file = app.vault.getFileByPath(path); - if (!file) { - return 'File does not exist' - } - return createEl('img', { attr: { src: app.vault.getResourcePath(file) } }); - case 'checkbox': - const el = createEl('input', { type: 'checkbox' }) - el.checked = !!customConfig.value - el.disabled = true - return el - default: - return 'Invalid Custom Element' +const renderImage = (app: App) => ([href, path]: [string, string]) => { + if (!isLinkLocal(href)) { + return createEl('img', { attr: { src: href } }); } + href = (href ?? '').trim() + if (href.startsWith('![[')) { + href = href.slice(3, -2) + } + let parentPath = '' + if (path) { + const parent = app.vault.getFileByPath(path) + if (parent) { + parentPath = parent.parent?.path ?? '' + } + } + path = (path ? parentPath + '/' : '') + href + const file = app.vault.getFileByPath(path); + if (!file) { + return 'File does not exist' + } + return createEl('img', { attr: { src: app.vault.getResourcePath(file) } }); +} + +export const registerDefaultFunctions = (registar: CellParserRegistar, app: App) => { + registar.register('checkbox', renderCheckbox(app), 1) + registar.register('a', renderLink(app), 2) + registar.register('img', renderImage(app), 2) } \ No newline at end of file diff --git a/src/vaultSync/tables/abstractFileSyncTable.ts b/src/vaultSync/tables/abstractFileSyncTable.ts index 95a140f..5b0a976 100644 --- a/src/vaultSync/tables/abstractFileSyncTable.ts +++ b/src/vaultSync/tables/abstractFileSyncTable.ts @@ -1,5 +1,5 @@ import { App, TFile } from "obsidian"; -import { SqlSealDatabase } from "src/database/database"; +import { SqlSealDatabase } from "../../database/database"; export abstract class AFileSyncTable { constructor( diff --git a/src/vaultSync/tables/filesTable.ts b/src/vaultSync/tables/filesTable.ts index 1a4debb..595fe63 100644 --- a/src/vaultSync/tables/filesTable.ts +++ b/src/vaultSync/tables/filesTable.ts @@ -39,7 +39,6 @@ export class FilesFileSyncTable extends AFileSyncTable { super(db, app) } async onFileModify(file: TFile): Promise { - console.log('ON FILE MODIFY', file.name) // we need to update the row const frontmatter = extractFrontmatterFromFile(file, this.plugin) const frontmatterWithFileData = fileData(file, frontmatter) diff --git a/src/view/CSVView.ts b/src/view/CSVView.ts index c00dc3e..588b6bc 100644 --- a/src/view/CSVView.ts +++ b/src/view/CSVView.ts @@ -1,9 +1,10 @@ import { WorkspaceLeaf, TextFileView, Menu } from 'obsidian'; import { parse, unparse } from 'papaparse'; -import { DeleteConfirmationModal } from 'src/modal/deleteConfirmationModal'; -import { RenameColumnModal } from 'src/modal/renameColumnModal'; -import { CodeSampleModal } from 'src/modal/showCodeSample'; -import { GridRenderer } from 'src/renderer/GridRenderer'; +import { DeleteConfirmationModal } from '../modal/deleteConfirmationModal'; +import { RenameColumnModal } from '../modal/renameColumnModal'; +import { CodeSampleModal } from '../modal/showCodeSample'; +import { GridRenderer } from '../renderer/GridRenderer'; +import { CellParser } from '../cellParser'; export const CSV_VIEW_TYPE = "csv-viewer" as const; export const CSV_VIEW_EXTENSIONS = ['csv']; @@ -12,7 +13,11 @@ export class CSVView extends TextFileView { private content: string; private table: HTMLTableElement; - constructor(leaf: WorkspaceLeaf, private enableEditing: boolean) { + constructor( + leaf: WorkspaceLeaf, + private enableEditing: boolean, + private cellParser: CellParser + ) { super(leaf); } @@ -161,7 +166,7 @@ export class CSVView extends TextFileView { }) - const grid = new GridRenderer(this.app) + const grid = new GridRenderer(this.app, this.cellParser) const csvView = this; const api = grid.render({ defaultColDef: { diff --git a/src/view/JsonView.ts b/src/view/JsonView.ts index 1c17a30..0bcb98a 100644 --- a/src/view/JsonView.ts +++ b/src/view/JsonView.ts @@ -5,8 +5,8 @@ export const JSON_VIEW_TYPE = "sqlseal-json-viewer"; export const JSON_VIEW_EXTENSIONS = ['json', 'json5']; export class JsonView extends TextFileView { - private content: string; - private table: HTMLTableElement; + private content: string = ''; + private table: HTMLTableElement | undefined = undefined; constructor(leaf: WorkspaceLeaf) { super(leaf); @@ -44,7 +44,7 @@ export class JsonView extends TextFileView { clear(): void { this.content = ''; - this.table.empty(); + this.table?.empty(); } api: any = null; diff --git a/tsconfig.json b/tsconfig.json index f902798..efc5506 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,6 +11,7 @@ "importHelpers": false, "isolatedModules": true, "skipLibCheck": true, + "resolveJsonModule": true, "types": [ "jest" ], diff --git a/types-package/README.md b/types-package/README.md new file mode 100644 index 0000000..d09c3a1 --- /dev/null +++ b/types-package/README.md @@ -0,0 +1,50 @@ +# SQLSeal Types Package +This package helps you type your external plugin that uses SQLSeal internally. + +## Installation +```bash +npm install --save-dev @hypersphere/sqlseal +``` + +## Usage +Example usage from SQLSeal Charts library. + +```typescript +import { Plugin } from 'obsidian'; +import { ChartRenderer } from './chartRenderer'; +import { pluginApi } from '@vanakat/plugin-api'; +import type { SQLSealRegisterApi } from '@hypersphere/sqlseal' + +export default class SQLSealCharts extends Plugin { + + async onload() { + this.registerWithSQLSeal(); + } + + private registerWithSQLSeal() { + const api = pluginApi('sqlseal') as SQLSealRegisterApi + const registar = api.registerForPlugin(this) + registar.registerView('sqlseal-charts', new ChartRenderer(this.app)) + } +} +``` + +Unregistering will be automatically done by SQLSeal when you unload your plugin so no need to implement custom `onunload()` logic. + +## Checking SQLSeal Version +You can check current SQLSeal version by calling: +```typescript +const api = pluginApi('sqlseal') as SQLSealRegisterApi +console.log(api.sqlSealVersion) // i.e. '0.20.0' +``` + +## Checking SQLSeal Version +You can check current SQLSeal version by calling: +```typescript +const api = pluginApi('sqlseal') as SQLSealRegisterApi +if (api.apiVersion >= 2) { + // Targetting API 2+ +} else { + console.log("Plugin is incompatible with SQLSeal installed. Please update SQLSeal to the latest version and try again") +} +``` \ No newline at end of file diff --git a/types-package/package.json b/types-package/package.json new file mode 100644 index 0000000..7ff9b7b --- /dev/null +++ b/types-package/package.json @@ -0,0 +1,25 @@ +{ + "name": "@hypersphere/sqlseal", + "version": "0.21.0", + "description": "", + "types": "dist/types-package/src/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc", + "prepare": "npm run build" + }, + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "typescript": "^5.0.0" + }, + "peerDependencies": { + "typescript": ">=4.0.0" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/types-package/pnpm-lock.yaml b/types-package/pnpm-lock.yaml new file mode 100644 index 0000000..6a63e99 --- /dev/null +++ b/types-package/pnpm-lock.yaml @@ -0,0 +1,24 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + typescript: + specifier: ^5.0.0 + version: 5.7.3 + +packages: + + typescript@5.7.3: + resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} + engines: {node: '>=14.17'} + hasBin: true + +snapshots: + + typescript@5.7.3: {} diff --git a/types-package/src/index.ts b/types-package/src/index.ts new file mode 100644 index 0000000..ef9a365 --- /dev/null +++ b/types-package/src/index.ts @@ -0,0 +1,9 @@ +import type { SQLSealRegisterApi, SQLSealApi } from '../../src/pluginApi/sqlSealApi' +import { RendererConfig } from '../../src/renderer/rendererRegistry' + + +export type { + SQLSealApi, + SQLSealRegisterApi, + RendererConfig +} \ No newline at end of file diff --git a/types-package/tsconfig.json b/types-package/tsconfig.json new file mode 100644 index 0000000..a2ea714 --- /dev/null +++ b/types-package/tsconfig.json @@ -0,0 +1,41 @@ +{ + "compilerOptions": { + "target": "es2018", + "module": "commonjs", + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "sourceMap": true, + "outDir": "./dist", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + // Disable strict checks + "strict": false, + "noImplicitAny": false, + "strictNullChecks": false, + "strictFunctionTypes": false, + "strictBindCallApply": false, + "strictPropertyInitialization": false, + "noImplicitThis": false, + "useUnknownInCatchVariables": false, + // Additional relaxed settings + "skipLibCheck": true, + "noEmitOnError": false, // Generate types even if there are errors + "allowJs": true, // Allow JavaScript files to be compiled + "checkJs": false, // Don't type-check JavaScript files + "allowUnreachableCode": true, + "allowUnusedLabels": true, + "noFallthroughCasesInSwitch": false, + "noImplicitReturns": false, + "noUnusedLocals": false, + "noUnusedParameters": false + }, + "include": [ + "src/**/*", + ], + "exclude": [ + "node_modules", + "dist" + ] +} \ No newline at end of file