From a584a5c2962626fa6a3c08a55b8d53dcf6449958 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Wed, 20 Sep 2023 03:38:55 -0400 Subject: [PATCH 01/28] Port from trilium notes --- .eslintrc | 100 ++ .gitignore | 2 + LICENSE | 201 ++++ package-lock.json | 2506 ++++++++++++++++++++++++++++++++++++++++++ package.json | 21 + scripts/.eslintrc | 6 + scripts/build.ts | 97 ++ src/main/index.ts | 312 ++++++ src/styles/index.css | 731 ++++++++++++ tsconfig.eslint.json | 8 + tsconfig.json | 34 + 11 files changed, 4018 insertions(+) create mode 100644 .eslintrc create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 scripts/.eslintrc create mode 100644 scripts/build.ts create mode 100644 src/main/index.ts create mode 100644 src/styles/index.css create mode 100644 tsconfig.eslint.json create mode 100644 tsconfig.json diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..cf8dd4cc5 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,100 @@ +{ + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended-type-checked", + "plugin:@typescript-eslint/stylistic-type-checked", + "plugin:solid/typescript" + ], + "env": { + "node": true, + "es2020": true, + "browser": true, + "jquery": true + }, + "plugins": ["@typescript-eslint", "solid"], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": ["./tsconfig.eslint.json"], + "tsconfigRootDir": ".", + "ecmaVersion": 2022, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true + } + }, + "rules": { + "accessor-pairs": "error", + "block-spacing": ["error", "never"], + "brace-style": ["error", "stroustrup", {"allowSingleLine": true}], + "curly": ["error", "multi-line", "consistent"], + "dot-location": ["error", "property"], + "dot-notation": "error", + "func-call-spacing": "error", + "handle-callback-err": "error", + "key-spacing": "error", + "keyword-spacing": "error", + "new-cap": ["error", {"newIsCap": true}], + "no-array-constructor": "error", + "no-caller": "error", + "no-console": "error", + "no-duplicate-imports": "error", + "no-else-return": "error", + "no-eval": "error", + "no-floating-decimal": "error", + "no-implied-eval": "error", + "no-iterator": "error", + "no-label-var": "error", + "no-labels": "error", + "no-lone-blocks": "error", + "no-mixed-spaces-and-tabs": "error", + "no-multi-spaces": "error", + "no-multi-str": "error", + "no-new": "error", + "no-new-func": "error", + "no-new-object": "error", + "no-new-wrappers": "error", + "no-octal-escape": "error", + "no-path-concat": "error", + "no-proto": "error", + "no-prototype-builtins": "off", + "no-redeclare": ["error", {"builtinGlobals": true}], + "no-self-compare": "error", + "no-sequences": "error", + "no-shadow": ["warn", {"builtinGlobals": false, "hoist": "functions"}], + "no-tabs": "error", + "no-template-curly-in-string": "error", + "no-throw-literal": "error", + "no-undef": "error", + "no-undef-init": "error", + "no-unmodified-loop-condition": "error", + "no-unneeded-ternary": "error", + "no-useless-call": "error", + "no-useless-computed-key": "error", + "no-useless-constructor": "error", + "no-useless-rename": "error", + "no-var": "error", + "no-whitespace-before-property": "error", + "object-curly-spacing": ["error", "never", {"objectsInObjects": false}], + "object-property-newline": ["error", {"allowAllPropertiesOnSameLine": true}], + "operator-linebreak": ["error", "none", {"overrides": {"?": "before", ":": "before", "&&": "before"}}], + "prefer-const": "error", + "quote-props": ["error", "consistent-as-needed", {"keywords": true}], + "quotes": ["error", "double", {"allowTemplateLiterals": true}], + "rest-spread-spacing": "error", + "semi": "error", + "semi-spacing": "error", + "space-before-blocks": "error", + "space-in-parens": "error", + "space-infix-ops": "error", + "space-unary-ops": ["error", {"words": true, "nonwords": false, "overrides": {"typeof": false}}], + "spaced-comment": ["error", "always", {"exceptions": ["-", "*"]}], + "template-curly-spacing": "error", + "wrap-iife": ["error", "inside"], + "yield-star-spacing": "error", + "yoda": "error" + }, + "globals": { + "api": "readonly", + "NodeJS": "readonly" + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..3ec544c7a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +.env \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..54bf50753 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2506 @@ +{ + "name": "trilium.rocks", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "trilium.rocks", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@digitak/esrun": "^3.2.24", + "@typescript-eslint/eslint-plugin": "^6.7.2", + "@typescript-eslint/parser": "^6.7.2", + "dotenv": "^16.3.1", + "esbuild": "^0.19.3", + "eslint": "^8.49.0", + "trilium-etapi": "^0.1.2", + "typescript": "^5.2.2" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@digitak/esrun": { + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/@digitak/esrun/-/esrun-3.2.24.tgz", + "integrity": "sha512-HvD1eSuZVBaFZpKU/kl2rzDELCAbAnrFO2in855IrX15Zji4sdrekiEQph+eq5W5xjCyc254zx/Bh8RM2216mg==", + "dev": true, + "dependencies": { + "@digitak/grubber": "^3.1.4", + "chokidar": "^3.5.1", + "esbuild": "^0.17.4" + }, + "bin": { + "esrun": "bin.js" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/android-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/android-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/android-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/darwin-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/darwin-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/freebsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/linux-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/linux-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/linux-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/linux-loong64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/linux-mips64el": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/linux-ppc64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/linux-riscv64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/linux-s390x": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/linux-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/netbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/openbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/sunos-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/win32-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/win32-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/@esbuild/win32-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@digitak/esrun/node_modules/esbuild": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" + } + }, + "node_modules/@digitak/grubber": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@digitak/grubber/-/grubber-3.1.4.tgz", + "integrity": "sha512-pqsnp2BUYlDoTXWG34HWgEJse/Eo1okRgNex8IG84wHrJp8h3SakeR5WhB4VxSA2+/D+frNYJ0ch3yXzsfNDoA==", + "dev": true + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.3.tgz", + "integrity": "sha512-Lemgw4io4VZl9GHJmjiBGzQ7ONXRfRPHcUEerndjwiSkbxzrpq0Uggku5MxxrXdwJ+pTj1qyw4jwTu7hkPsgIA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.3.tgz", + "integrity": "sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.3.tgz", + "integrity": "sha512-FKQJKkK5MXcBHoNZMDNUAg1+WcZlV/cuXrWCoGF/TvdRiYS4znA0m5Il5idUwfxrE20bG/vU1Cr5e1AD6IEIjQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.3.tgz", + "integrity": "sha512-kw7e3FXU+VsJSSSl2nMKvACYlwtvZB8RUIeVShIEY6PVnuZ3c9+L9lWB2nWeeKWNNYDdtL19foCQ0ZyUL7nqGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.3.tgz", + "integrity": "sha512-tPfZiwF9rO0jW6Jh9ipi58N5ZLoSjdxXeSrAYypy4psA2Yl1dAMhM71KxVfmjZhJmxRjSnb29YlRXXhh3GqzYw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.3.tgz", + "integrity": "sha512-ERDyjOgYeKe0Vrlr1iLrqTByB026YLPzTytDTz1DRCYM+JI92Dw2dbpRHYmdqn6VBnQ9Bor6J8ZlNwdZdxjlSg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.3.tgz", + "integrity": "sha512-nXesBZ2Ad1qL+Rm3crN7NmEVJ5uvfLFPLJev3x1j3feCQXfAhoYrojC681RhpdOph8NsvKBBwpYZHR7W0ifTTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.3.tgz", + "integrity": "sha512-zr48Cg/8zkzZCzDHNxXO/89bf9e+r4HtzNUPoz4GmgAkF1gFAFmfgOdCbR8zMbzFDGb1FqBBhdXUpcTQRYS1cQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.3.tgz", + "integrity": "sha512-qXvYKmXj8GcJgWq3aGvxL/JG1ZM3UR272SdPU4QSTzD0eymrM7leiZH77pvY3UetCy0k1xuXZ+VPvoJNdtrsWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.3.tgz", + "integrity": "sha512-7XlCKCA0nWcbvYpusARWkFjRQNWNGlt45S+Q18UeS///K6Aw8bB2FKYe9mhVWy/XLShvCweOLZPrnMswIaDXQA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.3.tgz", + "integrity": "sha512-qGTgjweER5xqweiWtUIDl9OKz338EQqCwbS9c2Bh5jgEH19xQ1yhgGPNesugmDFq+UUSDtWgZ264st26b3de8A==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.3.tgz", + "integrity": "sha512-gy1bFskwEyxVMFRNYSvBauDIWNggD6pyxUksc0MV9UOBD138dKTzr8XnM2R4mBsHwVzeuIH8X5JhmNs2Pzrx+A==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.3.tgz", + "integrity": "sha512-UrYLFu62x1MmmIe85rpR3qou92wB9lEXluwMB/STDzPF9k8mi/9UvNsG07Tt9AqwPQXluMQ6bZbTzYt01+Ue5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.3.tgz", + "integrity": "sha512-9E73TfyMCbE+1AwFOg3glnzZ5fBAFK4aawssvuMgCRqCYzE0ylVxxzjEfut8xjmKkR320BEoMui4o/t9KA96gA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.3.tgz", + "integrity": "sha512-LlmsbuBdm1/D66TJ3HW6URY8wO6IlYHf+ChOUz8SUAjVTuaisfuwCOAgcxo3Zsu3BZGxmI7yt//yGOxV+lHcEA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.3.tgz", + "integrity": "sha512-ogV0+GwEmvwg/8ZbsyfkYGaLACBQWDvO0Kkh8LKBGKj9Ru8VM39zssrnu9Sxn1wbapA2qNS6BiLdwJZGouyCwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.3.tgz", + "integrity": "sha512-o1jLNe4uzQv2DKXMlmEzf66Wd8MoIhLNO2nlQBHLtWyh2MitDG7sMpfCO3NTcoTMuqHjfufgUQDFRI5C+xsXQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.3.tgz", + "integrity": "sha512-AZJCnr5CZgZOdhouLcfRdnk9Zv6HbaBxjcyhq0StNcvAdVZJSKIdOiPB9az2zc06ywl0ePYJz60CjdKsQacp5Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.3.tgz", + "integrity": "sha512-Acsujgeqg9InR4glTRvLKGZ+1HMtDm94ehTIHKhJjFpgVzZG9/pIcWW/HA/DoMfEyXmANLDuDZ2sNrWcjq1lxw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.3.tgz", + "integrity": "sha512-FSrAfjVVy7TifFgYgliiJOyYynhQmqgPj15pzLyJk8BUsnlWNwP/IAy6GAiB1LqtoivowRgidZsfpoYLZH586A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.3.tgz", + "integrity": "sha512-xTScXYi12xLOWZ/sc5RBmMN99BcXp/eEf7scUC0oeiRoiT5Vvo9AycuqCp+xdpDyAU+LkrCqEpUS9fCSZF8J3Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.3.tgz", + "integrity": "sha512-FbUN+0ZRXsypPyWE2IwIkVjDkDnJoMJARWOcFZn4KPPli+QnKqF0z1anvfaYe3ev5HFCpRDLLBDHyOALLppWHw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz", + "integrity": "sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", + "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.6.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.3.tgz", + "integrity": "sha512-HksnYH4Ljr4VQgEy2lTStbCKv/P590tmPe5HqOnv9Gprffgv5WXAY+Y5Gqniu0GGqeTCUdBnzC3QSrzPkBkAMA==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.2.tgz", + "integrity": "sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.7.2", + "@typescript-eslint/type-utils": "6.7.2", + "@typescript-eslint/utils": "6.7.2", + "@typescript-eslint/visitor-keys": "6.7.2", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.2.tgz", + "integrity": "sha512-KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.7.2", + "@typescript-eslint/types": "6.7.2", + "@typescript-eslint/typescript-estree": "6.7.2", + "@typescript-eslint/visitor-keys": "6.7.2", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.2.tgz", + "integrity": "sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.2", + "@typescript-eslint/visitor-keys": "6.7.2" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.2.tgz", + "integrity": "sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.7.2", + "@typescript-eslint/utils": "6.7.2", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.2.tgz", + "integrity": "sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.2.tgz", + "integrity": "sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.2", + "@typescript-eslint/visitor-keys": "6.7.2", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.2.tgz", + "integrity": "sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.7.2", + "@typescript-eslint/types": "6.7.2", + "@typescript-eslint/typescript-estree": "6.7.2", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.2.tgz", + "integrity": "sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/centra": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/centra/-/centra-2.6.0.tgz", + "integrity": "sha512-dgh+YleemrT8u85QL11Z6tYhegAs3MMxsaWAq/oXeAmYJ7VxL3SI9TZtnfaEvNDMAPolj25FXIb3S+HCI4wQaQ==", + "dev": true + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.3.tgz", + "integrity": "sha512-UlJ1qUUA2jL2nNib1JTSkifQTcYTroFqRjwCFW4QYEKEsixXD5Tik9xML7zh2gTxkYTBKGHNH9y7txMwVyPbjw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.19.3", + "@esbuild/android-arm64": "0.19.3", + "@esbuild/android-x64": "0.19.3", + "@esbuild/darwin-arm64": "0.19.3", + "@esbuild/darwin-x64": "0.19.3", + "@esbuild/freebsd-arm64": "0.19.3", + "@esbuild/freebsd-x64": "0.19.3", + "@esbuild/linux-arm": "0.19.3", + "@esbuild/linux-arm64": "0.19.3", + "@esbuild/linux-ia32": "0.19.3", + "@esbuild/linux-loong64": "0.19.3", + "@esbuild/linux-mips64el": "0.19.3", + "@esbuild/linux-ppc64": "0.19.3", + "@esbuild/linux-riscv64": "0.19.3", + "@esbuild/linux-s390x": "0.19.3", + "@esbuild/linux-x64": "0.19.3", + "@esbuild/netbsd-x64": "0.19.3", + "@esbuild/openbsd-x64": "0.19.3", + "@esbuild/sunos-x64": "0.19.3", + "@esbuild/win32-arm64": "0.19.3", + "@esbuild/win32-ia32": "0.19.3", + "@esbuild/win32-x64": "0.19.3" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", + "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.49.0", + "@humanwhocodes/config-array": "^0.11.11", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "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.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "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" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", + "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", + "dev": true, + "dependencies": { + "flatted": "^3.2.7", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", + "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/phin": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/phin/-/phin-3.7.0.tgz", + "integrity": "sha512-DqnVNrpYhKGBZppNKprD+UJylMeEKOZxHgPB+ZP6mGzf3uA2uox4Ep9tUm+rUc8WLIdHT3HcAE4X8fhwQA9JKg==", + "dev": true, + "dependencies": { + "centra": "^2.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trilium-etapi": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/trilium-etapi/-/trilium-etapi-0.1.2.tgz", + "integrity": "sha512-es86UOZaUCmpkdH2xNk5GGusJ2UpfxgqFpWdr7y6j7ZxNHS4q9H1mzKlslT6skTKwqUoFvZSV9jfNEqEwgsAvw==", + "dev": true, + "dependencies": { + "@types/node": "^20.5.1", + "phin": "^3.7.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..23d56be20 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "trilium.rocks", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@digitak/esrun": "^3.2.24", + "@typescript-eslint/eslint-plugin": "^6.7.2", + "@typescript-eslint/parser": "^6.7.2", + "dotenv": "^16.3.1", + "esbuild": "^0.19.3", + "eslint": "^8.49.0", + "trilium-etapi": "^0.1.2", + "typescript": "^5.2.2" + } +} diff --git a/scripts/.eslintrc b/scripts/.eslintrc new file mode 100644 index 000000000..2598b25f5 --- /dev/null +++ b/scripts/.eslintrc @@ -0,0 +1,6 @@ +{ + "extends": "../.eslintrc", + "rules": { + "no-console": "off" + } +} \ No newline at end of file diff --git a/scripts/build.ts b/scripts/build.ts new file mode 100644 index 000000000..419b17c5c --- /dev/null +++ b/scripts/build.ts @@ -0,0 +1,97 @@ +import fs from "node:fs"; +import path from "node:path"; +// import {fileURLToPath} from "node:url"; + +import dotenv from "dotenv"; +import tepi from "trilium-etapi"; +import * as esbuild from "esbuild"; + + +// const fileURL = fileURLToPath(import.meta.url); +// let baseDir = path.dirname(fileURL); +// if (fileURL.includes("esrun-")) baseDir = path.join(baseDir, "..", "..", "scripts"); +// const rootDir = path.join(baseDir, ".."); +// console.log(process.env.npm_package_json); +const rootDir = path.dirname(process.env.npm_package_json!); + + +dotenv.config(); +if (process.env.TRILIUM_ETAPI_TOKEN) tepi.token(process.env.TRILIUM_ETAPI_TOKEN); + +const bundleMap = { + "main.js": process.env.JS_NOTE_ID, + "styles.css": process.env.CSS_NOTE_ID +}; + +const triliumPlugin: esbuild.Plugin = { + name: "Trilium", + setup(build) { + build.onEnd(async result => { + if (!result.metafile) return; + + const bundles = Object.keys(result.metafile.outputs); + for (const bundle of bundles) { + const filename = path.basename(bundle); + const noteId = bundleMap[filename as keyof typeof bundleMap]; + if (!noteId) { + console.info(`No note id found for bundle ${bundle}`); + continue; + } + + const bundlePath = path.join(rootDir, bundle); + if (!fs.existsSync(bundlePath)) { + console.error(`Could not find bundle ${bundle}`); + continue; + } + + const contents = fs.readFileSync(bundlePath).toString(); + await tepi.putNoteContentById(noteId, contents); + } + + }); + } +}; + + +const modules = ["main", "styles"]; +const entryPoints: {in: string, out: string}[] = []; +const makeEntry = (mod: string) => ({"in": path.join(rootDir, "src", mod, mod === "styles" ? "index.css" : "index.ts"), "out": mod}); + +const modulesRequested = process.argv.filter(a => a.startsWith("--module=")); +for (const mod of modulesRequested) { + const module = mod?.replace("--module=", "") ?? ""; + if (modules.includes(module)) entryPoints.push(makeEntry(module)); +} + +if (!entryPoints.length) for (const mod of modules) entryPoints.push(makeEntry(mod)); + + +async function runBuild() { + const before = performance.now(); + await esbuild.build({ + entryPoints: entryPoints, + bundle: true, + outdir: path.join(rootDir, "dist"), + format: "cjs", + external: ["fs", "path", "electron", "@electron/remote"], + banner: {js: "const require = mod => {try{return window.require(mod);}catch{return {};}};"}, + target: ["chrome96", "node16"], + loader: { + ".png": "dataurl", + ".gif": "dataurl", + ".woff": "dataurl", + ".woff2": "dataurl", + ".ttf": "dataurl", + ".html": "text", + ".css": "css" + }, + plugins: [triliumPlugin], + logLevel: "info", + metafile: true, + minify: process.argv.includes("--minify") + }); + const after = performance.now(); + console.log(`Build actually took ${(after - before).toFixed(2)}ms`); +} + +runBuild().catch(console.error); diff --git a/src/main/index.ts b/src/main/index.ts new file mode 100644 index 000000000..0abe7bf34 --- /dev/null +++ b/src/main/index.ts @@ -0,0 +1,312 @@ +// https://instance-name/api/notes/vW1cXaYNN7OM/download + +function addHljs() { + const link = document.createElement("link"); + link.rel = "stylesheet"; + link.href = "api/notes/cVaK9ZJwx5Hs/download"; + document.head.append(link); + + const script = document.createElement("script"); + script.src = "api/notes/6PVElIem02b5/download"; + script.addEventListener("load", () => { + hljs.registerAliases(["application-javascript-env-frontend", "application-javascript-env-backend"], {languageName: "javascript"}); + hljs.addPlugin({ + "after:highlight": (result) => { + // Add api global + result.value = result.value.replaceAll(/([^A-Za-z0-9])api\./g, function(match, prefix) { + return `${prefix}api.`; + }); + + // Add jquery global + result.value = result.value.replaceAll(/([^A-Za-z0-9\.])\$\((.+)\)/g, function(match, prefix, variable) { + return `${prefix}$(${variable})`; + }); + } + }) + hljs.highlightAll(); + }); + document.head.append(script); +} + +function fixTableHeaders() { + Array.from(document.querySelectorAll("th")).forEach(el => { + if (!el.textContent.trim()) el.classList.add("empty"); + }); +} + +/*Array.from(document.querySelectorAll("pre code")).forEach(el => { + if (el.className.includes("javascript-env")) el.className = "language-javascript"; +});*/ + +function addLogo() { + const logo = document.createElement("img"); + logo.src = "https://raw.githubusercontent.com/zadam/trilium/master/images/icon-color.svg"; + logo.id = "logo"; + document.querySelector("#menu > p").append(logo); +} + + +function fixActiveLink() { + const active = document.querySelector("#menu strong"); + const link = document.createElement("a"); + link.className = "type-text active"; + link.href = ``; + link.textContent = active.textContent; + active.replaceWith(link); + const id = document.body.dataset.noteId; + link.href = `./${id}`; + + /*fetchNote().then(note => { + link.href = `./${note.noteId}`; + });*/ +} + +function addSideNav() { + const categories = document.querySelectorAll("#menu > ul > li > ul > li"); + for (const cat of categories) cat.classList.add("category"); + const active = document.querySelector("#menu .active"); + const treeToClone = active.closest(".category.submenu-item") ?? active.closest(".submenu-item.hidden"); + if (!treeToClone) return; // probably homepage + const layout = document.querySelector("#layout"); + const sidebar = document.createElement("ul"); + sidebar.id = "sidebar"; + const clone = treeToClone.cloneNode(true); + /*const title = document.createElement("div"); + title.className = "title"; + title.append(clone.querySelector("p > a")); + sidebar.append(title); + sidebar.append(clone.querySelector("ul"));*/ + sidebar.append(clone); + if (sidebar.querySelectorAll("li").length <= 1) return; + layout.prepend(sidebar); +} + + +async function buildBreadcrumbs() { + const main = document.getElementById("main"); + const placeholder = document.createElement("div"); + placeholder.id = "breadcrumbs"; + const pspan = document.createElement("span"); + const plink = document.createElement("a"); + pspan.append(plink); + plink.href = "#"; + plink.textContent = document.getElementById("title").textContent; + placeholder.append(pspan); + main.prepend(placeholder); + + const container = document.createElement("div"); + container.id = "breadcrumbs"; + + // const notePath = []; + + let currentNote, parentId; + do { + currentNote = await fetchNote(parentId); + const span = document.createElement("span"); + const link = document.createElement("a"); + link.className = "type-text"; + link.href = `./${currentNote.noteId}`; + link.textContent = currentNote.title; + // notePath.splice(0, 0, link); + span.append(link); + container.prepend(span) + parentId = currentNote.parentNoteIds[0]; + if (parentId === "_share") { + link.textContent = ""; + const logo = document.createElement("img"); + logo.src = "https://raw.githubusercontent.com/zadam/trilium/master/images/icon-black.svg"; + link.append(logo); + } + } while(parentId !== "_share") + + if (container.children.length === 1) return; + + placeholder.replaceWith(container); + /*let currentNote = await fetchNote(); + let parentId = currentNote.parentNoteIds[0]; + while (parentId !== "_share") { + notePath.splice(0, 0, currentNote.title); + parentId = currentNote.parentNoteIds[0]; + currentNote = await fetchNote(parentId); + }*/ + + // console.log(notePath); +} + + + +function buildBreadcrumbsFromNav() { + const container = document.createElement("ul"); + container.id = "breadcrumbs"; + + const current = document.querySelector("#menu .active"); + const wrap = document.createElement("li"); + wrap.append(current.cloneNode(true)); + container.prepend(wrap); + let next = current.closest("ul"); + while (next) { + const clone = next.previousElementSibling.querySelector("a").cloneNode(true); + const wrap = document.createElement("li"); + wrap.append(clone); + container.prepend(wrap); + next = next.parentElement.closest("ul"); + if (!next) { + clone.textContent = ""; + const logo = document.createElement("img"); + logo.src = "https://raw.githubusercontent.com/zadam/trilium/master/images/icon-black.svg"; + clone.append(logo); + } + } + + // We don't need this at root + if (container.children.length === 1) return; + + const main = document.getElementById("main"); + main.prepend(container); +} + +const submenuBlacklist = ["ZapIU17QNEyU"] +//if (item.innerHTML.includes(submenuBlacklist[0])) item.className += " hidden"; +/*function fixSubMenu() { + const items = document.querySelectorAll("#menu > ul > li"); + for (const item of items) { + const sublist = item.querySelector("ul"); + if (sublist) { + if (sublist.children.length) { + item.className = "submenu"; + } + else { + sublist.remove(); + } + } + } +}*/ + +function fixSubMenu() { + const items = document.querySelectorAll("#menu ul li"); + for (const item of items) { + const sublist = item.querySelector("ul"); + if (sublist) { + if (sublist.children.length) { + const ihtml = item.innerHTML; + for (const bl of submenuBlacklist) { + if (!ihtml.includes(bl)) continue; + item.classList.add("hidden"); + } + item.classList.add("submenu-item"); + sublist.classList.add("submenu"); + if (sublist.querySelector("ul")?.children.length) sublist.classList.add("hasSubmenu"); + } + else { + sublist.remove(); + } + } + } +} + + +function generateTOC() { + const slugify = text => text.toLowerCase().replace(/[^\w]/g, "-"); + const buildItem = (heading) => { + const slug = slugify(heading.textContent); + + const anchor = document.createElement("a"); + anchor.setAttribute("href", `#${slug}`); + anchor.setAttribute("name", slug); + anchor.setAttribute("id", slug); + anchor.textContent = "#"; + + const link = document.createElement("a"); + link.setAttribute("href", `#${slug}`); + link.textContent = heading.textContent; + + heading.append(anchor); + + const li = document.createElement("li"); + li.append(link); + return li; + }; + + const headings = Array.from(document.querySelectorAll("h1, h2, h3, h4, h5, h6")); + const items = headings.map(h => buildItem(h)); + if (headings.length <= 1) return; + + const getNum = el => parseInt(el.tagName.replace("H","").replace("h","")); + + const toc = document.createElement("ul"); + toc.id = "toc"; + const first = headings[1]; + const firstDepth = getNum(first); + + for (let h = 0; h < headings.length; h++) { + const current = headings[h]; + const currentDepth = getNum(current); + if (currentDepth === firstDepth) toc.append(items[h]); + + let nextIndex = h + 1; + if (nextIndex >= headings.length) continue; + + const children = []; + const childDepth = currentDepth + 1; + let nextDepth = getNum(headings[nextIndex]); + while (nextDepth > currentDepth) { + if (nextDepth === childDepth) children.push(nextIndex); + nextIndex++; + if (nextIndex < headings.length) nextDepth = getNum(headings[nextIndex]); + else nextDepth = currentDepth; + } + + if (children.length) { + const ul = document.createElement("ul"); + for (const c of children) ul.append(items[c]); + items[h].append(ul); + } + } + + const sections = headings.slice(1); + const links = toc.querySelectorAll("a"); + function changeLinkState() { + let index = sections.length; + + while(--index && window.scrollY + 50 < sections[index].offsetTop) {} + + links.forEach((link) => link.classList.remove('active')); + links[index].classList.add('active'); + } + + changeLinkState(); + window.addEventListener('scroll', changeLinkState); + + const layout = document.querySelector("#layout"); + layout.classList.add("toc"); + layout.append(toc) +} + +function addExternalLinks() { + const mapping = { + EGFtX8Uw96FQ: "https://github.com/zadam/trilium" + }; + + for (const id in mapping) { + const links = document.querySelectorAll(`a[href*="${id}"]`); + if (!links.length) {console.warn(`Could not find link to note id ${id}`); continue;} + for (const link of links) { + link.href = mapping[id]; + link.target = "_blank"; + link.rel = "noopener noreferrer"; + } + } +} + + + +try{fixActiveLink();} catch(e){console.error(e)} +try{addHljs();} catch(e){console.error(e)} +try{fixTableHeaders();} catch(e){console.error(e)} +// try{addLogo();} catch{} + +try{fixSubMenu();} catch(e){console.error(e)} +try{addSideNav();} catch(e){console.error(e)} +try{buildBreadcrumbsFromNav();} catch(e){console.error(e)} +try{generateTOC();} catch(e){console.error(e)} +try{addExternalLinks();} catch(e){console.error(e)} \ No newline at end of file diff --git a/src/styles/index.css b/src/styles/index.css new file mode 100644 index 000000000..d77fef91c --- /dev/null +++ b/src/styles/index.css @@ -0,0 +1,731 @@ +* {box-sizing: border-box;} + +:root { + --shadow: 0px 24px 32px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 0px 1px rgba(0, 0, 0, 0.04); + --blue: #3E82E5; + --blue-strong: #008CBD; + --gray: gray; + --ck-color-table-caption-text: gray; + --bg-primary: #1E1E1E; + --bg-secondary: #3C3C3C; + --text-primary: #ddd; + --text-heading: #fff; + + --bottom-layer: #040405; + --middle-layer: #0C0D10; + --top-layer: #14151B; + --accent-layer: #20212B; + --accent-color: #3E82E5; + + --container-width: 1400px /*calc(100% - 10%)*/; + --pane-size: 20%; + scroll-padding-top: calc(72px + 1rem); +} + +body { + margin: 0px; + padding: 0px; + background: var(--middle-layer); + font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; + color: var(--text-primary); + margin-bottom: 200px; +} + +a { + text-decoration: none; + color: var(--text-primary); + transition: color 200ms ease; +} + +a:hover { + color: var(--accent-color); +} + +#main a { + color: var(--accent-color); +} + +#main a:hover { + color: var(--text-heading); +} + +/* a[href^="https://"]::after { + font-size: smaller; + content: "\2197"; + vertical-align: top; +} */ + +a[href^="https://"] { + display: inline-flex; + align-items: center; + gap: 6px; +} + +#main a[href^="https://"] { + padding-right: 6px; +} + +a[href^="https://"]::after { + content: ""; + background-color: currentcolor; + mask: url("data:image/svg+xml;utf8,"); + -webkit-mask: url("data:image/svg+xml;utf8,"); + width: 13.5px; + height: 13.5px; + display: inline-flex; +} + +/* pre { + overflow: auto; +} + +img { + width: auto; + height: auto; + max-width: 100%; + max-height: 100%; +} + +iframe { + border: none; + border-radius: 0.25rem; +} + +#layout { + display: flex; + flex-flow: row wrap; + justify-content: center; + min-height: 100vh; + max-width: 960px; +} + +#main { + contain: content; + background-color: var(--top-layer); + padding: 2rem 3rem 2rem 3rem; + box-shadow: 0 2px 10px rgba(0,0,0,.25)!important; + order: 2; +} + +#title { + text-align: center; + margin-block-end: 24px; + font-size: 32px; + line-height: 48px; + letter-spacing: 0.02em; +} */ + +#content { + font-size: 16px; + font-weight: 400; + -webkit-font-smoothing: subpixel-antialiased; + line-height: 1.5; + overflow-wrap: break-word; + word-wrap: break-word; + hyphens: auto; + letter-spacing: 0.02em; +} + +#main h1, +#main h2, +#main h3, +#main h4, +#main h5, +#main h6 { + color: var(--text-heading); + line-height: 36px; + padding: 5px 0px; + letter-spacing: 0.02em; + font-weight: 800; + position: relative; +} + +/* #content h2::before, +#content h3::before, +#content h4::before{ + content: '#'; + color: var(--blue); + position: absolute; + left: -20px; +} */ + +#main h1 a, +#main h2 a, +#main h3 a, +#main h4 a, +#main h5 a, +#main h6 a { + color: var(--blue); + margin-left: 10px; +} + +#content h2 { + font-size: 24px; +} + +#content h3 { + font-size: 20px; +} + +#content h4 { + font-size: 16px; +} + +#content p { + margin-block-start: 12px; + margin-block-end: 12px; + text-align: justify; + overflow: hidden; +} + +#content hr { + height: 1px; +} + +#content blockquote { + margin-block-start: 12px; + margin-block-end: 24px; + margin-inline-start: 32px; + margin-inline-end: 32px; + padding-block-start: 12px; + padding-block-end: 12px; + padding-inline-start: 16px; + padding-inline-end: 16px; + background-color: hsla(0deg, 0%, 0%, 0.054); +} + +#noteClippedFrom { + color: var(--gray); + margin-bottom: 0.5rem; + font-style:italic; +} + +.table { + overflow-wrap: anywhere; +} + +.pdf-view { + width: 100%; + min-height: 40rem; +} + +.type-file > button { + display: block; + margin: 2rem auto; + background-color: var(--blue); + border: none; + color: white; + padding: 1rem 2rem; + text-align: center; + text-decoration: none; + font-size: 1rem; + border-radius: 0.25rem; + box-shadow: var(--shadow); +} + +.mermaid { + text-align: center; +} + +/* Menu */ + +#toggleMenuButton { + display: none; +} + +/* .showMenu #menu { + display: block; +} + +#menu strong{ + color: var(--blue-strong); +} */ + +/* nav.grid ul { + display: flex; + flex-flow: row wrap; + gap: 1rem; +} +nav.list { + line-height: 1.5; +} + +nav ul { + list-style-type: none; + padding: 0 1rem; +} + +#parentLink { + display: none; + color: var(--gray); + position: relative; + left: -3rem; + top: -1rem; +} +#parentLink::before { + content: "@"; +} + +input:disabled { + border-radius: 3px; + background-color: #cacaca; +} + +.ck-content .table table { + border: 0; +} + +.ck-content .table table th { + background: #252526; + word-wrap: normal; + background-clip: padding-box; +} + +.ck-content .table table th.empty { + opacity: 0; + border: 0; +} + +@media screen and (min-width: 60rem) { + #main { + width: 48rem; + margin-block-start: 6rem; + margin-block-end: 6rem; + box-shadow: var(--shadow); + border-radius: 4px 4px 0px 0px; + } + #toggleMenuButton { + right: 1rem; + top: 1rem; + } +} + +@media screen and (max-width: 60rem) { + #main { + width: 100%; + } + #toggleMenuButton { + right: 1rem; + bottom: 1rem; + } +} + + +@media screen and (max-width: 32rem) { + #main { + padding: 4rem 2rem 2rem 2rem; + } + #content h2::before { + left: 1rem; + } + #parentLink { + left: 0rem; + } +} */ + +#menu { + display: flex; + position: fixed; + justify-content: space-between; + align-items: center; + top: 0; + left: 0; + right: 0; + width: inherit; + margin: 0 auto; + overflow: visible; + z-index: 10; + background: var(--bottom-layer); + padding: 8px 16px; +} + +#menu::before { + content: ""; + position: fixed; + left: 0; + right: 0; + background: var(--bottom-layer); + height: 72px; + z-index: -1; +} + +#menu > p > a { + display: flex; + white-space: nowrap; + gap: 10px; + align-items: center; +/* color: var(--text-heading); */ +} + +/* #menu > p > a:hover { + color: var(--accent-color); +} */ + +/* #menu a { + color: var(--text-heading); +} + +#menu a:hover { + color: var(--accent-color); +} */ + +#menu > p > a::before { + content: ""; + display: flex; + background: url("https://raw.githubusercontent.com/zadam/trilium/master/images/icon-color.svg"); + height: 40px; + width: 53px; +} + +/* #logo { + order: 1; + height: 40px; +} */ + +/* #menu > p > strong::before { + content: ""; + display: flex; + background: url("https://notes.cloud.zerebos.com/assets/v0.60.4/images/icon-black.svg"); +} */ + +#menu ul, #sidebar ul { + list-style: none; + padding: 0; + position: relative; +} + +#menu > ul { + margin: 0; + padding: 0; + display: flex; + gap: 30px; +} + +#menu > ul > li { + margin: 0; + padding: 0; + position: relative; +} + + + +#menu > ul > li > p > a { + display: flex; + gap: 10px; + align-items: center; + font-weight: 700; + padding: 16px 0; +} + +/* #menu > ul > li > p > a::after { + content: ''; + border: 4px solid transparent; + border-top: 4px solid white; + display: flex; + align-items: center; + margin-bottom: -8px; +} */ + +#menu > ul > li.submenu-item > p > a::after { + content: ""; + border-color: currentcolor #0000; + border-style: solid; + border-width: .4em .4em 0; + position: relative; + display: flex; + top: 2px; + align-items: center; +/* transform: translateY(-50%); */ +} + +#menu > ul > li.submenu-item.hidden > ul, +#menu > ul > li.submenu-item.hidden > p > a::after{ + display: none; +} + +#menu > ul > li > ul { +/* display: none; */ + opacity: 0; + pointer-events: none; + position: absolute; + top: 40px; + background: #242526; + border-radius: 6px; + min-width: 150px; + right: 50%; + transform: translateX(50%); + padding: 8px; + transition: top 200ms ease, opacity 200ms ease; +} + +#menu > ul > li > ul > li a { + display: flex; + border-radius: 6px; + padding: 3px 6px; + transition: background-color 200ms ease; +} + +#menu > ul > li > ul > li a:hover { + background: rgba(255,255,255,0.05); +} + +#menu > ul > li > ul > li > ul { + display: none; +} + +#menu > ul > li:hover > ul { +/* display: flex; */ + top: 50px; + opacity: 1; + pointer-events: initial; +} + + +body { + display: flex; + justify-content: center; +} + +#layout { + display: flex; + flex-direction: row; + width: var(--container-width); + max-width: var(--container-width); + margin-top: calc(72px + 1rem); + position: relative; + height: 100%; + gap: 16px; +} + +@media screen and (max-width: 1500px) { + #layout { + width: 1300px; + max-width: 1300px; + } +} + +@media screen and (max-width: 1300px) { + #layout { + width: 1100px; + max-width: 1200px; + } +} + +#sidebar { + flex: var(--pane-size); + height: fit-content; + position: sticky; + top: calc(72px + 1rem); +} + +#main { + flex: 100%; +} + +#layout.toc #main, +#sidebar + #main { + flex: calc(100% - var(--pane-size)); +} + +#layout.toc #sidebar + #main { + flex: calc(100% - (2 * var(--pane-size))); +} + +#main { + contain: content; + background-color: var(--top-layer); + padding: 1rem 3rem 2rem 3rem; + box-shadow: 0 2px 10px #00000040; + border-radius: 6px; +} + + + + +#sidebar, #sidebar ul { + list-style: none; + padding: 0; + margin: 0; +} + +#sidebar { +/* padding-right: 20px; */ +} + + + +/* #sidebar .title { + text-transform: uppercase; + text-align: center; + letter-spacing: 5px; + color: #777; + font-weight: 700; + border-bottom: 1px solid #777; +} */ + +#sidebar p { + display: flex; + margin: 0; +} + +#sidebar a { + flex: 1; + padding: 6px 6px 6px 12px; + border-radius: 6px; + transition: color 200ms ease, background-color 200ms ease; +} + +#sidebar a:hover { + background: rgba(255,255,255,0.05); +} + +#sidebar > li > ul > li.submenu-item > p > a { + text-transform: uppercase; + font-weight: 700; + color: #555; +} + +#sidebar li > ul { + margin-top: 5px; +} + +#sidebar > li > ul.hasSubmenu, +#sidebar > li > ul > li.submenu-item + li.submenu-item { + margin-top: 20px; +} + + + + + + + + +/* .ck-content pre {background: none;} */ +.ck-content code { + background-color: var(--accent-layer); + border-radius: 6px; + padding: 3px; +} +.ck-content pre { + background: var(--accent-layer); + border-radius: 6px; + border: 0; + box-shadow: 0 2px 4px #0003; +} + + +#childLinks.list { + display: none; +} + + + +#breadcrumbs { + margin-bottom: 30px; + display: flex; + align-items: center; + list-style: none; + padding: 0; +} + +#breadcrumbs li { + display: flex; + align-items: center; +} + +#breadcrumbs a { + display: flex; + align-items: center; + padding: 5px 10px; + border-radius: 20px; + transition: color 200ms ease, transform 200ms ease, background-color 200ms ease; +} + +#breadcrumbs img { + width: 18px; + filter: invert(100%); +} + +#breadcrumbs li:not(:last-child)::after { + background: url("data:image/svg+xml;utf8,") center; + content: " "; + display: inline-block; + filter: invert(0.64) sepia(0.11) saturate(0) hue-rotate(149deg) brightness(0.99) contrast(0.95); + height: 8px; + margin: 0 8px; + opacity: .5; + width: 8px; +} + +#breadcrumbs li:last-child a, +#breadcrumbs li a:hover { + background: #202127; +} + +#breadcrumbs li a:hover { + transform: translateY(-3px); +} + +#parentLink { + display: none; +} + + + + + +#toc { + flex: var(--pane-size); + position: sticky; + top: calc(72px + 1rem); + height: fit-content; + background: var(--top-layer); + margin: 0; + padding: 16px 16px 16px 32px; + border-radius: 6px; +} + +#toc, #toc ul { + list-style: none; +} + +#toc ul { + padding-left: 16px; +} + +#toc li a { + color: var(--text-heading); + transition: color 200ms ease; +} + +#toc li a:hover, +#toc li a.active { + color: var(--accent-color); +} + +#toc::before { + content: ""; + display: block; + position: absolute; + left: 16px; + top: 20px; + width: 2px; + height: calc(100% - 40px); + background: rgba(255, 255, 255, 0.1); +} + + + + + + + + +#childLinks.grid ul li a { + border: 1px solid rgba(255, 255, 255, 0.1); +} + +#childLinks.grid ul li a:hover { + background: rgba(255, 255, 255, 0.1); +} \ No newline at end of file diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 000000000..912c46deb --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true + }, + "include": ["src/**/*", "scripts/*", "static/*"], +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..662aaadf4 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "noImplicitAny": true, + "noImplicitThis": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "declaration": true, + "strictNullChecks": true, + "moduleResolution": "Node16", + "target": "ES2020", + "rootDir": "src", + "outDir": "lib/cjs", + "module": "Node16", + "jsx": "preserve", + "jsxImportSource": "solid-js", + }, + "include": ["src/**/*"], + "typedocOptions": { + "name": "TriliumETAPI", + "entryPoints": ["src/index.ts"], + "out": "docs", + "includeVersion": true, + "navigationLinks": { + "Trilium": "https://github.com/zadam/trilium" + }, + "sidebarLinks": {}, + "navigation": { + "fullTree": true, + "includeCategories": true, + "includeGroups": false + } + } +} \ No newline at end of file From 568ea271a403fec724ce6b05bb0f226f9ad12a67 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Thu, 21 Sep 2023 03:18:11 -0400 Subject: [PATCH 02/28] Modularize proof of concept --- .gitignore | 2 + package-lock.json | 10 ++ package.json | 4 + src/main/breadcrumbs.ts | 31 ++++ src/main/externallinks.ts | 15 ++ src/main/fixactivelink.ts | 11 ++ src/main/fixtableheaders.ts | 5 + src/main/highlight.ts | 31 ++++ src/main/index.ts | 314 ++---------------------------------- src/main/sidenav.ts | 19 +++ src/main/submenu.ts | 38 +++++ src/main/toc.ts | 76 +++++++++ tsconfig.json | 2 +- 13 files changed, 255 insertions(+), 303 deletions(-) create mode 100644 src/main/breadcrumbs.ts create mode 100644 src/main/externallinks.ts create mode 100644 src/main/fixactivelink.ts create mode 100644 src/main/fixtableheaders.ts create mode 100644 src/main/highlight.ts create mode 100644 src/main/sidenav.ts create mode 100644 src/main/submenu.ts create mode 100644 src/main/toc.ts diff --git a/.gitignore b/.gitignore index 3ec544c7a..01a022b4e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules/ +legacy/ +dist/ .env \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 54bf50753..1415c70da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "dotenv": "^16.3.1", "esbuild": "^0.19.3", "eslint": "^8.49.0", + "highlight.js": "^11.8.0", "trilium-etapi": "^0.1.2", "typescript": "^5.2.2" } @@ -1811,6 +1812,15 @@ "node": ">=8" } }, + "node_modules/highlight.js": { + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.8.0.tgz", + "integrity": "sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", diff --git a/package.json b/package.json index 23d56be20..ffe0518a2 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,9 @@ "description": "", "main": "index.js", "scripts": { + "build": "esrun scripts/build.ts", + "build-main": "esrun scripts/build.ts -- --module=main", + "build-styles": "esrun scripts/build.ts -- --module=styles", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", @@ -15,6 +18,7 @@ "dotenv": "^16.3.1", "esbuild": "^0.19.3", "eslint": "^8.49.0", + "highlight.js": "^11.8.0", "trilium-etapi": "^0.1.2", "typescript": "^5.2.2" } diff --git a/src/main/breadcrumbs.ts b/src/main/breadcrumbs.ts new file mode 100644 index 000000000..01e34c61d --- /dev/null +++ b/src/main/breadcrumbs.ts @@ -0,0 +1,31 @@ +export default function buildBreadcrumbsFromNav() { + const container = document.createElement("ul"); + container.id = "breadcrumbs"; + + const current = document.querySelector("#menu .active"); + if (!current) return; // Something went really wrong + const wrap = document.createElement("li"); + wrap.append(current.cloneNode(true)); + container.prepend(wrap); + let next = current.closest("ul"); + while (next) { + const clone = next?.previousElementSibling?.querySelector("a")?.cloneNode(true); + if (!clone) continue; // This also means something went very wrong + const wrap = document.createElement("li"); + wrap.append(clone); + container.prepend(wrap); + next = next?.parentElement?.closest("ul") ?? null; + if (!next) { + clone.textContent = ""; + const logo = document.createElement("img"); + logo.src = "https://raw.githubusercontent.com/zadam/trilium/master/images/icon-black.svg"; + clone.appendChild(logo); + } + } + + // We don't need this at root + if (container.children.length === 1) return; + + const main = document.getElementById("main"); + main?.prepend(container); +} \ No newline at end of file diff --git a/src/main/externallinks.ts b/src/main/externallinks.ts new file mode 100644 index 000000000..e411412f4 --- /dev/null +++ b/src/main/externallinks.ts @@ -0,0 +1,15 @@ +export default function addExternalLinks() { + const mapping = { + EGFtX8Uw96FQ: "https://github.com/zadam/trilium" + }; + + for (const id in mapping) { + const links = document.querySelectorAll(`a[href*="${id}"]`); + if (!links.length) {console.warn(`Could not find link to note id ${id}`); continue;} + for (const link of links) { + link.href = mapping[id as keyof typeof mapping]; + link.target = "_blank"; + link.rel = "noopener noreferrer"; + } + } +} \ No newline at end of file diff --git a/src/main/fixactivelink.ts b/src/main/fixactivelink.ts new file mode 100644 index 000000000..c4797c401 --- /dev/null +++ b/src/main/fixactivelink.ts @@ -0,0 +1,11 @@ +export default function fixActiveLink() { + const active = document.querySelector("#menu strong"); + if (!active) return; // Something is really wrong + const link = document.createElement("a"); + link.className = "type-text active"; + link.href = ``; + link.textContent = active.textContent; + active.replaceWith(link); + const id = document.body.dataset.noteId; + link.href = `./${id}`; +} \ No newline at end of file diff --git a/src/main/fixtableheaders.ts b/src/main/fixtableheaders.ts new file mode 100644 index 000000000..0dbf61f5d --- /dev/null +++ b/src/main/fixtableheaders.ts @@ -0,0 +1,5 @@ +export default function fixTableHeaders() { + Array.from(document.querySelectorAll("th")).forEach(el => { + if (!el.textContent?.trim()) el.classList.add("empty"); + }); +} \ No newline at end of file diff --git a/src/main/highlight.ts b/src/main/highlight.ts new file mode 100644 index 000000000..38871f021 --- /dev/null +++ b/src/main/highlight.ts @@ -0,0 +1,31 @@ +import {HLJSApi} from "highlight.js"; + +declare const hljs: HLJSApi; + +export default function addHljs() { + const link = document.createElement("link"); + link.rel = "stylesheet"; + link.href = "api/notes/cVaK9ZJwx5Hs/download"; + document.head.append(link); + + const script = document.createElement("script"); + script.src = "api/notes/6PVElIem02b5/download"; + script.addEventListener("load", () => { + hljs.registerAliases(["application-javascript-env-frontend", "application-javascript-env-backend"], {languageName: "javascript"}); + hljs.addPlugin({ + "after:highlight": (result) => { + // Add api global + result.value = result.value.replaceAll(/([^A-Za-z0-9])api\./g, function(match, prefix) { + return `${prefix}api.`; + }); + + // Add jquery global + result.value = result.value.replaceAll(/([^A-Za-z0-9\.])\$\((.+)\)/g, function(match, prefix, variable) { + return `${prefix}$(${variable})`; + }); + } + }) + hljs.highlightAll(); + }); + document.head.append(script); +} \ No newline at end of file diff --git a/src/main/index.ts b/src/main/index.ts index 0abe7bf34..2e92bc03e 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,312 +1,22 @@ +import fixActiveLink from "./fixactivelink"; +import fixTableHeaders from "./fixtableheaders"; +import highlight from "./highlight"; +import buildSidenav from "./sidenav"; +import buildBreadcrumbs from "./breadcrumbs"; +import fixSubMenu from "./submenu"; +import generateTOC from "./toc"; +import addExternalLinks from "./externallinks"; + // https://instance-name/api/notes/vW1cXaYNN7OM/download -function addHljs() { - const link = document.createElement("link"); - link.rel = "stylesheet"; - link.href = "api/notes/cVaK9ZJwx5Hs/download"; - document.head.append(link); - - const script = document.createElement("script"); - script.src = "api/notes/6PVElIem02b5/download"; - script.addEventListener("load", () => { - hljs.registerAliases(["application-javascript-env-frontend", "application-javascript-env-backend"], {languageName: "javascript"}); - hljs.addPlugin({ - "after:highlight": (result) => { - // Add api global - result.value = result.value.replaceAll(/([^A-Za-z0-9])api\./g, function(match, prefix) { - return `${prefix}api.`; - }); - - // Add jquery global - result.value = result.value.replaceAll(/([^A-Za-z0-9\.])\$\((.+)\)/g, function(match, prefix, variable) { - return `${prefix}$(${variable})`; - }); - } - }) - hljs.highlightAll(); - }); - document.head.append(script); -} - -function fixTableHeaders() { - Array.from(document.querySelectorAll("th")).forEach(el => { - if (!el.textContent.trim()) el.classList.add("empty"); - }); -} - -/*Array.from(document.querySelectorAll("pre code")).forEach(el => { - if (el.className.includes("javascript-env")) el.className = "language-javascript"; -});*/ - -function addLogo() { - const logo = document.createElement("img"); - logo.src = "https://raw.githubusercontent.com/zadam/trilium/master/images/icon-color.svg"; - logo.id = "logo"; - document.querySelector("#menu > p").append(logo); -} - - -function fixActiveLink() { - const active = document.querySelector("#menu strong"); - const link = document.createElement("a"); - link.className = "type-text active"; - link.href = ``; - link.textContent = active.textContent; - active.replaceWith(link); - const id = document.body.dataset.noteId; - link.href = `./${id}`; - - /*fetchNote().then(note => { - link.href = `./${note.noteId}`; - });*/ -} - -function addSideNav() { - const categories = document.querySelectorAll("#menu > ul > li > ul > li"); - for (const cat of categories) cat.classList.add("category"); - const active = document.querySelector("#menu .active"); - const treeToClone = active.closest(".category.submenu-item") ?? active.closest(".submenu-item.hidden"); - if (!treeToClone) return; // probably homepage - const layout = document.querySelector("#layout"); - const sidebar = document.createElement("ul"); - sidebar.id = "sidebar"; - const clone = treeToClone.cloneNode(true); - /*const title = document.createElement("div"); - title.className = "title"; - title.append(clone.querySelector("p > a")); - sidebar.append(title); - sidebar.append(clone.querySelector("ul"));*/ - sidebar.append(clone); - if (sidebar.querySelectorAll("li").length <= 1) return; - layout.prepend(sidebar); -} - - -async function buildBreadcrumbs() { - const main = document.getElementById("main"); - const placeholder = document.createElement("div"); - placeholder.id = "breadcrumbs"; - const pspan = document.createElement("span"); - const plink = document.createElement("a"); - pspan.append(plink); - plink.href = "#"; - plink.textContent = document.getElementById("title").textContent; - placeholder.append(pspan); - main.prepend(placeholder); - - const container = document.createElement("div"); - container.id = "breadcrumbs"; - - // const notePath = []; - - let currentNote, parentId; - do { - currentNote = await fetchNote(parentId); - const span = document.createElement("span"); - const link = document.createElement("a"); - link.className = "type-text"; - link.href = `./${currentNote.noteId}`; - link.textContent = currentNote.title; - // notePath.splice(0, 0, link); - span.append(link); - container.prepend(span) - parentId = currentNote.parentNoteIds[0]; - if (parentId === "_share") { - link.textContent = ""; - const logo = document.createElement("img"); - logo.src = "https://raw.githubusercontent.com/zadam/trilium/master/images/icon-black.svg"; - link.append(logo); - } - } while(parentId !== "_share") - - if (container.children.length === 1) return; - - placeholder.replaceWith(container); - /*let currentNote = await fetchNote(); - let parentId = currentNote.parentNoteIds[0]; - while (parentId !== "_share") { - notePath.splice(0, 0, currentNote.title); - parentId = currentNote.parentNoteIds[0]; - currentNote = await fetchNote(parentId); - }*/ - - // console.log(notePath); -} - - - -function buildBreadcrumbsFromNav() { - const container = document.createElement("ul"); - container.id = "breadcrumbs"; - - const current = document.querySelector("#menu .active"); - const wrap = document.createElement("li"); - wrap.append(current.cloneNode(true)); - container.prepend(wrap); - let next = current.closest("ul"); - while (next) { - const clone = next.previousElementSibling.querySelector("a").cloneNode(true); - const wrap = document.createElement("li"); - wrap.append(clone); - container.prepend(wrap); - next = next.parentElement.closest("ul"); - if (!next) { - clone.textContent = ""; - const logo = document.createElement("img"); - logo.src = "https://raw.githubusercontent.com/zadam/trilium/master/images/icon-black.svg"; - clone.append(logo); - } - } - - // We don't need this at root - if (container.children.length === 1) return; - - const main = document.getElementById("main"); - main.prepend(container); -} - -const submenuBlacklist = ["ZapIU17QNEyU"] -//if (item.innerHTML.includes(submenuBlacklist[0])) item.className += " hidden"; -/*function fixSubMenu() { - const items = document.querySelectorAll("#menu > ul > li"); - for (const item of items) { - const sublist = item.querySelector("ul"); - if (sublist) { - if (sublist.children.length) { - item.className = "submenu"; - } - else { - sublist.remove(); - } - } - } -}*/ - -function fixSubMenu() { - const items = document.querySelectorAll("#menu ul li"); - for (const item of items) { - const sublist = item.querySelector("ul"); - if (sublist) { - if (sublist.children.length) { - const ihtml = item.innerHTML; - for (const bl of submenuBlacklist) { - if (!ihtml.includes(bl)) continue; - item.classList.add("hidden"); - } - item.classList.add("submenu-item"); - sublist.classList.add("submenu"); - if (sublist.querySelector("ul")?.children.length) sublist.classList.add("hasSubmenu"); - } - else { - sublist.remove(); - } - } - } -} - - -function generateTOC() { - const slugify = text => text.toLowerCase().replace(/[^\w]/g, "-"); - const buildItem = (heading) => { - const slug = slugify(heading.textContent); - - const anchor = document.createElement("a"); - anchor.setAttribute("href", `#${slug}`); - anchor.setAttribute("name", slug); - anchor.setAttribute("id", slug); - anchor.textContent = "#"; - - const link = document.createElement("a"); - link.setAttribute("href", `#${slug}`); - link.textContent = heading.textContent; - - heading.append(anchor); - - const li = document.createElement("li"); - li.append(link); - return li; - }; - - const headings = Array.from(document.querySelectorAll("h1, h2, h3, h4, h5, h6")); - const items = headings.map(h => buildItem(h)); - if (headings.length <= 1) return; - - const getNum = el => parseInt(el.tagName.replace("H","").replace("h","")); - - const toc = document.createElement("ul"); - toc.id = "toc"; - const first = headings[1]; - const firstDepth = getNum(first); - - for (let h = 0; h < headings.length; h++) { - const current = headings[h]; - const currentDepth = getNum(current); - if (currentDepth === firstDepth) toc.append(items[h]); - - let nextIndex = h + 1; - if (nextIndex >= headings.length) continue; - - const children = []; - const childDepth = currentDepth + 1; - let nextDepth = getNum(headings[nextIndex]); - while (nextDepth > currentDepth) { - if (nextDepth === childDepth) children.push(nextIndex); - nextIndex++; - if (nextIndex < headings.length) nextDepth = getNum(headings[nextIndex]); - else nextDepth = currentDepth; - } - - if (children.length) { - const ul = document.createElement("ul"); - for (const c of children) ul.append(items[c]); - items[h].append(ul); - } - } - - const sections = headings.slice(1); - const links = toc.querySelectorAll("a"); - function changeLinkState() { - let index = sections.length; - - while(--index && window.scrollY + 50 < sections[index].offsetTop) {} - - links.forEach((link) => link.classList.remove('active')); - links[index].classList.add('active'); - } - - changeLinkState(); - window.addEventListener('scroll', changeLinkState); - - const layout = document.querySelector("#layout"); - layout.classList.add("toc"); - layout.append(toc) -} - -function addExternalLinks() { - const mapping = { - EGFtX8Uw96FQ: "https://github.com/zadam/trilium" - }; - - for (const id in mapping) { - const links = document.querySelectorAll(`a[href*="${id}"]`); - if (!links.length) {console.warn(`Could not find link to note id ${id}`); continue;} - for (const link of links) { - link.href = mapping[id]; - link.target = "_blank"; - link.rel = "noopener noreferrer"; - } - } -} - - try{fixActiveLink();} catch(e){console.error(e)} -try{addHljs();} catch(e){console.error(e)} +try{highlight();} catch(e){console.error(e)} try{fixTableHeaders();} catch(e){console.error(e)} // try{addLogo();} catch{} try{fixSubMenu();} catch(e){console.error(e)} -try{addSideNav();} catch(e){console.error(e)} -try{buildBreadcrumbsFromNav();} catch(e){console.error(e)} +try{buildSidenav();} catch(e){console.error(e)} +try{buildBreadcrumbs();} catch(e){console.error(e)} try{generateTOC();} catch(e){console.error(e)} try{addExternalLinks();} catch(e){console.error(e)} \ No newline at end of file diff --git a/src/main/sidenav.ts b/src/main/sidenav.ts new file mode 100644 index 000000000..c8d4c7af1 --- /dev/null +++ b/src/main/sidenav.ts @@ -0,0 +1,19 @@ +export default function addSideNav() { + const categories = document.querySelectorAll("#menu > ul > li > ul > li"); + for (const cat of categories) cat.classList.add("category"); + const active = document.querySelector("#menu .active"); + const treeToClone = active?.closest(".category.submenu-item") ?? active?.closest(".submenu-item.hidden"); + if (!treeToClone) return; // probably homepage + const layout = document.querySelector("#layout"); + const sidebar = document.createElement("ul"); + sidebar.id = "sidebar"; + const clone = treeToClone.cloneNode(true); + /*const title = document.createElement("div"); + title.className = "title"; + title.append(clone.querySelector("p > a")); + sidebar.append(title); + sidebar.append(clone.querySelector("ul"));*/ + sidebar.append(clone); + if (sidebar.querySelectorAll("li").length <= 1) return; + layout?.prepend(sidebar); +} \ No newline at end of file diff --git a/src/main/submenu.ts b/src/main/submenu.ts new file mode 100644 index 000000000..60d9998ca --- /dev/null +++ b/src/main/submenu.ts @@ -0,0 +1,38 @@ +const submenuBlacklist = ["ZapIU17QNEyU"] +//if (item.innerHTML.includes(submenuBlacklist[0])) item.className += " hidden"; +/*function fixSubMenu() { + const items = document.querySelectorAll("#menu > ul > li"); + for (const item of items) { + const sublist = item.querySelector("ul"); + if (sublist) { + if (sublist.children.length) { + item.className = "submenu"; + } + else { + sublist.remove(); + } + } + } +}*/ + +export default function fixSubMenu() { + const items = document.querySelectorAll("#menu ul li"); + for (const item of items) { + const sublist = item.querySelector("ul"); + if (sublist) { + if (sublist.children.length) { + const ihtml = item.innerHTML; + for (const bl of submenuBlacklist) { + if (!ihtml.includes(bl)) continue; + item.classList.add("hidden"); + } + item.classList.add("submenu-item"); + sublist.classList.add("submenu"); + if (sublist.querySelector("ul")?.children.length) sublist.classList.add("hasSubmenu"); + } + else { + sublist.remove(); + } + } + } +} \ No newline at end of file diff --git a/src/main/toc.ts b/src/main/toc.ts new file mode 100644 index 000000000..6b8b346e4 --- /dev/null +++ b/src/main/toc.ts @@ -0,0 +1,76 @@ +export default function generateTOC() { + const slugify = (text: string) => text.toLowerCase().replace(/[^\w]/g, "-"); + const buildItem = (heading: Element) => { + const slug = slugify(heading.textContent ?? ""); + + const anchor = document.createElement("a"); + anchor.setAttribute("href", `#${slug}`); + anchor.setAttribute("name", slug); + anchor.setAttribute("id", slug); + anchor.textContent = "#"; + + const link = document.createElement("a"); + link.setAttribute("href", `#${slug}`); + link.textContent = heading.textContent; + + heading.append(anchor); + + const li = document.createElement("li"); + li.append(link); + return li; + }; + + const headings = Array.from(document.querySelectorAll("h1, h2, h3, h4, h5, h6")); + const items = headings.map(h => buildItem(h)); + if (headings.length <= 1) return; + + const getNum = (el: Element) => parseInt(el.tagName.replace("H","").replace("h","")); + + const toc = document.createElement("ul"); + toc.id = "toc"; + const first = headings[1]; + const firstDepth = getNum(first); + + for (let h = 0; h < headings.length; h++) { + const current = headings[h]; + const currentDepth = getNum(current); + if (currentDepth === firstDepth) toc.append(items[h]); + + let nextIndex = h + 1; + if (nextIndex >= headings.length) continue; + + const children = []; + const childDepth = currentDepth + 1; + let nextDepth = getNum(headings[nextIndex]); + while (nextDepth > currentDepth) { + if (nextDepth === childDepth) children.push(nextIndex); + nextIndex++; + if (nextIndex < headings.length) nextDepth = getNum(headings[nextIndex]); + else nextDepth = currentDepth; + } + + if (children.length) { + const ul = document.createElement("ul"); + for (const c of children) ul.append(items[c]); + items[h].append(ul); + } + } + + const sections = headings.slice(1); + const links = toc.querySelectorAll("a"); + function changeLinkState() { + let index = sections.length; + + while(--index && window.scrollY + 50 < (sections[index] as HTMLElement).offsetTop) {} + + links.forEach((link) => link.classList.remove('active')); + links[index].classList.add('active'); + } + + changeLinkState(); + window.addEventListener('scroll', changeLinkState); + + const layout = document.querySelector("#layout"); + layout?.classList.add("toc"); + layout?.append(toc) +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 662aaadf4..e32c9959d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,7 @@ "declaration": true, "strictNullChecks": true, "moduleResolution": "Node16", - "target": "ES2020", + "target": "ES2021", "rootDir": "src", "outDir": "lib/cjs", "module": "Node16", From 039a5ac2e359d201ab03f4339181f23c458c54c6 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Fri, 22 Sep 2023 23:57:17 -0400 Subject: [PATCH 03/28] Modularize more and add swagger-ui --- .eslintrc | 19 +--- package-lock.json | 7 ++ package.json | 2 + scripts/.eslintrc | 3 + scripts/build.ts | 4 +- src/main/breadcrumbs.ts | 31 ----- src/main/externallinks.ts | 15 --- .../{fixactivelink.ts => fixes/activelink.ts} | 17 ++- src/main/fixes/externallinks.ts | 19 ++++ src/main/fixes/submenu.ts | 62 ++++++++++ src/main/fixes/tableheaders.ts | 10 ++ src/main/fixtableheaders.ts | 5 - src/main/highlight.ts | 31 ----- src/main/index.ts | 87 +++++++++++--- src/main/navigation/breadcrumbs.ts | 50 +++++++++ src/main/navigation/sidenav.ts | 30 +++++ src/main/navigation/toc.ts | 106 ++++++++++++++++++ src/main/other/highlight.ts | 53 +++++++++ src/main/other/swagger.ts | 35 ++++++ src/main/sidenav.ts | 19 ---- src/main/submenu.ts | 38 ------- src/main/toc.ts | 76 ------------- src/styles/index.css | 4 + src/styles/swagger.css | 85 ++++++++++++++ tsconfig.eslint.json | 2 +- tsconfig.json | 22 +--- 26 files changed, 560 insertions(+), 272 deletions(-) delete mode 100644 src/main/breadcrumbs.ts delete mode 100644 src/main/externallinks.ts rename src/main/{fixactivelink.ts => fixes/activelink.ts} (53%) create mode 100644 src/main/fixes/externallinks.ts create mode 100644 src/main/fixes/submenu.ts create mode 100644 src/main/fixes/tableheaders.ts delete mode 100644 src/main/fixtableheaders.ts delete mode 100644 src/main/highlight.ts create mode 100644 src/main/navigation/breadcrumbs.ts create mode 100644 src/main/navigation/sidenav.ts create mode 100644 src/main/navigation/toc.ts create mode 100644 src/main/other/highlight.ts create mode 100644 src/main/other/swagger.ts delete mode 100644 src/main/sidenav.ts delete mode 100644 src/main/submenu.ts delete mode 100644 src/main/toc.ts create mode 100644 src/styles/swagger.css diff --git a/.eslintrc b/.eslintrc index cf8dd4cc5..23653b106 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,25 +2,20 @@ "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended-type-checked", - "plugin:@typescript-eslint/stylistic-type-checked", - "plugin:solid/typescript" + "plugin:@typescript-eslint/stylistic-type-checked" ], "env": { - "node": true, "es2020": true, - "browser": true, - "jquery": true + "browser": true }, - "plugins": ["@typescript-eslint", "solid"], + "plugins": ["@typescript-eslint"], "parser": "@typescript-eslint/parser", + "ignorePatterns": ["legacy/", "dist/"], "parserOptions": { "project": ["./tsconfig.eslint.json"], "tsconfigRootDir": ".", "ecmaVersion": 2022, - "sourceType": "module", - "ecmaFeatures": { - "jsx": true - } + "sourceType": "module" }, "rules": { "accessor-pairs": "error", @@ -92,9 +87,5 @@ "wrap-iife": ["error", "inside"], "yield-star-spacing": "error", "yoda": "error" - }, - "globals": { - "api": "readonly", - "NodeJS": "readonly" } } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1415c70da..f7ce1e157 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "devDependencies": { "@digitak/esrun": "^3.2.24", + "@types/swagger-ui": "^3.52.0", "@typescript-eslint/eslint-plugin": "^6.7.2", "@typescript-eslint/parser": "^6.7.2", "dotenv": "^16.3.1", @@ -935,6 +936,12 @@ "integrity": "sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==", "dev": true }, + "node_modules/@types/swagger-ui": { + "version": "3.52.0", + "resolved": "https://registry.npmjs.org/@types/swagger-ui/-/swagger-ui-3.52.0.tgz", + "integrity": "sha512-SlufixEmh+8CLHNgTfAfCT1icNOF7bXboWabhHr1+hIolqlvfwYJGe7HgRcpI3ChE7HWASmEKLkMu34rxseJjQ==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.7.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.2.tgz", diff --git a/package.json b/package.json index ffe0518a2..5bc733544 100644 --- a/package.json +++ b/package.json @@ -7,12 +7,14 @@ "build": "esrun scripts/build.ts", "build-main": "esrun scripts/build.ts -- --module=main", "build-styles": "esrun scripts/build.ts -- --module=styles", + "dist": "esrun scripts/build.ts -- --minify", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "devDependencies": { "@digitak/esrun": "^3.2.24", + "@types/swagger-ui": "^3.52.0", "@typescript-eslint/eslint-plugin": "^6.7.2", "@typescript-eslint/parser": "^6.7.2", "dotenv": "^16.3.1", diff --git a/scripts/.eslintrc b/scripts/.eslintrc index 2598b25f5..ba1c19726 100644 --- a/scripts/.eslintrc +++ b/scripts/.eslintrc @@ -1,5 +1,8 @@ { "extends": "../.eslintrc", + "env": { + "node": true + }, "rules": { "no-console": "off" } diff --git a/scripts/build.ts b/scripts/build.ts index 419b17c5c..4591c6db3 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -73,9 +73,7 @@ async function runBuild() { bundle: true, outdir: path.join(rootDir, "dist"), format: "cjs", - external: ["fs", "path", "electron", "@electron/remote"], - banner: {js: "const require = mod => {try{return window.require(mod);}catch{return {};}};"}, - target: ["chrome96", "node16"], + target: ["chrome96"], loader: { ".png": "dataurl", ".gif": "dataurl", diff --git a/src/main/breadcrumbs.ts b/src/main/breadcrumbs.ts deleted file mode 100644 index 01e34c61d..000000000 --- a/src/main/breadcrumbs.ts +++ /dev/null @@ -1,31 +0,0 @@ -export default function buildBreadcrumbsFromNav() { - const container = document.createElement("ul"); - container.id = "breadcrumbs"; - - const current = document.querySelector("#menu .active"); - if (!current) return; // Something went really wrong - const wrap = document.createElement("li"); - wrap.append(current.cloneNode(true)); - container.prepend(wrap); - let next = current.closest("ul"); - while (next) { - const clone = next?.previousElementSibling?.querySelector("a")?.cloneNode(true); - if (!clone) continue; // This also means something went very wrong - const wrap = document.createElement("li"); - wrap.append(clone); - container.prepend(wrap); - next = next?.parentElement?.closest("ul") ?? null; - if (!next) { - clone.textContent = ""; - const logo = document.createElement("img"); - logo.src = "https://raw.githubusercontent.com/zadam/trilium/master/images/icon-black.svg"; - clone.appendChild(logo); - } - } - - // We don't need this at root - if (container.children.length === 1) return; - - const main = document.getElementById("main"); - main?.prepend(container); -} \ No newline at end of file diff --git a/src/main/externallinks.ts b/src/main/externallinks.ts deleted file mode 100644 index e411412f4..000000000 --- a/src/main/externallinks.ts +++ /dev/null @@ -1,15 +0,0 @@ -export default function addExternalLinks() { - const mapping = { - EGFtX8Uw96FQ: "https://github.com/zadam/trilium" - }; - - for (const id in mapping) { - const links = document.querySelectorAll(`a[href*="${id}"]`); - if (!links.length) {console.warn(`Could not find link to note id ${id}`); continue;} - for (const link of links) { - link.href = mapping[id as keyof typeof mapping]; - link.target = "_blank"; - link.rel = "noopener noreferrer"; - } - } -} \ No newline at end of file diff --git a/src/main/fixactivelink.ts b/src/main/fixes/activelink.ts similarity index 53% rename from src/main/fixactivelink.ts rename to src/main/fixes/activelink.ts index c4797c401..f991393b0 100644 --- a/src/main/fixactivelink.ts +++ b/src/main/fixes/activelink.ts @@ -1,11 +1,24 @@ +/** + * For some reason Trilium share chooses to have the + * active link be just a rather than a true + * link with a special style. This fixes that and + * turns the back into an actual link + * with the correct note id. + */ export default function fixActiveLink() { const active = document.querySelector("#menu strong"); if (!active) return; // Something is really wrong + + // Currently active note id is stored on body + const id = document.body.dataset.noteId; + + // Create the new link const link = document.createElement("a"); link.className = "type-text active"; link.href = ``; link.textContent = active.textContent; - active.replaceWith(link); - const id = document.body.dataset.noteId; link.href = `./${id}`; + + // Replace the + active.replaceWith(link); } \ No newline at end of file diff --git a/src/main/fixes/externallinks.ts b/src/main/fixes/externallinks.ts new file mode 100644 index 000000000..bdbac9333 --- /dev/null +++ b/src/main/fixes/externallinks.ts @@ -0,0 +1,19 @@ +/** + * This function just lets us map some note links to be external links. + * This was originally designed to link the Trilium GitHub via a note. + */ +export default function addExternalLinks(mapping: Record) { + for (const id in mapping) { + const links = document.querySelectorAll(`a[href*="${id}"]`); + if (!links.length) { + // eslint-disable-next-line no-console + console.warn(`Could not find link to note id ${id}`); + continue; + } + for (const link of links) { + link.href = mapping[id]; + link.target = "_blank"; + link.rel = "noopener noreferrer"; + } + } +} \ No newline at end of file diff --git a/src/main/fixes/submenu.ts b/src/main/fixes/submenu.ts new file mode 100644 index 000000000..82609d6ca --- /dev/null +++ b/src/main/fixes/submenu.ts @@ -0,0 +1,62 @@ +const submenuBlacklist = ["ZapIU17QNEyU"]; +// if (item.innerHTML.includes(submenuBlacklist[0])) item.className += " hidden"; +/* function fixSubMenu() { + const items = document.querySelectorAll("#menu > ul > li"); + for (const item of items) { + const sublist = item.querySelector("ul"); + if (sublist) { + if (sublist.children.length) { + item.className = "submenu"; + } + else { + sublist.remove(); + } + } + } +} */ + +/** + * General premise here is to find all submenus/sublists + * and give them a submenu class. Then any list item + * that contains one of these submenus gets a submenu-item + * class. Additionally, any sublist that itself has a + * sublist is given the class hasSubmenu. + */ +export default function fixSubMenu() { + // Get every list item in the navigation + const items = document.querySelectorAll("#menu ul li"); + for (const item of items) { + const sublist = item.querySelector("ul"); + + // If the list item has no submenu, ignore and move on + if (!sublist) continue; + + // There seems to be some weird edge cases where a + // sublist ul is added but has no list items and + // in trilium the corresponding note has no children + if (!sublist.children.length) { + sublist.remove(); + continue; + } + + // If the submenu is part of our blacklist, then + // give it the hidden class. This is for pages + // that have no subcategories and only a long + // list of subpages. + const ihtml = item.innerHTML; + for (const bl of submenuBlacklist) { + if (!ihtml.includes(bl)) continue; + item.classList.add("hidden"); + } + + // Finally, add the corresponding classes to the elements + item.classList.add("submenu-item"); + sublist.classList.add("submenu"); + + // Currently, this is only used by the sidebar styles to + // adjust margins. TODO: Might be worth investigating a + // different method. + if (sublist.querySelector("ul")?.children.length) sublist.classList.add("hasSubmenu"); + + } +} \ No newline at end of file diff --git a/src/main/fixes/tableheaders.ts b/src/main/fixes/tableheaders.ts new file mode 100644 index 000000000..d67174fe1 --- /dev/null +++ b/src/main/fixes/tableheaders.ts @@ -0,0 +1,10 @@ +/** + * This specifically fixes when you have empty corners + * like on tables with column and row headers + */ +export default function fixTableHeaders() { + const headers = document.querySelectorAll("th"); + for (const header of headers) { + if (!header.textContent?.trim()) header.classList.add("empty"); + } +} \ No newline at end of file diff --git a/src/main/fixtableheaders.ts b/src/main/fixtableheaders.ts deleted file mode 100644 index 0dbf61f5d..000000000 --- a/src/main/fixtableheaders.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default function fixTableHeaders() { - Array.from(document.querySelectorAll("th")).forEach(el => { - if (!el.textContent?.trim()) el.classList.add("empty"); - }); -} \ No newline at end of file diff --git a/src/main/highlight.ts b/src/main/highlight.ts deleted file mode 100644 index 38871f021..000000000 --- a/src/main/highlight.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {HLJSApi} from "highlight.js"; - -declare const hljs: HLJSApi; - -export default function addHljs() { - const link = document.createElement("link"); - link.rel = "stylesheet"; - link.href = "api/notes/cVaK9ZJwx5Hs/download"; - document.head.append(link); - - const script = document.createElement("script"); - script.src = "api/notes/6PVElIem02b5/download"; - script.addEventListener("load", () => { - hljs.registerAliases(["application-javascript-env-frontend", "application-javascript-env-backend"], {languageName: "javascript"}); - hljs.addPlugin({ - "after:highlight": (result) => { - // Add api global - result.value = result.value.replaceAll(/([^A-Za-z0-9])api\./g, function(match, prefix) { - return `${prefix}api.`; - }); - - // Add jquery global - result.value = result.value.replaceAll(/([^A-Za-z0-9\.])\$\((.+)\)/g, function(match, prefix, variable) { - return `${prefix}$(${variable})`; - }); - } - }) - hljs.highlightAll(); - }); - document.head.append(script); -} \ No newline at end of file diff --git a/src/main/index.ts b/src/main/index.ts index 2e92bc03e..08de4d39d 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,22 +1,75 @@ -import fixActiveLink from "./fixactivelink"; -import fixTableHeaders from "./fixtableheaders"; -import highlight from "./highlight"; -import buildSidenav from "./sidenav"; -import buildBreadcrumbs from "./breadcrumbs"; -import fixSubMenu from "./submenu"; -import generateTOC from "./toc"; -import addExternalLinks from "./externallinks"; +/* eslint-disable no-console */ +import fixActiveLink from "./fixes/activelink"; +import fixTableHeaders from "./fixes/tableheaders"; +import highlight from "./other/highlight"; +import buildSidenav from "./navigation/sidenav"; +import buildBreadcrumbs from "./navigation/breadcrumbs"; +import fixSubMenu from "./fixes/submenu"; +import generateTOC from "./navigation/toc"; +import addExternalLinks from "./fixes/externallinks"; +import injectSwagger from "./other/swagger"; // https://instance-name/api/notes/vW1cXaYNN7OM/download +const EXTERNAL_LINKS = { + EGFtX8Uw96FQ: "https://github.com/zadam/trilium" +}; -try{fixActiveLink();} catch(e){console.error(e)} -try{highlight();} catch(e){console.error(e)} -try{fixTableHeaders();} catch(e){console.error(e)} -// try{addLogo();} catch{} +function $try unknown>(func: T, ...args: Parameters) { + try { + func.apply(func, args); + } + catch (e) { + console.error(e); + } +} -try{fixSubMenu();} catch(e){console.error(e)} -try{buildSidenav();} catch(e){console.error(e)} -try{buildBreadcrumbs();} catch(e){console.error(e)} -try{generateTOC();} catch(e){console.error(e)} -try{addExternalLinks();} catch(e){console.error(e)} \ No newline at end of file +// const $try = (func, ...args) => { +// try { +// func.apply() +// } +// }; + +// Perform fixes first +$try(fixActiveLink); +$try(fixTableHeaders); +$try(fixSubMenu); +$try(addExternalLinks, EXTERNAL_LINKS); + +// Now layout changes +$try(buildBreadcrumbs); +$try(buildSidenav); +$try(generateTOC); + +// Finally, other features +$try(highlight); +$try(injectSwagger); + + +// try {fixActiveLink();} +// catch (e) {console.error(e);} + +// try {highlight();} +// catch (e) {console.error(e);} + +// try {fixTableHeaders();} +// catch (e) {console.error(e);} + +// try{addLogo();} +// catch{} + + +// try {fixSubMenu();} +// catch (e) {console.error(e);} + +// try {buildSidenav();} +// catch (e) {console.error(e);} + +// try {buildBreadcrumbs();} +// catch (e) {console.error(e);} + +// try {generateTOC();} +// catch (e) {console.error(e);} + +// try {addExternalLinks(EXTERNAL_LINKS);} +// catch (e) {console.error(e);} diff --git a/src/main/navigation/breadcrumbs.ts b/src/main/navigation/breadcrumbs.ts new file mode 100644 index 000000000..79380c3fd --- /dev/null +++ b/src/main/navigation/breadcrumbs.ts @@ -0,0 +1,50 @@ +/** + * This build breadcrumb-style navigation using the existing + * tree menu generated by Trilium. The concept is to find + * the currently active link (fixed by an earlier script) + * and follow that up to the root part of the menu + */ +export default function buildBreadcrumbsFromNav() { + const container = document.createElement("ul"); + container.id = "breadcrumbs"; + + // Find currently active link + const current = document.querySelector("#menu .active"); + if (!current) return; // Something went really wrong + + // Clone the link and add it to the front of the breadcrumb list + const firstItem = document.createElement("li"); + firstItem.append(current.cloneNode(true)); + container.prepend(firstItem); + + // Walk the sublists upward until the root + let next = current.closest("ul"); + while (next) { + const clone = next?.previousElementSibling?.querySelector("a")?.cloneNode(true); + if (!clone) continue; // This also means something went very wrong + + // Get the parent of the previous and add to front of breadcrumbs + const ancestorItem = document.createElement("li"); + ancestorItem.append(clone); + container.prepend(ancestorItem); + + // Get the next sublist upward + next = next?.parentElement?.closest("ul") ?? null; + + // We are not yet at root, continue + if (next) continue; + + // Since next == null, we are at root, let's ue a pretty logo + clone.textContent = ""; + const logo = document.createElement("img"); + logo.src = "https://raw.githubusercontent.com/zadam/trilium/master/images/icon-black.svg"; + clone.appendChild(logo); + } + + // We don't need this at root + if (container.children.length === 1) return; + + // Add breadcrumb container to the main layout + const main = document.getElementById("main"); + main?.prepend(container); +} \ No newline at end of file diff --git a/src/main/navigation/sidenav.ts b/src/main/navigation/sidenav.ts new file mode 100644 index 000000000..0663155f4 --- /dev/null +++ b/src/main/navigation/sidenav.ts @@ -0,0 +1,30 @@ +/** + * This generates a docs-style sidebar navigation using the Trilium tree + */ +export default function addSideNav() { + // Give all tier 2 list items category class... TODO: should this be done elsewhere? + const categories = document.querySelectorAll("#menu > ul > li > ul > li"); + for (const cat of categories) cat.classList.add("category"); + + // Use the active link as our reference point + const active = document.querySelector("#menu .active"); + + // From the active link, find the nearest category that is also a submenu item + // If there is none, try to grab the nearest hidden submenu item (for non- + // category style pages) + const treeToClone = active?.closest(".category.submenu-item") ?? active?.closest(".submenu-item.hidden"); + if (!treeToClone) return; // If neither exist, 99% chance it's the homepage + + // Clone the found node and add it to the sidebar + const sidebar = document.createElement("ul"); + sidebar.id = "sidebar"; + const clone = treeToClone.cloneNode(true); + sidebar.append(clone); + + // If there's only a single item... probably not worth having a sidebar + if (sidebar.querySelectorAll("li").length <= 1) return; + + // Add the sidebar to the front of the layout container + const layout = document.querySelector("#layout"); + layout?.prepend(sidebar); +} \ No newline at end of file diff --git a/src/main/navigation/toc.ts b/src/main/navigation/toc.ts new file mode 100644 index 000000000..89f46bfa9 --- /dev/null +++ b/src/main/navigation/toc.ts @@ -0,0 +1,106 @@ +const slugify = (text: string) => text.toLowerCase().replace(/[^\w]/g, "-"); + +const getDepth = (el: Element) => parseInt(el.tagName.replace("H","").replace("h","")); + +const buildItem = (heading: Element) => { + const slug = slugify(heading.textContent ?? ""); + + const anchor = document.createElement("a"); + anchor.setAttribute("href", `#${slug}`); + anchor.setAttribute("name", slug); + anchor.setAttribute("id", slug); + anchor.textContent = "#"; + + const link = document.createElement("a"); + link.setAttribute("href", `#${slug}`); + link.textContent = heading.textContent; + + heading.append(anchor); + + const li = document.createElement("li"); + li.append(link); + return li; +}; + +/** + * Generate a ToC from all heading elements in the main content area. + * This should go to full h6 depth and not be too opinionated. It + * does assume a "sensible" structure in that you don't go from + * h2 > h4 > h1 but rather h2 > h3 > h2 so you change by 1 and end + * up at the same level as before. + */ +export default function generateTOC() { + // Get all headings from the page and map them to already built elements + const headings = Array.from(document.querySelectorAll("h1, h2, h3, h4, h5, h6")); + if (headings.length <= 1) return; // But if there are none, let's do nothing + const items = headings.map(h => buildItem(h)); + + // Setup the ToC list + const toc = document.createElement("ul"); + toc.id = "toc"; + + // Get the depth of the first content heading on the page. + // This depth will be used as reference for all other headings. + // headings[0] === the

from Trilium + const firstDepth = getDepth(headings[1]); + + // Loop over ALL headings including the first + for (let h = 0; h < headings.length; h++) { + // Get current heading and determine depth + const current = headings[h]; + const currentDepth = getDepth(current); + + // If it's the same depth as our first heading, add to ToC + if (currentDepth === firstDepth) toc.append(items[h]); + + // If this is the last element then it will have already + // been added as a child or as same depth as first + let nextIndex = h + 1; + if (nextIndex >= headings.length) continue; + + // Time to find all children of this heading + const children = []; + const childDepth = currentDepth + 1; + let depthOfNext = getDepth(headings[nextIndex]); + while (depthOfNext > currentDepth) { + // If it's the expected depth, add as child + if (depthOfNext === childDepth) children.push(nextIndex); + nextIndex++; + + // If the next index is valid, grab the depth for next loop + // TODO: could this be done cleaner with a for loop? + if (nextIndex < headings.length) depthOfNext = getDepth(headings[nextIndex]); + else depthOfNext = currentDepth; // If the index was invalid, break loop + } + + // If this heading had children, add them as children + if (children.length) { + const ul = document.createElement("ul"); + for (const c of children) ul.append(items[c]); + items[h].append(ul); + } + } + + // Setup a moving "active" in the ToC that adjusts with the scroll state + const sections = headings.slice(1); + const links = toc.querySelectorAll("a"); + function changeLinkState() { + let index = sections.length; + + // Work backkwards to find the first matching section + while (--index && window.scrollY + 50 < (sections[index] as HTMLElement).offsetTop) {} // eslint-disable-line no-empty + + // Update the "active" item in ToC + links.forEach((link) => link.classList.remove("active")); + links[index].classList.add("active"); + } + + // Initial render + changeLinkState(); + window.addEventListener("scroll", changeLinkState); + + // Finally, add the ToC to the end of layout. Give the layout a class for adjusting widths. + const layout = document.querySelector("#layout"); + layout?.classList.add("toc"); + layout?.append(toc); +} \ No newline at end of file diff --git a/src/main/other/highlight.ts b/src/main/other/highlight.ts new file mode 100644 index 000000000..7218847ec --- /dev/null +++ b/src/main/other/highlight.ts @@ -0,0 +1,53 @@ +import {HLJSApi, HLJSPlugin} from "highlight.js"; + + +declare const hljs: HLJSApi; + + +// Custom highlight.js plugin to highlight the `api` globals for Trilium +const highlightTriliumApi: HLJSPlugin = { + "after:highlight": (result) => { + result.value = result.value.replaceAll(/([^A-Za-z0-9])api\./g, function(match, prefix) { + return `${prefix}api.`; + }); + } +}; + +// Custom highlight.js plugin to highlight JQuery function usage +const highlightJQuery: HLJSPlugin = { + "after:highlight": (result) => { + result.value = result.value.replaceAll(/([^A-Za-z0-9.])\$\((.+)\)/g, function(match, prefix, variable) { + return `${prefix}$(${variable})`; + }); + // TODO: add highlighting for static calls like $.ajax + } +}; + + +/** + * Let's highlight some codeblocks! + * TODO: check if there are codeblocks on the page before performing this action + */ +export default function addHljs() { + // Add the hightlight.js styles from the child note of this script + // TODO: make this a mapping + const link = document.createElement("link"); + link.rel = "stylesheet"; + link.href = "api/notes/cVaK9ZJwx5Hs/download"; + document.head.append(link); + + // Add the highlight.js script too + // TODO: make this a mappin as well + const script = document.createElement("script"); + script.src = "api/notes/6PVElIem02b5/download"; + script.addEventListener("load", () => { + // This registers the JS Frontend and JS Backend types as javascript aliases for highlighting purposes + hljs.registerAliases(["application-javascript-env-frontend", "application-javascript-env-backend"], {languageName: "javascript"}); + + // Add our custom plugins and highlight all on page + hljs.addPlugin(highlightTriliumApi); + hljs.addPlugin(highlightJQuery); + hljs.highlightAll(); + }); + document.head.append(script); +} \ No newline at end of file diff --git a/src/main/other/swagger.ts b/src/main/other/swagger.ts new file mode 100644 index 000000000..de8fa3f44 --- /dev/null +++ b/src/main/other/swagger.ts @@ -0,0 +1,35 @@ +import SwaggerUI, {SwaggerUIOptions} from "swagger-ui"; + + +declare const SwaggerUIBundle: typeof SwaggerUI; +const opts: SwaggerUIOptions = { + url: "https://raw.githubusercontent.com/zadam/trilium/master/src/etapi/etapi.openapi.yaml" +}; + +/** + * Add swagger to the ETAPI page! + */ +export default function injectSwagger() { + // Check if it's the ETAPI page, otherwise avoid dependency + const noteId = document.body.dataset.noteId; + if (noteId !== "pPIXi0uwF5GX") return; // TODO: make this a param + const main = document.getElementById("main")!; + main.innerHTML = ""; + opts.domNode = main; + // Add the swagger-ui styles from unpkg + // TODO: make this hosted by trilium + const link = document.createElement("link"); + link.rel = "stylesheet"; + link.href = "https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui.css"; + document.head.append(link); + + // Add the swagger-ui script too + // TODO: make this hosted by trilium + const script = document.createElement("script"); + script.src = "https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui-bundle.js"; + script.addEventListener("load", () => { + // This immediately generated the swagger-ui in the main element + SwaggerUIBundle(opts); // eslint-disable-line new-cap + }); + document.head.append(script); +} \ No newline at end of file diff --git a/src/main/sidenav.ts b/src/main/sidenav.ts deleted file mode 100644 index c8d4c7af1..000000000 --- a/src/main/sidenav.ts +++ /dev/null @@ -1,19 +0,0 @@ -export default function addSideNav() { - const categories = document.querySelectorAll("#menu > ul > li > ul > li"); - for (const cat of categories) cat.classList.add("category"); - const active = document.querySelector("#menu .active"); - const treeToClone = active?.closest(".category.submenu-item") ?? active?.closest(".submenu-item.hidden"); - if (!treeToClone) return; // probably homepage - const layout = document.querySelector("#layout"); - const sidebar = document.createElement("ul"); - sidebar.id = "sidebar"; - const clone = treeToClone.cloneNode(true); - /*const title = document.createElement("div"); - title.className = "title"; - title.append(clone.querySelector("p > a")); - sidebar.append(title); - sidebar.append(clone.querySelector("ul"));*/ - sidebar.append(clone); - if (sidebar.querySelectorAll("li").length <= 1) return; - layout?.prepend(sidebar); -} \ No newline at end of file diff --git a/src/main/submenu.ts b/src/main/submenu.ts deleted file mode 100644 index 60d9998ca..000000000 --- a/src/main/submenu.ts +++ /dev/null @@ -1,38 +0,0 @@ -const submenuBlacklist = ["ZapIU17QNEyU"] -//if (item.innerHTML.includes(submenuBlacklist[0])) item.className += " hidden"; -/*function fixSubMenu() { - const items = document.querySelectorAll("#menu > ul > li"); - for (const item of items) { - const sublist = item.querySelector("ul"); - if (sublist) { - if (sublist.children.length) { - item.className = "submenu"; - } - else { - sublist.remove(); - } - } - } -}*/ - -export default function fixSubMenu() { - const items = document.querySelectorAll("#menu ul li"); - for (const item of items) { - const sublist = item.querySelector("ul"); - if (sublist) { - if (sublist.children.length) { - const ihtml = item.innerHTML; - for (const bl of submenuBlacklist) { - if (!ihtml.includes(bl)) continue; - item.classList.add("hidden"); - } - item.classList.add("submenu-item"); - sublist.classList.add("submenu"); - if (sublist.querySelector("ul")?.children.length) sublist.classList.add("hasSubmenu"); - } - else { - sublist.remove(); - } - } - } -} \ No newline at end of file diff --git a/src/main/toc.ts b/src/main/toc.ts deleted file mode 100644 index 6b8b346e4..000000000 --- a/src/main/toc.ts +++ /dev/null @@ -1,76 +0,0 @@ -export default function generateTOC() { - const slugify = (text: string) => text.toLowerCase().replace(/[^\w]/g, "-"); - const buildItem = (heading: Element) => { - const slug = slugify(heading.textContent ?? ""); - - const anchor = document.createElement("a"); - anchor.setAttribute("href", `#${slug}`); - anchor.setAttribute("name", slug); - anchor.setAttribute("id", slug); - anchor.textContent = "#"; - - const link = document.createElement("a"); - link.setAttribute("href", `#${slug}`); - link.textContent = heading.textContent; - - heading.append(anchor); - - const li = document.createElement("li"); - li.append(link); - return li; - }; - - const headings = Array.from(document.querySelectorAll("h1, h2, h3, h4, h5, h6")); - const items = headings.map(h => buildItem(h)); - if (headings.length <= 1) return; - - const getNum = (el: Element) => parseInt(el.tagName.replace("H","").replace("h","")); - - const toc = document.createElement("ul"); - toc.id = "toc"; - const first = headings[1]; - const firstDepth = getNum(first); - - for (let h = 0; h < headings.length; h++) { - const current = headings[h]; - const currentDepth = getNum(current); - if (currentDepth === firstDepth) toc.append(items[h]); - - let nextIndex = h + 1; - if (nextIndex >= headings.length) continue; - - const children = []; - const childDepth = currentDepth + 1; - let nextDepth = getNum(headings[nextIndex]); - while (nextDepth > currentDepth) { - if (nextDepth === childDepth) children.push(nextIndex); - nextIndex++; - if (nextIndex < headings.length) nextDepth = getNum(headings[nextIndex]); - else nextDepth = currentDepth; - } - - if (children.length) { - const ul = document.createElement("ul"); - for (const c of children) ul.append(items[c]); - items[h].append(ul); - } - } - - const sections = headings.slice(1); - const links = toc.querySelectorAll("a"); - function changeLinkState() { - let index = sections.length; - - while(--index && window.scrollY + 50 < (sections[index] as HTMLElement).offsetTop) {} - - links.forEach((link) => link.classList.remove('active')); - links[index].classList.add('active'); - } - - changeLinkState(); - window.addEventListener('scroll', changeLinkState); - - const layout = document.querySelector("#layout"); - layout?.classList.add("toc"); - layout?.append(toc) -} \ No newline at end of file diff --git a/src/styles/index.css b/src/styles/index.css index d77fef91c..2028b06d8 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -1,3 +1,5 @@ +@import "./swagger.css"; + * {box-sizing: border-box;} :root { @@ -8,6 +10,7 @@ --ck-color-table-caption-text: gray; --bg-primary: #1E1E1E; --bg-secondary: #3C3C3C; + --text-muted: #777; --text-primary: #ddd; --text-heading: #fff; @@ -20,6 +23,7 @@ --container-width: 1400px /*calc(100% - 10%)*/; --pane-size: 20%; scroll-padding-top: calc(72px + 1rem); + color-scheme: dark; } body { diff --git a/src/styles/swagger.css b/src/styles/swagger.css new file mode 100644 index 000000000..472a5043f --- /dev/null +++ b/src/styles/swagger.css @@ -0,0 +1,85 @@ +#main .swagger-ui .scheme-container { + background: #20212B; +} + +#main .swagger-ui .opblock .opblock-section-header { + background: rgba(0, 0, 0, 0.2); +} + +#main .swagger-ui .opblock-body pre.microlight { + background: #20212B!important; +} + +#main .swagger-ui, +#main .swagger-ui .info li, +#main .swagger-ui .info p, +#main .swagger-ui .info table, +#main .swagger-ui .opblock-description-wrapper p, +#main .swagger-ui .opblock-external-docs-wrapper p, +#main .swagger-ui .opblock-title_normal p, +#main .swagger-ui .response-col_status, +#main .swagger-ui .response-col_links { + color: var(--text-primary); +} + +#main .swagger-ui .btn, +#main .swagger-ui .model, +#main .swagger-ui .tab li, +#main .swagger-ui table thead tr td, +#main .swagger-ui table thead tr th, +#main .swagger-ui .parameter__type, +#main .swagger-ui .parameter__name, +#main .swagger-ui thead tr td.col_header, +#main .swagger-ui .model-title { + color: var(--text-heading); +} + +#main .swagger-ui .model .property.primitive { + color: var(--text-muted); +} + +#main .swagger-ui .prop-type { + color: var(--accent-color); +} + +#main .swagger-ui svg { + fill: var(--text-heading); +} + + +#main .swagger-ui .model-toggle::after { + filter: invert(100%); +} + +#main .swagger-ui .btn { + border: 2px solid var(--text-heading); + transition: transform 200ms ease; +} + +#main .swagger-ui .btn:hover { + transform: translateY(-5px); +} + +#main .swagger-ui input[disabled], +#main .swagger-ui select[disabled], +#main .swagger-ui textarea[disabled], +#main .swagger-ui select { + background: rgba(0, 0, 0, 0.2); + color: var(--text-primary); + border: 2px solid rgba(0,0,0,0.3); + appearance: auto; +} + + +#main .models > h4 { + margin: 5px 10px; +} + + +#main .swagger-ui .opblock-summary-control { + margin-right: 10px; +} + +#main .swagger-ui .authorization__btn { + padding-left: 0; +} \ No newline at end of file diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index 912c46deb..5e04c9f4f 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -4,5 +4,5 @@ "rootDir": ".", "noEmit": true }, - "include": ["src/**/*", "scripts/*", "static/*"], + "include": ["src/**/*", "scripts/*"], } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index e32c9959d..ef08e0c07 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,30 +5,12 @@ "esModuleInterop": true, "allowSyntheticDefaultImports": true, "resolveJsonModule": true, - "declaration": true, "strictNullChecks": true, "moduleResolution": "Node16", "target": "ES2021", "rootDir": "src", "outDir": "lib/cjs", - "module": "Node16", - "jsx": "preserve", - "jsxImportSource": "solid-js", + "module": "Node16" }, - "include": ["src/**/*"], - "typedocOptions": { - "name": "TriliumETAPI", - "entryPoints": ["src/index.ts"], - "out": "docs", - "includeVersion": true, - "navigationLinks": { - "Trilium": "https://github.com/zadam/trilium" - }, - "sidebarLinks": {}, - "navigation": { - "fullTree": true, - "includeCategories": true, - "includeGroups": false - } - } + "include": ["src/**/*"] } \ No newline at end of file From cb19ed36bc85e3be03d6ea1b616a533e793b1c23 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Sat, 23 Sep 2023 02:56:59 -0400 Subject: [PATCH 04/28] Make mobile compatible --- src/main/index.ts | 9 + src/main/navigation/breadcrumbs.ts | 1 + src/main/other/mobile.ts | 74 +++++ src/styles/breadcrumbs.css | 47 +++ src/styles/externallinks.css | 19 ++ src/styles/index.css | 468 +---------------------------- src/styles/mobile.css | 148 +++++++++ src/styles/navbar.css | 152 ++++++++++ src/styles/sidebar.css | 58 ++++ src/styles/toc.css | 39 +++ 10 files changed, 559 insertions(+), 456 deletions(-) create mode 100644 src/main/other/mobile.ts create mode 100644 src/styles/breadcrumbs.css create mode 100644 src/styles/externallinks.css create mode 100644 src/styles/mobile.css create mode 100644 src/styles/navbar.css create mode 100644 src/styles/sidebar.css create mode 100644 src/styles/toc.css diff --git a/src/main/index.ts b/src/main/index.ts index 08de4d39d..1588c830e 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -8,6 +8,7 @@ import fixSubMenu from "./fixes/submenu"; import generateTOC from "./navigation/toc"; import addExternalLinks from "./fixes/externallinks"; import injectSwagger from "./other/swagger"; +import makeMobileMenu from "./other/mobile"; // https://instance-name/api/notes/vW1cXaYNN7OM/download @@ -30,6 +31,8 @@ function $try unknown>(func: T, ...args: Paramete // } // }; + + // Perform fixes first $try(fixActiveLink); $try(fixTableHeaders); @@ -44,6 +47,12 @@ $try(generateTOC); // Finally, other features $try(highlight); $try(injectSwagger); +$try(makeMobileMenu); + +// mobileMenu.append(document.querySelector("#menu > ul")!); +// mobileMenu.append(document.querySelector("#sidebar")!); + + // try {fixActiveLink();} diff --git a/src/main/navigation/breadcrumbs.ts b/src/main/navigation/breadcrumbs.ts index 79380c3fd..a32353915 100644 --- a/src/main/navigation/breadcrumbs.ts +++ b/src/main/navigation/breadcrumbs.ts @@ -47,4 +47,5 @@ export default function buildBreadcrumbsFromNav() { // Add breadcrumb container to the main layout const main = document.getElementById("main"); main?.prepend(container); + container.scrollLeft = container.scrollWidth - container.clientWidth; } \ No newline at end of file diff --git a/src/main/other/mobile.ts b/src/main/other/mobile.ts new file mode 100644 index 000000000..857ca1268 --- /dev/null +++ b/src/main/other/mobile.ts @@ -0,0 +1,74 @@ +const parseHTML = (html: string, fragment = false) => { + const template = document.createElement("template"); + template.innerHTML = html; + const node = template.content.cloneNode(true); + if (fragment) return node; + return node.childNodes.length > 1 ? node.childNodes : node.childNodes[0]; +}; + +const goBackString = `
  • ↩ Back to Main Menu

  • `; +const menuButtonString = ``; + +const isMobileAgent = /Mobi/.test(navigator.userAgent); +const isMobileSize = window.matchMedia("only screen and (max-width: 760px)").matches; + +// TODO: maybe re-work this to have #sidebar be later than #menu +// then use #menu.expanded ~ #sidebar for showing the sidebar +// that way less JS is involved to make mobile work properly +export default function makeMobileMenu() { + if (!isMobileAgent && !isMobileSize) return; // If nothing indicates mobile, bail out + const menuWrap = document.querySelector("#menu"); + const mainMenu = document.querySelector("#menu > ul"); + if (!menuWrap || !mainMenu) return; // Something went really wrong... + const sidebar = document.querySelector("#sidebar"); + + const toggleMenu = (event: MouseEvent) => { + event.stopPropagation(); // Don't preventDefault to allow links + + const isVisible = menuWrap.classList.contains("expanded"); + + if (isVisible) { + menuWrap.classList.remove("expanded"); + if (sidebar) { + mainMenu.classList.remove("active"); + if (!sidebar.classList.contains("active")) sidebar.classList.add("active"); + } + } + else { + menuWrap.classList.add("expanded"); + } + }; + + const menuButton = parseHTML(menuButtonString) as HTMLButtonElement; + menuButton.addEventListener("click", toggleMenu); + + window.addEventListener("click", e => { + const isVisible = menuWrap.classList.contains("expanded"); + if (!isVisible) return; // This is only for when the menu is open + toggleMenu(e); + }); + + + if (sidebar) { + const goBackButton = parseHTML(goBackString) as HTMLLIElement; + goBackButton.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + mainMenu.classList.add("active"); + sidebar.classList.remove("active"); + }); + + sidebar.prepend(goBackButton); + } + + + if (sidebar) sidebar.classList.add("active"); + else mainMenu.classList.add("active"); + + menuWrap.append(menuButton); + if (sidebar) menuWrap.append(sidebar); +} \ No newline at end of file diff --git a/src/styles/breadcrumbs.css b/src/styles/breadcrumbs.css new file mode 100644 index 000000000..ba56f7ac1 --- /dev/null +++ b/src/styles/breadcrumbs.css @@ -0,0 +1,47 @@ +#breadcrumbs { + margin-bottom: 30px; + display: flex; + align-items: center; + list-style: none; + padding: 0; + overflow-x: auto; +} + +#breadcrumbs li { + display: flex; + align-items: center; +} + +#breadcrumbs a { + display: flex; + align-items: center; + white-space: nowrap; + padding: 5px 10px; + border-radius: 20px; + transition: color 200ms ease, transform 200ms ease, background-color 200ms ease; +} + +#breadcrumbs img { + min-width: 18px; + filter: invert(100%); +} + +#breadcrumbs li:not(:last-child)::after { + background: url("data:image/svg+xml;utf8,") center; + content: " "; + display: inline-block; + filter: invert(0.64) sepia(0.11) saturate(0) hue-rotate(149deg) brightness(0.99) contrast(0.95); + height: 8px; + margin: 0 8px; + opacity: .5; + width: 8px; +} + +#breadcrumbs li:last-child a, +#breadcrumbs li a:hover { + background: #202127; +} + +#breadcrumbs li a:hover { + transform: translateY(-3px); +} \ No newline at end of file diff --git a/src/styles/externallinks.css b/src/styles/externallinks.css new file mode 100644 index 000000000..9beffff9a --- /dev/null +++ b/src/styles/externallinks.css @@ -0,0 +1,19 @@ +a[href^="https://"] { + display: inline-flex; + align-items: center; + gap: 6px; +} + +#main a[href^="https://"] { + padding-right: 6px; +} + +a[href^="https://"]::after { + content: ""; + background-color: currentcolor; + mask: url("data:image/svg+xml;utf8,"); + -webkit-mask: url("data:image/svg+xml;utf8,"); + width: 13.5px; + height: 13.5px; + display: inline-flex; +} \ No newline at end of file diff --git a/src/styles/index.css b/src/styles/index.css index 2028b06d8..6fcf80bcc 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -1,4 +1,12 @@ +@import "./breadcrumbs.css"; +@import "./externallinks.css"; +@import "./navbar.css"; +@import "./sidebar.css"; @import "./swagger.css"; +@import "./toc.css"; + +@import "./mobile.css"; + * {box-sizing: border-box;} @@ -22,6 +30,8 @@ --container-width: 1400px /*calc(100% - 10%)*/; --pane-size: 20%; + + scroll-padding-top: calc(72px + 1rem); color-scheme: dark; } @@ -45,79 +55,8 @@ a:hover { color: var(--accent-color); } -#main a { - color: var(--accent-color); -} -#main a:hover { - color: var(--text-heading); -} -/* a[href^="https://"]::after { - font-size: smaller; - content: "\2197"; - vertical-align: top; -} */ - -a[href^="https://"] { - display: inline-flex; - align-items: center; - gap: 6px; -} - -#main a[href^="https://"] { - padding-right: 6px; -} - -a[href^="https://"]::after { - content: ""; - background-color: currentcolor; - mask: url("data:image/svg+xml;utf8,"); - -webkit-mask: url("data:image/svg+xml;utf8,"); - width: 13.5px; - height: 13.5px; - display: inline-flex; -} - -/* pre { - overflow: auto; -} - -img { - width: auto; - height: auto; - max-width: 100%; - max-height: 100%; -} - -iframe { - border: none; - border-radius: 0.25rem; -} - -#layout { - display: flex; - flex-flow: row wrap; - justify-content: center; - min-height: 100vh; - max-width: 960px; -} - -#main { - contain: content; - background-color: var(--top-layer); - padding: 2rem 3rem 2rem 3rem; - box-shadow: 0 2px 10px rgba(0,0,0,.25)!important; - order: 2; -} - -#title { - text-align: center; - margin-block-end: 24px; - font-size: 32px; - line-height: 48px; - letter-spacing: 0.02em; -} */ #content { font-size: 16px; @@ -237,252 +176,7 @@ iframe { display: none; } -/* .showMenu #menu { - display: block; -} -#menu strong{ - color: var(--blue-strong); -} */ - -/* nav.grid ul { - display: flex; - flex-flow: row wrap; - gap: 1rem; -} -nav.list { - line-height: 1.5; -} - -nav ul { - list-style-type: none; - padding: 0 1rem; -} - -#parentLink { - display: none; - color: var(--gray); - position: relative; - left: -3rem; - top: -1rem; -} -#parentLink::before { - content: "@"; -} - -input:disabled { - border-radius: 3px; - background-color: #cacaca; -} - -.ck-content .table table { - border: 0; -} - -.ck-content .table table th { - background: #252526; - word-wrap: normal; - background-clip: padding-box; -} - -.ck-content .table table th.empty { - opacity: 0; - border: 0; -} - -@media screen and (min-width: 60rem) { - #main { - width: 48rem; - margin-block-start: 6rem; - margin-block-end: 6rem; - box-shadow: var(--shadow); - border-radius: 4px 4px 0px 0px; - } - #toggleMenuButton { - right: 1rem; - top: 1rem; - } -} - -@media screen and (max-width: 60rem) { - #main { - width: 100%; - } - #toggleMenuButton { - right: 1rem; - bottom: 1rem; - } -} - - -@media screen and (max-width: 32rem) { - #main { - padding: 4rem 2rem 2rem 2rem; - } - #content h2::before { - left: 1rem; - } - #parentLink { - left: 0rem; - } -} */ - -#menu { - display: flex; - position: fixed; - justify-content: space-between; - align-items: center; - top: 0; - left: 0; - right: 0; - width: inherit; - margin: 0 auto; - overflow: visible; - z-index: 10; - background: var(--bottom-layer); - padding: 8px 16px; -} - -#menu::before { - content: ""; - position: fixed; - left: 0; - right: 0; - background: var(--bottom-layer); - height: 72px; - z-index: -1; -} - -#menu > p > a { - display: flex; - white-space: nowrap; - gap: 10px; - align-items: center; -/* color: var(--text-heading); */ -} - -/* #menu > p > a:hover { - color: var(--accent-color); -} */ - -/* #menu a { - color: var(--text-heading); -} - -#menu a:hover { - color: var(--accent-color); -} */ - -#menu > p > a::before { - content: ""; - display: flex; - background: url("https://raw.githubusercontent.com/zadam/trilium/master/images/icon-color.svg"); - height: 40px; - width: 53px; -} - -/* #logo { - order: 1; - height: 40px; -} */ - -/* #menu > p > strong::before { - content: ""; - display: flex; - background: url("https://notes.cloud.zerebos.com/assets/v0.60.4/images/icon-black.svg"); -} */ - -#menu ul, #sidebar ul { - list-style: none; - padding: 0; - position: relative; -} - -#menu > ul { - margin: 0; - padding: 0; - display: flex; - gap: 30px; -} - -#menu > ul > li { - margin: 0; - padding: 0; - position: relative; -} - - - -#menu > ul > li > p > a { - display: flex; - gap: 10px; - align-items: center; - font-weight: 700; - padding: 16px 0; -} - -/* #menu > ul > li > p > a::after { - content: ''; - border: 4px solid transparent; - border-top: 4px solid white; - display: flex; - align-items: center; - margin-bottom: -8px; -} */ - -#menu > ul > li.submenu-item > p > a::after { - content: ""; - border-color: currentcolor #0000; - border-style: solid; - border-width: .4em .4em 0; - position: relative; - display: flex; - top: 2px; - align-items: center; -/* transform: translateY(-50%); */ -} - -#menu > ul > li.submenu-item.hidden > ul, -#menu > ul > li.submenu-item.hidden > p > a::after{ - display: none; -} - -#menu > ul > li > ul { -/* display: none; */ - opacity: 0; - pointer-events: none; - position: absolute; - top: 40px; - background: #242526; - border-radius: 6px; - min-width: 150px; - right: 50%; - transform: translateX(50%); - padding: 8px; - transition: top 200ms ease, opacity 200ms ease; -} - -#menu > ul > li > ul > li a { - display: flex; - border-radius: 6px; - padding: 3px 6px; - transition: background-color 200ms ease; -} - -#menu > ul > li > ul > li a:hover { - background: rgba(255,255,255,0.05); -} - -#menu > ul > li > ul > li > ul { - display: none; -} - -#menu > ul > li:hover > ul { -/* display: flex; */ - top: 50px; - opacity: 1; - pointer-events: initial; -} body { @@ -515,12 +209,7 @@ body { } } -#sidebar { - flex: var(--pane-size); - height: fit-content; - position: sticky; - top: calc(72px + 1rem); -} + #main { flex: 100%; @@ -546,57 +235,6 @@ body { -#sidebar, #sidebar ul { - list-style: none; - padding: 0; - margin: 0; -} - -#sidebar { -/* padding-right: 20px; */ -} - - - -/* #sidebar .title { - text-transform: uppercase; - text-align: center; - letter-spacing: 5px; - color: #777; - font-weight: 700; - border-bottom: 1px solid #777; -} */ - -#sidebar p { - display: flex; - margin: 0; -} - -#sidebar a { - flex: 1; - padding: 6px 6px 6px 12px; - border-radius: 6px; - transition: color 200ms ease, background-color 200ms ease; -} - -#sidebar a:hover { - background: rgba(255,255,255,0.05); -} - -#sidebar > li > ul > li.submenu-item > p > a { - text-transform: uppercase; - font-weight: 700; - color: #555; -} - -#sidebar li > ul { - margin-top: 5px; -} - -#sidebar > li > ul.hasSubmenu, -#sidebar > li > ul > li.submenu-item + li.submenu-item { - margin-top: 20px; -} @@ -605,7 +243,7 @@ body { -/* .ck-content pre {background: none;} */ + .ck-content code { background-color: var(--accent-layer); border-radius: 6px; @@ -625,51 +263,7 @@ body { -#breadcrumbs { - margin-bottom: 30px; - display: flex; - align-items: center; - list-style: none; - padding: 0; -} -#breadcrumbs li { - display: flex; - align-items: center; -} - -#breadcrumbs a { - display: flex; - align-items: center; - padding: 5px 10px; - border-radius: 20px; - transition: color 200ms ease, transform 200ms ease, background-color 200ms ease; -} - -#breadcrumbs img { - width: 18px; - filter: invert(100%); -} - -#breadcrumbs li:not(:last-child)::after { - background: url("data:image/svg+xml;utf8,") center; - content: " "; - display: inline-block; - filter: invert(0.64) sepia(0.11) saturate(0) hue-rotate(149deg) brightness(0.99) contrast(0.95); - height: 8px; - margin: 0 8px; - opacity: .5; - width: 8px; -} - -#breadcrumbs li:last-child a, -#breadcrumbs li a:hover { - background: #202127; -} - -#breadcrumbs li a:hover { - transform: translateY(-3px); -} #parentLink { display: none; @@ -679,45 +273,7 @@ body { -#toc { - flex: var(--pane-size); - position: sticky; - top: calc(72px + 1rem); - height: fit-content; - background: var(--top-layer); - margin: 0; - padding: 16px 16px 16px 32px; - border-radius: 6px; -} -#toc, #toc ul { - list-style: none; -} - -#toc ul { - padding-left: 16px; -} - -#toc li a { - color: var(--text-heading); - transition: color 200ms ease; -} - -#toc li a:hover, -#toc li a.active { - color: var(--accent-color); -} - -#toc::before { - content: ""; - display: block; - position: absolute; - left: 16px; - top: 20px; - width: 2px; - height: calc(100% - 40px); - background: rgba(255, 255, 255, 0.1); -} diff --git a/src/styles/mobile.css b/src/styles/mobile.css new file mode 100644 index 000000000..408d677b6 --- /dev/null +++ b/src/styles/mobile.css @@ -0,0 +1,148 @@ +.expand { + display: none +} + +button.expand { + position: relative; + padding: 9px; + margin: 9.5px 0; + border: 0 solid #000; + border-radius: 4px; + background: 0 0; + color: #fff; + -webkit-transition: -webkit-transform .5s; + -moz-transition: transform .5s; + -o-transition: transform .5s; + transition: transform .5s +} + +.expanded button.expand { + background-color: #000; + -webkit-transition: -webkit-transform .5s, background-color .5s; + -moz-transition: transform .5s, background-color .5s; + -o-transition: transform .5s, background-color .5s; + transition: transform .5s, background-color .5s; + -ms-transform: rotate(90deg); + -webkit-transform: rotate(90deg); + transform: rotate(90deg) +} + +.expanded .rectangle { + background: #fff; +} + +.rectangle { + display: block; + width: 24px; + height: 4px; + border-radius: 1px; + background: var(--text-heading); + transition: transform .5s, background-color .5s; +} + +.rectangle + .rectangle { + margin-top: 5px +} + +@media screen and (max-width: 768px) { + #toc { + display: none; + } + + #main { + padding: 1rem; + } + + #layout { + overflow: hidden; + } + + #menu { + width: auto; + } + + .expand { + display: inline-block + } + + #menu::after { + content: ""; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + height: 100%; + width: 100%; + transition: background-color 200ms ease; + pointer-events: none; + } + + #menu.expanded::after { + width: 100%; + background: rgba(0,0,0,0.7); + } + + + + #menu > ul { + position: fixed; + transform: translateX(-100%); + transition: transform 200ms ease; + transform-origin: 0 0; + z-index: 1; + background: #040405; + display: initial; + top: 0; + left: 0; + bottom: 0; + height: 100%; + + width: 70%; + padding: 1rem; + overflow: auto; + } + + #menu #go-back { + margin-bottom: 20px; + } + + #menu.expanded > ul.active { + transform: translateX(0); + } + + #menu #sidebar .category > p > a { + font-weight: 400; + } + + #menu > ul > li > p > a { + padding: 3px 6px; + } + + #menu > ul > li > ul { + background: none; + opacity: 1; + position: initial; + transform: none; + margin-top: 0; + pointer-events: initial; + padding: 0; + } + + #menu > ul > li.submenu-item > p > a::after { + display: none; + } + + #menu > ul > li > ul > li > ul { + display: initial; + } + + + #menu > ul:not(#sidebar) > li > ul > li > ul { + display: none; + } + + #menu > ul:not(#sidebar) ul { + margin-left: 20px; + } +} \ No newline at end of file diff --git a/src/styles/navbar.css b/src/styles/navbar.css new file mode 100644 index 000000000..a2685fb47 --- /dev/null +++ b/src/styles/navbar.css @@ -0,0 +1,152 @@ +#menu { + display: flex; + position: fixed; + justify-content: space-between; + align-items: center; + top: 0; + left: 0; + right: 0; + width: inherit; + margin: 0 auto; + overflow: visible; + z-index: 10; + background: var(--bottom-layer); + padding: 8px 16px; +} + +#menu::before { + content: ""; + position: fixed; + left: 0; + right: 0; + background: var(--bottom-layer); + height: 72px; + z-index: -1; +} + +#menu > p > a { + display: flex; + white-space: nowrap; + gap: 10px; + align-items: center; +/* color: var(--text-heading); */ +} + +#main a { + color: var(--accent-color); +} + +#main a:hover { + color: var(--text-heading); +} + +#menu > p > a::before { + content: ""; + display: flex; + background: url("https://raw.githubusercontent.com/zadam/trilium/master/images/icon-color.svg"); + height: 40px; + width: 53px; +} + +/* #logo { + order: 1; + height: 40px; +} */ + +/* #menu > p > strong::before { + content: ""; + display: flex; + background: url("https://notes.cloud.zerebos.com/assets/v0.60.4/images/icon-black.svg"); +} */ + +#menu ul, #sidebar ul { + list-style: none; + padding: 0; + position: relative; +} + +#menu > ul { + margin: 0; + padding: 0; + display: flex; + gap: 30px; +} + +#menu > ul > li { + margin: 0; + padding: 0; + position: relative; +} + + + +#menu > ul > li > p > a { + display: flex; + gap: 10px; + align-items: center; + font-weight: 700; + padding: 16px 0; +} + +/* #menu > ul > li > p > a::after { + content: ''; + border: 4px solid transparent; + border-top: 4px solid white; + display: flex; + align-items: center; + margin-bottom: -8px; +} */ + +#menu > ul > li.submenu-item > p > a::after { + content: ""; + border-color: currentcolor #0000; + border-style: solid; + border-width: .4em .4em 0; + position: relative; + display: flex; + top: 2px; + align-items: center; +/* transform: translateY(-50%); */ +} + +#menu > ul > li.submenu-item.hidden > ul, +#menu > ul > li.submenu-item.hidden > p > a::after{ + display: none; +} + +#menu > ul > li > ul { +/* display: none; */ + opacity: 0; + pointer-events: none; + position: absolute; + top: 40px; + background: #242526; + border-radius: 6px; + min-width: 150px; + right: 50%; + transform: translateX(50%); + padding: 8px; + transition: top 200ms ease, opacity 200ms ease; +} + +#menu > ul > li > ul > li a { + display: flex; + border-radius: 6px; + padding: 3px 6px; + transition: background-color 200ms ease; +} + +#menu > ul > li > ul > li a:hover { + background: rgba(255,255,255,0.05); +} + +#menu > ul > li > ul > li > ul { + display: none; +} + +#menu > ul > li:hover > ul { +/* display: flex; */ + top: 50px; + opacity: 1; + pointer-events: initial; +} \ No newline at end of file diff --git a/src/styles/sidebar.css b/src/styles/sidebar.css new file mode 100644 index 000000000..3a2b099ba --- /dev/null +++ b/src/styles/sidebar.css @@ -0,0 +1,58 @@ +#sidebar { + flex: var(--pane-size); + height: fit-content; + position: sticky; + top: calc(72px + 1rem); +} + +#sidebar, #sidebar ul { + list-style: none; + padding: 0; + margin: 0; +} + +#sidebar { +/* padding-right: 20px; */ +} + + + +/* #sidebar .title { + text-transform: uppercase; + text-align: center; + letter-spacing: 5px; + color: #777; + font-weight: 700; + border-bottom: 1px solid #777; +} */ + +#sidebar p { + display: flex; + margin: 0; +} + +#sidebar a { + flex: 1; + padding: 6px 6px 6px 12px; + border-radius: 6px; + transition: color 200ms ease, background-color 200ms ease; +} + +#sidebar a:hover { + background: rgba(255,255,255,0.05); +} + +#sidebar > li > ul > li.submenu-item > p > a { + text-transform: uppercase; + font-weight: 700; + color: #555; +} + +#sidebar li > ul { + margin-top: 5px; +} + +#sidebar > li > ul.hasSubmenu, +#sidebar > li > ul > li.submenu-item + li.submenu-item { + margin-top: 20px; +} \ No newline at end of file diff --git a/src/styles/toc.css b/src/styles/toc.css new file mode 100644 index 000000000..6ae6962c4 --- /dev/null +++ b/src/styles/toc.css @@ -0,0 +1,39 @@ +#toc { + flex: var(--pane-size); + position: sticky; + top: calc(72px + 1rem); + height: fit-content; + background: var(--top-layer); + margin: 0; + padding: 16px 16px 16px 32px; + border-radius: 6px; +} + +#toc, #toc ul { + list-style: none; +} + +#toc ul { + padding-left: 16px; +} + +#toc li a { + color: var(--text-heading); + transition: color 200ms ease; +} + +#toc li a:hover, +#toc li a.active { + color: var(--accent-color); +} + +#toc::before { + content: ""; + display: block; + position: absolute; + left: 16px; + top: 20px; + width: 2px; + height: calc(100% - 40px); + background: rgba(255, 255, 255, 0.1); +} \ No newline at end of file From 34fa9a1f01d77ee81ddfed5f31bf86b4c5cac6d1 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Sat, 23 Sep 2023 07:21:38 -0400 Subject: [PATCH 05/28] Some final touches --- src/main/fixes/activelink.ts | 9 ++-- src/main/fixes/submenu.ts | 19 +------- src/main/index.ts | 75 +++++++++++------------------- src/main/navigation/breadcrumbs.ts | 2 + src/main/other/highlight.ts | 4 +- src/main/other/opengraph.ts | 40 ++++++++++++++++ src/main/other/swagger.ts | 4 +- src/styles/breadcrumbs.css | 5 +- 8 files changed, 83 insertions(+), 75 deletions(-) create mode 100644 src/main/other/opengraph.ts diff --git a/src/main/fixes/activelink.ts b/src/main/fixes/activelink.ts index f991393b0..1f9506173 100644 --- a/src/main/fixes/activelink.ts +++ b/src/main/fixes/activelink.ts @@ -4,20 +4,23 @@ * link with a special style. This fixes that and * turns the back into an actual link * with the correct note id. + * + * TODO: make it fix aliases too */ -export default function fixActiveLink() { +export default function fixActiveLink(aliases: Record) { const active = document.querySelector("#menu strong"); if (!active) return; // Something is really wrong // Currently active note id is stored on body - const id = document.body.dataset.noteId; + const id = document.body.dataset.noteId!; + const href = aliases[id] ?? id; // Create the new link const link = document.createElement("a"); link.className = "type-text active"; link.href = ``; link.textContent = active.textContent; - link.href = `./${id}`; + link.href = `./${href}`; // Replace the active.replaceWith(link); diff --git a/src/main/fixes/submenu.ts b/src/main/fixes/submenu.ts index 82609d6ca..6b167769b 100644 --- a/src/main/fixes/submenu.ts +++ b/src/main/fixes/submenu.ts @@ -1,20 +1,3 @@ -const submenuBlacklist = ["ZapIU17QNEyU"]; -// if (item.innerHTML.includes(submenuBlacklist[0])) item.className += " hidden"; -/* function fixSubMenu() { - const items = document.querySelectorAll("#menu > ul > li"); - for (const item of items) { - const sublist = item.querySelector("ul"); - if (sublist) { - if (sublist.children.length) { - item.className = "submenu"; - } - else { - sublist.remove(); - } - } - } -} */ - /** * General premise here is to find all submenus/sublists * and give them a submenu class. Then any list item @@ -22,7 +5,7 @@ const submenuBlacklist = ["ZapIU17QNEyU"]; * class. Additionally, any sublist that itself has a * sublist is given the class hasSubmenu. */ -export default function fixSubMenu() { +export default function fixSubMenu(submenuBlacklist: string[]) { // Get every list item in the navigation const items = document.querySelectorAll("#menu ul li"); for (const item of items) { diff --git a/src/main/index.ts b/src/main/index.ts index 1588c830e..33a8966da 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,19 +1,24 @@ -/* eslint-disable no-console */ import fixActiveLink from "./fixes/activelink"; import fixTableHeaders from "./fixes/tableheaders"; import highlight from "./other/highlight"; import buildSidenav from "./navigation/sidenav"; import buildBreadcrumbs from "./navigation/breadcrumbs"; -import fixSubMenu from "./fixes/submenu"; +import fixSubMenus from "./fixes/submenu"; import generateTOC from "./navigation/toc"; import addExternalLinks from "./fixes/externallinks"; import injectSwagger from "./other/swagger"; import makeMobileMenu from "./other/mobile"; +import addOpenGraphMeta from "./other/opengraph"; -// https://instance-name/api/notes/vW1cXaYNN7OM/download +const ETAPI_REF_NOTE_ID = "pPIXi0uwF5GX"; +const HIDDEN_SUBMENUS = ["blog"]; const EXTERNAL_LINKS = { - EGFtX8Uw96FQ: "https://github.com/zadam/trilium" + EGFtX8Uw96FQ: "https://github.com/zadam/trilium", +}; +const ALIASES = { + WqBnya4Ye8rS: "", + ZapIU17QNEyU: "blog" }; function $try unknown>(func: T, ...args: Parameters) { @@ -21,22 +26,27 @@ function $try unknown>(func: T, ...args: Paramete func.apply(func, args); } catch (e) { - console.error(e); + console.error(e); // eslint-disable-line no-console } } -// const $try = (func, ...args) => { -// try { -// func.apply() -// } -// }; - - +/** + * Lots of these functions seem to depend on each other indirectly + * through DOM changes or classes or what-have-you. This is + * obviously not ideal as it makes things less clear, and also + * makes TypeScript less helpful. + * + * TODO: Find a good way of restructuring that allows things + * to act a bit more harmoniously. + * + * TODO: Make use of esbuild's define api to enable a debug + * build that contains all the console logs and such. + */ // Perform fixes first -$try(fixActiveLink); +$try(fixActiveLink, ALIASES); $try(fixTableHeaders); -$try(fixSubMenu); +$try(fixSubMenus, HIDDEN_SUBMENUS); $try(addExternalLinks, EXTERNAL_LINKS); // Now layout changes @@ -46,39 +56,6 @@ $try(generateTOC); // Finally, other features $try(highlight); -$try(injectSwagger); +$try(injectSwagger, ETAPI_REF_NOTE_ID); $try(makeMobileMenu); - -// mobileMenu.append(document.querySelector("#menu > ul")!); -// mobileMenu.append(document.querySelector("#sidebar")!); - - - - -// try {fixActiveLink();} -// catch (e) {console.error(e);} - -// try {highlight();} -// catch (e) {console.error(e);} - -// try {fixTableHeaders();} -// catch (e) {console.error(e);} - -// try{addLogo();} -// catch{} - - -// try {fixSubMenu();} -// catch (e) {console.error(e);} - -// try {buildSidenav();} -// catch (e) {console.error(e);} - -// try {buildBreadcrumbs();} -// catch (e) {console.error(e);} - -// try {generateTOC();} -// catch (e) {console.error(e);} - -// try {addExternalLinks(EXTERNAL_LINKS);} -// catch (e) {console.error(e);} +$try(addOpenGraphMeta); diff --git a/src/main/navigation/breadcrumbs.ts b/src/main/navigation/breadcrumbs.ts index a32353915..16bd58409 100644 --- a/src/main/navigation/breadcrumbs.ts +++ b/src/main/navigation/breadcrumbs.ts @@ -47,5 +47,7 @@ export default function buildBreadcrumbsFromNav() { // Add breadcrumb container to the main layout const main = document.getElementById("main"); main?.prepend(container); + + // Scroll to the active breadcrumb in case of overflow container.scrollLeft = container.scrollWidth - container.clientWidth; } \ No newline at end of file diff --git a/src/main/other/highlight.ts b/src/main/other/highlight.ts index 7218847ec..4895ab475 100644 --- a/src/main/other/highlight.ts +++ b/src/main/other/highlight.ts @@ -26,9 +26,11 @@ const highlightJQuery: HLJSPlugin = { /** * Let's highlight some codeblocks! - * TODO: check if there are codeblocks on the page before performing this action */ export default function addHljs() { + const codeblocks = document.querySelectorAll(`.ck-content pre`); + if (!codeblocks.length) return; // If there are none, don't add dependency + // Add the hightlight.js styles from the child note of this script // TODO: make this a mapping const link = document.createElement("link"); diff --git a/src/main/other/opengraph.ts b/src/main/other/opengraph.ts new file mode 100644 index 000000000..c9fb88844 --- /dev/null +++ b/src/main/other/opengraph.ts @@ -0,0 +1,40 @@ +// TODO: move to common location +const parseHTML = (html: string, fragment = false) => { + const template = document.createElement("template"); + template.innerHTML = html; + const node = template.content.cloneNode(true); + if (fragment) return node; + return node.childNodes.length > 1 ? node.childNodes : node.childNodes[0]; +}; + + +// TODO: See if there's a way to inject this without client-side js +const metaString = ` + + + + + + + + + + + + + + + + + +`; + + +export default function addOpenGraphMeta() { + const currentTitle = document.querySelector("title")!; + currentTitle.textContent = currentTitle.textContent === "Trilium Rocks" ? "Trilium Rocks - Home" : `Trilium Rocks - ${currentTitle.textContent}`; + const nodes = parseHTML(metaString.replaceAll("{{title}}", currentTitle.textContent)) as NodeListOf; + for (const node of nodes) { + document.head.append(node); + } +} \ No newline at end of file diff --git a/src/main/other/swagger.ts b/src/main/other/swagger.ts index de8fa3f44..572250bd8 100644 --- a/src/main/other/swagger.ts +++ b/src/main/other/swagger.ts @@ -9,10 +9,10 @@ const opts: SwaggerUIOptions = { /** * Add swagger to the ETAPI page! */ -export default function injectSwagger() { +export default function injectSwagger(expectedNoteId: string) { // Check if it's the ETAPI page, otherwise avoid dependency const noteId = document.body.dataset.noteId; - if (noteId !== "pPIXi0uwF5GX") return; // TODO: make this a param + if (noteId !== expectedNoteId) return; // TODO: make this a param const main = document.getElementById("main")!; main.innerHTML = ""; opts.domNode = main; diff --git a/src/styles/breadcrumbs.css b/src/styles/breadcrumbs.css index ba56f7ac1..23fd91d3f 100644 --- a/src/styles/breadcrumbs.css +++ b/src/styles/breadcrumbs.css @@ -1,9 +1,9 @@ #breadcrumbs { - margin-bottom: 30px; + margin: 0 0 30px 0; display: flex; align-items: center; list-style: none; - padding: 0; + padding: 16px 0 0 0; overflow-x: auto; } @@ -22,6 +22,7 @@ } #breadcrumbs img { + width: 18px; min-width: 18px; filter: invert(100%); } From ad590803079efb9e0421cfc3b202b414742d5156 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Sat, 23 Sep 2023 22:39:38 -0400 Subject: [PATCH 06/28] Some more finishing touches --- src/main/index.ts | 16 +++++++++++++-- src/main/navigation/toc.ts | 9 +++++++++ src/main/other/opengraph.ts | 40 ------------------------------------- src/styles/mobile.css | 9 +++++++++ src/styles/sidebar.css | 8 ++++++++ 5 files changed, 40 insertions(+), 42 deletions(-) delete mode 100644 src/main/other/opengraph.ts diff --git a/src/main/index.ts b/src/main/index.ts index 33a8966da..88879347b 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -8,13 +8,13 @@ import generateTOC from "./navigation/toc"; import addExternalLinks from "./fixes/externallinks"; import injectSwagger from "./other/swagger"; import makeMobileMenu from "./other/mobile"; -import addOpenGraphMeta from "./other/opengraph"; const ETAPI_REF_NOTE_ID = "pPIXi0uwF5GX"; const HIDDEN_SUBMENUS = ["blog"]; const EXTERNAL_LINKS = { EGFtX8Uw96FQ: "https://github.com/zadam/trilium", + dXAKFE0fJtom: "https://discord.gg/eTaTXUgcBr" }; const ALIASES = { WqBnya4Ye8rS: "", @@ -58,4 +58,16 @@ $try(generateTOC); $try(highlight); $try(injectSwagger, ETAPI_REF_NOTE_ID); $try(makeMobileMenu); -$try(addOpenGraphMeta); + +/** + * This was removed because both the title change and the opengraph + * additions are now handled by a traefik plugin that rewrites + * the body of the http request, that way the change does not + * require client-side JS. This is important for sites wishing + * to display that information. + * + * TODO: Determine how reasonable it would be to move more + * of these modules over to a traefik rewrite plugin. This gives + * a better experience to end users, SEO, etc. + */ +// $try(addOpenGraphMeta); diff --git a/src/main/navigation/toc.ts b/src/main/navigation/toc.ts index 89f46bfa9..052da9583 100644 --- a/src/main/navigation/toc.ts +++ b/src/main/navigation/toc.ts @@ -14,6 +14,15 @@ const buildItem = (heading: Element) => { const link = document.createElement("a"); link.setAttribute("href", `#${slug}`); link.textContent = heading.textContent; + link.addEventListener("click", e => { + const target = document.querySelector(`#${slug}`); + if (!target) return; + + e.preventDefault(); + e.stopPropagation(); + + target.scrollIntoView({behavior: "smooth"}); + }); heading.append(anchor); diff --git a/src/main/other/opengraph.ts b/src/main/other/opengraph.ts deleted file mode 100644 index c9fb88844..000000000 --- a/src/main/other/opengraph.ts +++ /dev/null @@ -1,40 +0,0 @@ -// TODO: move to common location -const parseHTML = (html: string, fragment = false) => { - const template = document.createElement("template"); - template.innerHTML = html; - const node = template.content.cloneNode(true); - if (fragment) return node; - return node.childNodes.length > 1 ? node.childNodes : node.childNodes[0]; -}; - - -// TODO: See if there's a way to inject this without client-side js -const metaString = ` - - - - - - - - - - - - - - - - - -`; - - -export default function addOpenGraphMeta() { - const currentTitle = document.querySelector("title")!; - currentTitle.textContent = currentTitle.textContent === "Trilium Rocks" ? "Trilium Rocks - Home" : `Trilium Rocks - ${currentTitle.textContent}`; - const nodes = parseHTML(metaString.replaceAll("{{title}}", currentTitle.textContent)) as NodeListOf; - for (const node of nodes) { - document.head.append(node); - } -} \ No newline at end of file diff --git a/src/styles/mobile.css b/src/styles/mobile.css index 408d677b6..967ee166a 100644 --- a/src/styles/mobile.css +++ b/src/styles/mobile.css @@ -145,4 +145,13 @@ button.expand { #menu > ul:not(#sidebar) ul { margin-left: 20px; } + + #menu > ul:not(#sidebar) ul { + margin-left: 20px; + } + + #menu > ul:not(#sidebar) a.active { + background: var(--accent-color); + border-radius: 6px; + } } \ No newline at end of file diff --git a/src/styles/sidebar.css b/src/styles/sidebar.css index 3a2b099ba..acf97f222 100644 --- a/src/styles/sidebar.css +++ b/src/styles/sidebar.css @@ -55,4 +55,12 @@ #sidebar > li > ul.hasSubmenu, #sidebar > li > ul > li.submenu-item + li.submenu-item { margin-top: 20px; +} + +#sidebar a.active, +#sidebar > li > ul > li.submenu-item > p > a.active { + background: var(--accent-color); + /* background: rgba(255, 255, 255, 0.1); */ + color: var(--text-heading); + font-weight: 700; } \ No newline at end of file From d9e33270b057ae36a9487c0eaaec85da83292c9c Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Sun, 24 Sep 2023 06:03:35 -0400 Subject: [PATCH 07/28] Add README --- README.md | 21 +++++++++++++++++++++ src/main/other/highlight.ts | 14 ++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..c26aefcd2 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# [Trilium Rocks!](https://trilium.rocks/) [![Discord](https://img.shields.io/discord/1155302051987849320?style=flat-square&logo=discord&logoColor=white&label=Discord&color=%235865F2)](https://discord.gg/eTaTXUgcBr) + +[Trilium Notes](https://github.com/zadam/trilium) really does rock! Don't believe me? Well I created the entire [trilium.rocks](https://trilium.rocks) website using the shared notes feature inside Trilium with a little bit of extra CSS and JS also contained in Trilium. That JS and CSS? That's what you'll find here in this repository. + +**Disclaimer:** This project and website is not at all affiliated with Trilium Notes or its creator(s). + +## Why? + +I love Trilium, it has made me the most organized I have ever been in my many many years of digital storage. The only thing I found lacking was the centralization of information. The [wiki](https://github.com/zadam/trilium/wiki) is a great resource, but it's outdated in some areas, and lacking in many many others—especially in regards to developing addons for Trilium. I also don't personally use gitter so the [official Trilium gitter](https://gitter.im/trilium-notes/Lobby) is not useful for me, and last time I checked it was _very_ inactive. So I made the website [trilium.rocks](https://trilium.rocks) and [a Discord server](https://discord.gg/eTaTXUgcBr) to try and help with each of those respectively. + +With the website, I want to at least provide supplementary knowledge to the wiki by adding extended guides for users and developers. I also want to try and make it a more user-friendly central place to browse addons. + +With the Discord server, I wanted to interact with the community and see what kind of addons people may be interested in. It also provides a quick and easy way to provide support to people, or even get support from others. And hopefully, it lets the community's developers come together to share information and make all of our addons even better. + +## About The Site + +Rather than saying some specific goals of what this site strives to be, I'll say what it strives not to be. This site is not meant to be a complete recreation of the Wiki with every detail and page included. It is meant to be a (mostly) one-stop shop for users and developers alike looking to supplement their knowledge. It may at some point expand and include everything from the wiki because users tend to prefer a fancier UI like this, but it is not the end-goal. It also may move in that direction if [zadam](https://github.com/zadam) wants to use this (or parts of this) project as part of the in-app documentation. + +## Contributing + +Since the entire site is just a share from my personal Trilium instance, there is no easy way to contribute new pages or fixes for typos. For now, this GitHub repo's issues and discussion can be used as places to contribute bug reports, feature requests, and even documentation contributions. But who knows, maybe soon I'll think of some clever way to introduce contributions directly to my Trilium instance. \ No newline at end of file diff --git a/src/main/other/highlight.ts b/src/main/other/highlight.ts index 4895ab475..2465c5a68 100644 --- a/src/main/other/highlight.ts +++ b/src/main/other/highlight.ts @@ -43,6 +43,20 @@ export default function addHljs() { const script = document.createElement("script"); script.src = "api/notes/6PVElIem02b5/download"; script.addEventListener("load", () => { + // hljs.configure({languageDetectRe: /\blanguage-text-x-([\w-]+)\b/i}); + + const allLanguages = hljs.listLanguages().map(l => { + const definition = hljs.getLanguage(l); + if (definition?.aliases) return [l, ...definition.aliases]; + return [l]; + }); + for (const langs of allLanguages) { + const lang = langs[0]; + for (const l of langs) { + hljs.registerAliases(`text-x-${l}`, {languageName: lang}); + } + } + // This registers the JS Frontend and JS Backend types as javascript aliases for highlighting purposes hljs.registerAliases(["application-javascript-env-frontend", "application-javascript-env-backend"], {languageName: "javascript"}); From 2ae6d4c5a42d093a10feb8eaa0cb3bbb088c5bd3 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Wed, 27 Sep 2023 16:20:04 -0400 Subject: [PATCH 08/28] Add initial templates --- src/templates/page.ejs | 132 ++++++++++++++++++++++++++++++++++++ src/templates/tree_item.ejs | 25 +++++++ 2 files changed, 157 insertions(+) create mode 100644 src/templates/page.ejs create mode 100644 src/templates/tree_item.ejs diff --git a/src/templates/page.ejs b/src/templates/page.ejs new file mode 100644 index 000000000..5abba1535 --- /dev/null +++ b/src/templates/page.ejs @@ -0,0 +1,132 @@ + + + + + <% if (note.hasLabel("shareDescription")) { %> + "> + <% } %> + + + <% if (note.hasRelation("shareFavicon")) { %> + /download"> + <% } else { %> + + <% } %> + + <% if (!note.hasLabel("shareOmitDefaultCss")) { %> + + + <% } %> + <% if (note.hasLabel("shareSwagger")) { %> + + + + <% } %> + <% if (note.type === 'text' || note.type === 'book') { %> + + <% } %> + <% for (const cssRelation of note.getRelations("shareCss")) { %> + + <% } %> + <% for (const jsRelation of note.getRelations("shareJs")) { %> + + <% } %> + <% if (note.hasLabel('shareDisallowRobotIndexing')) { %> + + <% } %> + <%- header %> + <%= note.title %><% if (note.noteId !== subRoot.noteId) { %> - <%= subRoot.title %><% } %> + +<% +const currentTheme = note.getLabel("shareTheme") === "light" ? "light" : "dark"; +const themeClass = currentTheme === "light" ? " theme-light" : " theme-dark"; +%> + + +
    +
    + +
    +
    +
    + + + <% if (note.hasLabel("pageUrl")) { %> +
    This note was originally clipped from "><%= note.getLabelValue("pageUrl") %>
    + <% } %> + + <% if (!isEmpty) { %> +
    +

    <%= note.title %>

    + <%- content %> +
    + <% } %> + + <% if (note.hasVisibleChildren()) { %> + + <% } else if (isEmpty) { %> +
    +

    <%= note.title %>

    +

    This note has no content.

    +
    + <% } %> +
    +
    +
    + + diff --git a/src/templates/tree_item.ejs b/src/templates/tree_item.ejs new file mode 100644 index 000000000..dd50a108a --- /dev/null +++ b/src/templates/tree_item.ejs @@ -0,0 +1,25 @@ +<% +const linkClass = `type-${note.type}` + (activeNote.noteId === note.noteId ? " active" : ""); +const isExternalLink = note.hasLabel("shareExternal"); +const linkHref = isExternalLink ? note.getLabelValue("shareExternal") : `./${note.shareId}`; +const target = isExternalLink ? ` target="_blank" rel="noopener noreferrer"` : ""; +%> + + <% if (note.noteId !== subRoot.noteId) { %> +> + <% if (note.hasVisibleChildren()) { %><% } %> + <%= note.title %> + +<% } %> + + +<% if (note.hasVisibleChildren()) { %> +
      + <% note.getVisibleChildNotes().forEach(function (childNote) { %> + <% const hasChildren = childNote.hasVisibleChildren(); %> +
    • + <%- include('tree_item', {note: childNote, subRoot: subRoot}) %> +
    • + <% }) %> +
    +<% } %> From 667cd64f3b898ba5ff082352e9ed3026fb96e0a3 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Wed, 27 Sep 2023 16:28:08 -0400 Subject: [PATCH 09/28] Rename main to scripts --- scripts/build.ts | 2 +- src/{main => scripts}/fixes/activelink.ts | 0 src/{main => scripts}/fixes/externallinks.ts | 0 src/{main => scripts}/fixes/submenu.ts | 0 src/{main => scripts}/fixes/tableheaders.ts | 0 src/{main => scripts}/index.ts | 0 src/{main => scripts}/navigation/breadcrumbs.ts | 0 src/{main => scripts}/navigation/sidenav.ts | 0 src/{main => scripts}/navigation/toc.ts | 0 src/{main => scripts}/other/highlight.ts | 0 src/{main => scripts}/other/mobile.ts | 0 src/{main => scripts}/other/swagger.ts | 0 12 files changed, 1 insertion(+), 1 deletion(-) rename src/{main => scripts}/fixes/activelink.ts (100%) rename src/{main => scripts}/fixes/externallinks.ts (100%) rename src/{main => scripts}/fixes/submenu.ts (100%) rename src/{main => scripts}/fixes/tableheaders.ts (100%) rename src/{main => scripts}/index.ts (100%) rename src/{main => scripts}/navigation/breadcrumbs.ts (100%) rename src/{main => scripts}/navigation/sidenav.ts (100%) rename src/{main => scripts}/navigation/toc.ts (100%) rename src/{main => scripts}/other/highlight.ts (100%) rename src/{main => scripts}/other/mobile.ts (100%) rename src/{main => scripts}/other/swagger.ts (100%) diff --git a/scripts/build.ts b/scripts/build.ts index 4591c6db3..ad74c7a11 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -19,7 +19,7 @@ dotenv.config(); if (process.env.TRILIUM_ETAPI_TOKEN) tepi.token(process.env.TRILIUM_ETAPI_TOKEN); const bundleMap = { - "main.js": process.env.JS_NOTE_ID, + "scripts.js": process.env.JS_NOTE_ID, "styles.css": process.env.CSS_NOTE_ID }; diff --git a/src/main/fixes/activelink.ts b/src/scripts/fixes/activelink.ts similarity index 100% rename from src/main/fixes/activelink.ts rename to src/scripts/fixes/activelink.ts diff --git a/src/main/fixes/externallinks.ts b/src/scripts/fixes/externallinks.ts similarity index 100% rename from src/main/fixes/externallinks.ts rename to src/scripts/fixes/externallinks.ts diff --git a/src/main/fixes/submenu.ts b/src/scripts/fixes/submenu.ts similarity index 100% rename from src/main/fixes/submenu.ts rename to src/scripts/fixes/submenu.ts diff --git a/src/main/fixes/tableheaders.ts b/src/scripts/fixes/tableheaders.ts similarity index 100% rename from src/main/fixes/tableheaders.ts rename to src/scripts/fixes/tableheaders.ts diff --git a/src/main/index.ts b/src/scripts/index.ts similarity index 100% rename from src/main/index.ts rename to src/scripts/index.ts diff --git a/src/main/navigation/breadcrumbs.ts b/src/scripts/navigation/breadcrumbs.ts similarity index 100% rename from src/main/navigation/breadcrumbs.ts rename to src/scripts/navigation/breadcrumbs.ts diff --git a/src/main/navigation/sidenav.ts b/src/scripts/navigation/sidenav.ts similarity index 100% rename from src/main/navigation/sidenav.ts rename to src/scripts/navigation/sidenav.ts diff --git a/src/main/navigation/toc.ts b/src/scripts/navigation/toc.ts similarity index 100% rename from src/main/navigation/toc.ts rename to src/scripts/navigation/toc.ts diff --git a/src/main/other/highlight.ts b/src/scripts/other/highlight.ts similarity index 100% rename from src/main/other/highlight.ts rename to src/scripts/other/highlight.ts diff --git a/src/main/other/mobile.ts b/src/scripts/other/mobile.ts similarity index 100% rename from src/main/other/mobile.ts rename to src/scripts/other/mobile.ts diff --git a/src/main/other/swagger.ts b/src/scripts/other/swagger.ts similarity index 100% rename from src/main/other/swagger.ts rename to src/scripts/other/swagger.ts From 124fd480b73508119412fa5bcd8a83f85e5c828e Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Wed, 27 Sep 2023 16:49:30 -0400 Subject: [PATCH 10/28] Update build script for templates --- package.json | 4 +++- scripts/build.ts | 22 +++++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5bc733544..75d438fc0 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,10 @@ "main": "index.js", "scripts": { "build": "esrun scripts/build.ts", - "build-main": "esrun scripts/build.ts -- --module=main", + "build-all": "esrun scripts/build.ts -- --templates", + "build-scripts": "esrun scripts/build.ts -- --module=scripts", "build-styles": "esrun scripts/build.ts -- --module=styles", + "templates": "esrun scripts/build.ts -- --only-templates", "dist": "esrun scripts/build.ts -- --minify", "test": "echo \"Error: no test specified\" && exit 1" }, diff --git a/scripts/build.ts b/scripts/build.ts index ad74c7a11..d8fea2d7a 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -18,6 +18,25 @@ const rootDir = path.dirname(process.env.npm_package_json!); dotenv.config(); if (process.env.TRILIUM_ETAPI_TOKEN) tepi.token(process.env.TRILIUM_ETAPI_TOKEN); + +const templateMap: Record = { + "src/templates/page.ejs": process.env.PAGE_TEMPLATE_ID!, + "src/templates/tree_item.ejs": process.env.ITEM_TEMPLATE_ID!, +}; + +async function sendTemplates() { + for (const template in templateMap) { + const templatePath = path.join(rootDir, template); + const contents = fs.readFileSync(templatePath).toString(); + await tepi.putNoteContentById(templateMap[template], contents); + } +} + +if (process.argv.includes("--only-templates")) { + sendTemplates().catch(console.error); + process.exit(0); +} + const bundleMap = { "scripts.js": process.env.JS_NOTE_ID, "styles.css": process.env.CSS_NOTE_ID @@ -53,7 +72,7 @@ const triliumPlugin: esbuild.Plugin = { }; -const modules = ["main", "styles"]; +const modules = ["scripts", "styles"]; const entryPoints: {in: string, out: string}[] = []; const makeEntry = (mod: string) => ({"in": path.join(rootDir, "src", mod, mod === "styles" ? "index.css" : "index.ts"), "out": mod}); @@ -89,6 +108,7 @@ async function runBuild() { minify: process.argv.includes("--minify") }); const after = performance.now(); + if (process.argv.includes("--templates")) await sendTemplates(); console.log(`Build actually took ${(after - before).toFixed(2)}ms`); } From 1a40658345a24a52f76c9d2740e94b788a474dbc Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Wed, 27 Sep 2023 22:53:59 -0400 Subject: [PATCH 11/28] Rework css for new design --- src/styles/breadcrumbs.css | 48 ------ src/styles/childlinks.css | 53 ++++++ src/styles/index.css | 314 +++++++--------------------------- src/styles/mobile.css | 178 +++++++------------ src/styles/navbar.css | 152 ---------------- src/styles/navbar/header.css | 141 +++++++++++++++ src/styles/navbar/index.css | 2 + src/styles/navbar/navbar.css | 141 +++++++++++++++ src/styles/popouts/index.css | 1 + src/styles/popouts/search.css | 34 ++++ src/styles/sidebar.css | 66 ------- src/styles/swagger.css | 40 ++++- src/styles/toc.css | 70 ++++++-- 13 files changed, 588 insertions(+), 652 deletions(-) delete mode 100644 src/styles/breadcrumbs.css create mode 100644 src/styles/childlinks.css delete mode 100644 src/styles/navbar.css create mode 100644 src/styles/navbar/header.css create mode 100644 src/styles/navbar/index.css create mode 100644 src/styles/navbar/navbar.css create mode 100644 src/styles/popouts/index.css create mode 100644 src/styles/popouts/search.css delete mode 100644 src/styles/sidebar.css diff --git a/src/styles/breadcrumbs.css b/src/styles/breadcrumbs.css deleted file mode 100644 index 23fd91d3f..000000000 --- a/src/styles/breadcrumbs.css +++ /dev/null @@ -1,48 +0,0 @@ -#breadcrumbs { - margin: 0 0 30px 0; - display: flex; - align-items: center; - list-style: none; - padding: 16px 0 0 0; - overflow-x: auto; -} - -#breadcrumbs li { - display: flex; - align-items: center; -} - -#breadcrumbs a { - display: flex; - align-items: center; - white-space: nowrap; - padding: 5px 10px; - border-radius: 20px; - transition: color 200ms ease, transform 200ms ease, background-color 200ms ease; -} - -#breadcrumbs img { - width: 18px; - min-width: 18px; - filter: invert(100%); -} - -#breadcrumbs li:not(:last-child)::after { - background: url("data:image/svg+xml;utf8,") center; - content: " "; - display: inline-block; - filter: invert(0.64) sepia(0.11) saturate(0) hue-rotate(149deg) brightness(0.99) contrast(0.95); - height: 8px; - margin: 0 8px; - opacity: .5; - width: 8px; -} - -#breadcrumbs li:last-child a, -#breadcrumbs li a:hover { - background: #202127; -} - -#breadcrumbs li a:hover { - transform: translateY(-3px); -} \ No newline at end of file diff --git a/src/styles/childlinks.css b/src/styles/childlinks.css new file mode 100644 index 000000000..094f48efb --- /dev/null +++ b/src/styles/childlinks.css @@ -0,0 +1,53 @@ +#childLinks, +#childLinks ul, +#childLinks li { + list-style: none; + display: flex; + align-items: center; +} + +#childLinks { + padding-top: 10px; + flex-direction: column; + gap: 0px; + justify-content: center; + border-top: 1px solid var(--background-highlight); +} + +#childLinks ul { + padding: 0; + gap: 10px; + flex-wrap: wrap; + justify-content: center; +} + +#childLinks li { + background: var(--background-highlight); + padding: 2px 12px; + border-radius: 12px; +} + + +.no-content + #childLinks { + border: 0; +} + +#childLinks.grid li { + padding: 0; +} + +#childLinks.grid li a { + padding: 50px; + border-radius: 12px; + background: var(--background-highlight); + color: var(--text-primary); + transform: translateY(0); + transition: transform 200ms ease, background-color 200ms ease, color 200ms ease; +} + +#childLinks.grid li a:hover { + background: var(--background-active); + color: var(--background-secondary); + text-decoration: none; + transform: translateY(-5px); +} \ No newline at end of file diff --git a/src/styles/index.css b/src/styles/index.css index 6fcf80bcc..8ae5f3043 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -1,291 +1,103 @@ -@import "./breadcrumbs.css"; +@import "./childlinks.css"; @import "./externallinks.css"; -@import "./navbar.css"; -@import "./sidebar.css"; @import "./swagger.css"; @import "./toc.css"; +@import "./navbar/index.css"; +@import "./popouts/index.css"; + @import "./mobile.css"; -* {box-sizing: border-box;} +* { + box-sizing: border-box; +} :root { - --shadow: 0px 24px 32px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 0px 1px rgba(0, 0, 0, 0.04); - --blue: #3E82E5; - --blue-strong: #008CBD; - --gray: gray; - --ck-color-table-caption-text: gray; - --bg-primary: #1E1E1E; - --bg-secondary: #3C3C3C; - --text-muted: #777; - --text-primary: #ddd; - --text-heading: #fff; - - --bottom-layer: #040405; - --middle-layer: #0C0D10; - --top-layer: #14151B; - --accent-layer: #20212B; - --accent-color: #3E82E5; - - --container-width: 1400px /*calc(100% - 10%)*/; - --pane-size: 20%; + --background-primary: #333333; + --background-secondary: #1F1F1F; + --background-highlight: #484848; + --background-active: #777777; + --text-primary: #cccccc; + --text-heading: #cccccc; + --text-menu: #AAAAAA; + --text-link: #87CEFA; + --text-menu-active: #000000; +} - - scroll-padding-top: calc(72px + 1rem); - color-scheme: dark; +body.theme-light { + --background-primary: #FFFFFF; + --background-secondary: #F3F3F3; + --background-highlight: #DDDDDD; + --background-active: #777777; + --text-primary: #000000; + --text-heading: #000000; + --text-menu: #333333; + --text-link: #0000ff; } body { - margin: 0px; - padding: 0px; - background: var(--middle-layer); - font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; + background: var(--background-primary); + font-family: 'Montserrat', 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif; + line-height: 1.5; color: var(--text-primary); - margin-bottom: 200px; } a { + color: var(--text-link); text-decoration: none; - color: var(--text-primary); - transition: color 200ms ease; } a:hover { - color: var(--accent-color); + text-decoration: underline; } - - - -#content { - font-size: 16px; - font-weight: 400; - -webkit-font-smoothing: subpixel-antialiased; - line-height: 1.5; - overflow-wrap: break-word; - word-wrap: break-word; - hyphens: auto; - letter-spacing: 0.02em; -} - -#main h1, -#main h2, -#main h3, -#main h4, -#main h5, -#main h6 { - color: var(--text-heading); - line-height: 36px; - padding: 5px 0px; - letter-spacing: 0.02em; - font-weight: 800; - position: relative; -} - -/* #content h2::before, -#content h3::before, -#content h4::before{ - content: '#'; - color: var(--blue); - position: absolute; - left: -20px; -} */ - -#main h1 a, -#main h2 a, -#main h3 a, -#main h4 a, -#main h5 a, -#main h6 a { - color: var(--blue); - margin-left: 10px; -} - -#content h2 { - font-size: 24px; -} - -#content h3 { - font-size: 20px; -} - -#content h4 { - font-size: 16px; -} - -#content p { - margin-block-start: 12px; - margin-block-end: 12px; - text-align: justify; - overflow: hidden; -} - -#content hr { - height: 1px; -} - -#content blockquote { - margin-block-start: 12px; - margin-block-end: 24px; - margin-inline-start: 32px; - margin-inline-end: 32px; - padding-block-start: 12px; - padding-block-end: 12px; - padding-inline-start: 16px; - padding-inline-end: 16px; - background-color: hsla(0deg, 0%, 0%, 0.054); -} - -#noteClippedFrom { - color: var(--gray); - margin-bottom: 0.5rem; - font-style:italic; -} - -.table { - overflow-wrap: anywhere; -} - -.pdf-view { - width: 100%; - min-height: 40rem; -} - -.type-file > button { - display: block; - margin: 2rem auto; - background-color: var(--blue); - border: none; - color: white; - padding: 1rem 2rem; - text-align: center; - text-decoration: none; - font-size: 1rem; - border-radius: 0.25rem; - box-shadow: var(--shadow); -} - -.mermaid { - text-align: center; -} - -/* Menu */ - -#toggleMenuButton { - display: none; -} - - - - -body { - display: flex; - justify-content: center; -} - -#layout { +#split-pane { display: flex; flex-direction: row; - width: var(--container-width); - max-width: var(--container-width); - margin-top: calc(72px + 1rem); - position: relative; - height: 100%; - gap: 16px; + gap: 50px; } -@media screen and (max-width: 1500px) { - #layout { - width: 1300px; - max-width: 1300px; - } +#left-pane { + display: flex; + width: calc((100vw - 900px) / 2); + min-width: fit-content; + height: calc(100vh); + background: var(--background-secondary); + border-right: 5px solid var(--background-highlight); + justify-content: flex-end; + position: sticky; + top: 0; } -@media screen and (max-width: 1300px) { - #layout { - width: 1100px; - max-width: 1200px; - } -} - - - -#main { - flex: 100%; -} - -#layout.toc #main, -#sidebar + #main { - flex: calc(100% - var(--pane-size)); -} - -#layout.toc #sidebar + #main { - flex: calc(100% - (2 * var(--pane-size))); +#right-pane { + display: flex; + margin: 0 auto; + gap: 40px; + flex: 1; + padding-bottom: 500px; + padding-right: 50px; } #main { - contain: content; - background-color: var(--top-layer); - padding: 1rem 3rem 2rem 3rem; - box-shadow: 0 2px 10px #00000040; - border-radius: 6px; + order: 2; + max-width: 900px; + flex: 1; } - - - - - - - - - - -.ck-content code { - background-color: var(--accent-layer); - border-radius: 6px; - padding: 3px; -} .ck-content pre { - background: var(--accent-layer); - border-radius: 6px; - border: 0; - box-shadow: 0 2px 4px #0003; + color: var(--text-primary); } - -#childLinks.list { - display: none; -} - - - - - -#parentLink { - display: none; -} - - - - - - - - - - - - - - -#childLinks.grid ul li a { - border: 1px solid rgba(255, 255, 255, 0.1); -} - -#childLinks.grid ul li a:hover { - background: rgba(255, 255, 255, 0.1); +#content h1, +#content h2, +#content h3, +#content h4, +#content h5, +#content h6 { + color: var(--text-heading); + border-bottom: 1px solid var(--background-highlight); + padding-bottom: 5px; } \ No newline at end of file diff --git a/src/styles/mobile.css b/src/styles/mobile.css index 967ee166a..d0959b351 100644 --- a/src/styles/mobile.css +++ b/src/styles/mobile.css @@ -1,157 +1,95 @@ -.expand { - display: none +#mobile-header { + display: none; + background: var(--background-secondary); + justify-content: space-between; + align-items: center; + padding: 6px 12px; } -button.expand { - position: relative; - padding: 9px; - margin: 9.5px 0; - border: 0 solid #000; - border-radius: 4px; - background: 0 0; - color: #fff; - -webkit-transition: -webkit-transform .5s; - -moz-transition: transform .5s; - -o-transition: transform .5s; - transition: transform .5s +#mobile-header a { + display: flex; + align-items: center; + gap: 5px; } -.expanded button.expand { - background-color: #000; - -webkit-transition: -webkit-transform .5s, background-color .5s; - -moz-transition: transform .5s, background-color .5s; - -o-transition: transform .5s, background-color .5s; - transition: transform .5s, background-color .5s; - -ms-transform: rotate(90deg); - -webkit-transform: rotate(90deg); - transform: rotate(90deg) +#mobile-header a img { + max-width: 32px; } -.expanded .rectangle { - background: #fff; +#mobile-header button { + color: var(--text-menu); + background: transparent; + margin: 0; + padding: 0; + border: 0; + outline: 0; + display: flex; + align-items: center; + cursor: pointer; + border-radius: 6px; + transform: rotate(0); + transition: background-color 200ms ease, transform 200ms ease; } -.rectangle { - display: block; - width: 24px; - height: 4px; - border-radius: 1px; - background: var(--text-heading); - transition: transform .5s, background-color .5s; -} -.rectangle + .rectangle { - margin-top: 5px -} -@media screen and (max-width: 768px) { - #toc { - display: none; +@media (max-width: 48em) { + + #right-pane, #main { + width: 100%; + overflow: hidden; + padding: 0; } #main { padding: 1rem; } - #layout { - overflow: hidden; + + #mobile-header { + display: flex; } - #menu { - width: auto; + #mobile-header button svg { + width: 32px; + height: 32px; } - .expand { - display: inline-block - } - - #menu::after { - content: ""; + #left-pane { position: fixed; top: 0; - right: 0; - bottom: 0; left: 0; - height: 100%; - width: 100%; - transition: background-color 200ms ease; - pointer-events: none; - } - - #menu.expanded::after { - width: 100%; - background: rgba(0,0,0,0.7); - } - - - - #menu > ul { - position: fixed; + width: auto; transform: translateX(-100%); transition: transform 200ms ease; - transform-origin: 0 0; - z-index: 1; - background: #040405; - display: initial; - top: 0; - left: 0; - bottom: 0; - height: 100%; - - width: 70%; - padding: 1rem; - overflow: auto; + z-index: 2; } - #menu #go-back { - margin-bottom: 20px; - } - - #menu.expanded > ul.active { + .menu-open #left-pane { transform: translateX(0); } - #menu #sidebar .category > p > a { - font-weight: 400; + body::before { + content: ""; + display: block; + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + background: rgba(0,0,0,0); + pointer-events: none; + transition: background-color 200ms ease; + z-index: 1; } - #menu > ul > li > p > a { - padding: 3px 6px; - } - - #menu > ul > li > ul { - background: none; - opacity: 1; - position: initial; - transform: none; - margin-top: 0; - pointer-events: initial; - padding: 0; - } - - #menu > ul > li.submenu-item > p > a::after { - display: none; - } - - #menu > ul > li > ul > li > ul { - display: initial; + body.menu-open::before { + background: rgba(0,0,0, 0.6); } - #menu > ul:not(#sidebar) > li > ul > li > ul { - display: none; - } - - #menu > ul:not(#sidebar) ul { - margin-left: 20px; - } - - #menu > ul:not(#sidebar) ul { - margin-left: 20px; - } - - #menu > ul:not(#sidebar) a.active { - background: var(--accent-color); - border-radius: 6px; + body.menu-open #show-menu-button { + background: var(--background-highlight); + transform: rotate(90deg); } } \ No newline at end of file diff --git a/src/styles/navbar.css b/src/styles/navbar.css deleted file mode 100644 index a2685fb47..000000000 --- a/src/styles/navbar.css +++ /dev/null @@ -1,152 +0,0 @@ -#menu { - display: flex; - position: fixed; - justify-content: space-between; - align-items: center; - top: 0; - left: 0; - right: 0; - width: inherit; - margin: 0 auto; - overflow: visible; - z-index: 10; - background: var(--bottom-layer); - padding: 8px 16px; -} - -#menu::before { - content: ""; - position: fixed; - left: 0; - right: 0; - background: var(--bottom-layer); - height: 72px; - z-index: -1; -} - -#menu > p > a { - display: flex; - white-space: nowrap; - gap: 10px; - align-items: center; -/* color: var(--text-heading); */ -} - -#main a { - color: var(--accent-color); -} - -#main a:hover { - color: var(--text-heading); -} - -#menu > p > a::before { - content: ""; - display: flex; - background: url("https://raw.githubusercontent.com/zadam/trilium/master/images/icon-color.svg"); - height: 40px; - width: 53px; -} - -/* #logo { - order: 1; - height: 40px; -} */ - -/* #menu > p > strong::before { - content: ""; - display: flex; - background: url("https://notes.cloud.zerebos.com/assets/v0.60.4/images/icon-black.svg"); -} */ - -#menu ul, #sidebar ul { - list-style: none; - padding: 0; - position: relative; -} - -#menu > ul { - margin: 0; - padding: 0; - display: flex; - gap: 30px; -} - -#menu > ul > li { - margin: 0; - padding: 0; - position: relative; -} - - - -#menu > ul > li > p > a { - display: flex; - gap: 10px; - align-items: center; - font-weight: 700; - padding: 16px 0; -} - -/* #menu > ul > li > p > a::after { - content: ''; - border: 4px solid transparent; - border-top: 4px solid white; - display: flex; - align-items: center; - margin-bottom: -8px; -} */ - -#menu > ul > li.submenu-item > p > a::after { - content: ""; - border-color: currentcolor #0000; - border-style: solid; - border-width: .4em .4em 0; - position: relative; - display: flex; - top: 2px; - align-items: center; -/* transform: translateY(-50%); */ -} - -#menu > ul > li.submenu-item.hidden > ul, -#menu > ul > li.submenu-item.hidden > p > a::after{ - display: none; -} - -#menu > ul > li > ul { -/* display: none; */ - opacity: 0; - pointer-events: none; - position: absolute; - top: 40px; - background: #242526; - border-radius: 6px; - min-width: 150px; - right: 50%; - transform: translateX(50%); - padding: 8px; - transition: top 200ms ease, opacity 200ms ease; -} - -#menu > ul > li > ul > li a { - display: flex; - border-radius: 6px; - padding: 3px 6px; - transition: background-color 200ms ease; -} - -#menu > ul > li > ul > li a:hover { - background: rgba(255,255,255,0.05); -} - -#menu > ul > li > ul > li > ul { - display: none; -} - -#menu > ul > li:hover > ul { -/* display: flex; */ - top: 50px; - opacity: 1; - pointer-events: initial; -} \ No newline at end of file diff --git a/src/styles/navbar/header.css b/src/styles/navbar/header.css new file mode 100644 index 000000000..b17e873d1 --- /dev/null +++ b/src/styles/navbar/header.css @@ -0,0 +1,141 @@ +#site-header { + display: flex; + flex-direction: column; + gap: 20px; +} + +#site-header > a { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; +} + + + /* The switch - the box around the slider */ +.switch { + position: relative; + display: inline-block; + width: 60px; + height: 27px; /* 32 */ +} + +/* Hide default HTML checkbox */ +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +/* The slider */ +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: var(--text-primary);; + transition: .4s; + border-radius: 34px; +} + +.slider::before { + position: absolute; + content: ""; + height: 19px; /* 26px */ + width: 19px; + left: 4px; + bottom: 4px; + background-color: white; + transition: .4s; + border-radius: 50%; + z-index: 2; +} + +input:checked + .slider { + background-color: var(--background-highlight); +} + +input:focus + .slider { + box-shadow: 0 0 1px var(--background-highlight); +} + +input:checked + .slider:before { + transform: translateX(33px); /* whole width - slider width - 8px padding*/ +} + + +.theme-selection { + display: flex; + align-items: center; + justify-content: space-between; +} + + +.theme-selection label { + position: relative; +} + +.dark-icon, .light-icon { + display: flex; + opacity: 0; + transition: opacity 400ms ease, color 400ms ease; + pointer-events: none; + position: absolute; + top: 0; + height: 100%; + width: 24px; +} + +/* input ~ .dark-icon { + display: none; +} */ + +input:not(:checked) ~ .light-icon { + opacity: 1; +} + +input:checked ~ .dark-icon { + opacity: 1; +} + +/* input:checked ~ .light-icon { + display: none; +} */ + + +.dark-icon { + left: 5px; + color: var(--text-primary); +} + +.light-icon { + right: 5px; + color: var(--background-highlight); +} + + +.search-item { + display: flex; + align-items: center; + position: relative; +} + +.search-input { + color: var(--text-primary); + background: var(--background-highlight); + outline: 0; + border: 0; + flex: 1; + padding: 5px 5px 5px 32px; + width: 200px; +} + +.search-icon { + display: flex; + color: var(--text-primary); + position: absolute; + width: 20px; + left: 5px; +} \ No newline at end of file diff --git a/src/styles/navbar/index.css b/src/styles/navbar/index.css new file mode 100644 index 000000000..e77988f54 --- /dev/null +++ b/src/styles/navbar/index.css @@ -0,0 +1,2 @@ +@import "./header.css"; +@import "./navbar.css"; \ No newline at end of file diff --git a/src/styles/navbar/navbar.css b/src/styles/navbar/navbar.css new file mode 100644 index 000000000..7e1adf0e8 --- /dev/null +++ b/src/styles/navbar/navbar.css @@ -0,0 +1,141 @@ +#navigation { + display: flex; + flex-direction: column; + padding: 25px; + gap: 10px; +} + +#menu { + order: 1; +/* margin-left: auto; */ + + white-space: nowrap; + + flex: 0; +/* padding: 25px; */ +} + +#menu > ul { + overflow-y: auto; + list-style: none; + padding-left: 0!important; +} + +/* #menu > ul, #menu > div { + width: fit-content; + margin-left: auto; +} */ + +#menu ul { + overflow-y: hidden; + position: relative; + display: flex; + flex-direction: column; + list-style: none; + padding-left: 20px; +} + +#menu li { +/* overflow-y: hidden; */ + position: relative; + display: flex; + flex-direction: column; + list-style: none; + cursor: pointer; +} + +#menu li .collapsible-label { + display: flex; + flex: 1 +} + +#menu li > ul { + transition: height 1000ms ease; +} + +#menu li:not(.expanded) > ul { + height: 0!important; +/* transition: height 1000ms ease; */ +} + +#menu li.expanded > ul { +/* max-height: 500px; */ +} + +#menu p { + display: flex; +} + +#menu li.item > a { + padding-left: 24px; +} + +#menu a { + display: inline-flex; + align-items: center; + justify-content: flex-start; + gap: 5px; + color: var(--text-menu); + text-decoration: none; + border-radius: 6px; + border: 1px solid transparent; + flex: 1; + padding: 2px 6px; + font-weight: 400; +} + +#menu a:hover { + border-color: var(--text-menu); +} + +#menu a.active { + background: var(--background-active); + color: var(--background-secondary); + font-weight: 700; +} + +#menu li ul { + position: relative; +} + +#menu li ul::before { + content: ""; + display: flex; + position: absolute; + top: 5px; + bottom: 5px; + left: 10px; + width: 2px; + background: var(--background-highlight); +} + +.active .collapse-button { + background: none; + color: var(--background-secondary); +} + + +.collapse-button { + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0; + background: var(--background-secondary); + border: 0; + color: var(--text-menu); +/* position: absolute; */ +/* top: 2px; */ +/* left: -18px; */ + transform: rotate(-90deg); + transition: transform 200ms ease; +} + +.expanded > .collapse-button, +.expanded > a > .collapse-button { + transform: rotate(0); +} + +.collapse-button svg { + width: 14px; +} \ No newline at end of file diff --git a/src/styles/popouts/index.css b/src/styles/popouts/index.css new file mode 100644 index 000000000..52ded226e --- /dev/null +++ b/src/styles/popouts/index.css @@ -0,0 +1 @@ +@import "./search.css"; \ No newline at end of file diff --git a/src/styles/popouts/search.css b/src/styles/popouts/search.css new file mode 100644 index 000000000..3ab336906 --- /dev/null +++ b/src/styles/popouts/search.css @@ -0,0 +1,34 @@ +.search-results { + display: flex; + flex-direction: column; + position: fixed; + background: var(--background-highlight); + margin-top: 10px; + border-radius: 12px; + padding: 0; + overflow: hidden; + z-index: 5; +} + +.search-result-item { + display: flex; + flex-direction: column; + padding: 4px 12px; + color: var(--text-primary); +} + +.search-result-item:hover { + cursor: pointer; + background: var(--background-active); + color: var(--text-menu-active); + text-decoration: none; +} + +.search-result-item:hover .search-result-note { + color: var(--text-menu-active); +} + +.search-result-note { + font-size: 12px; + color: var(--text-menu); +} \ No newline at end of file diff --git a/src/styles/sidebar.css b/src/styles/sidebar.css deleted file mode 100644 index acf97f222..000000000 --- a/src/styles/sidebar.css +++ /dev/null @@ -1,66 +0,0 @@ -#sidebar { - flex: var(--pane-size); - height: fit-content; - position: sticky; - top: calc(72px + 1rem); -} - -#sidebar, #sidebar ul { - list-style: none; - padding: 0; - margin: 0; -} - -#sidebar { -/* padding-right: 20px; */ -} - - - -/* #sidebar .title { - text-transform: uppercase; - text-align: center; - letter-spacing: 5px; - color: #777; - font-weight: 700; - border-bottom: 1px solid #777; -} */ - -#sidebar p { - display: flex; - margin: 0; -} - -#sidebar a { - flex: 1; - padding: 6px 6px 6px 12px; - border-radius: 6px; - transition: color 200ms ease, background-color 200ms ease; -} - -#sidebar a:hover { - background: rgba(255,255,255,0.05); -} - -#sidebar > li > ul > li.submenu-item > p > a { - text-transform: uppercase; - font-weight: 700; - color: #555; -} - -#sidebar li > ul { - margin-top: 5px; -} - -#sidebar > li > ul.hasSubmenu, -#sidebar > li > ul > li.submenu-item + li.submenu-item { - margin-top: 20px; -} - -#sidebar a.active, -#sidebar > li > ul > li.submenu-item > p > a.active { - background: var(--accent-color); - /* background: rgba(255, 255, 255, 0.1); */ - color: var(--text-heading); - font-weight: 700; -} \ No newline at end of file diff --git a/src/styles/swagger.css b/src/styles/swagger.css index 472a5043f..310ece9d3 100644 --- a/src/styles/swagger.css +++ b/src/styles/swagger.css @@ -1,5 +1,5 @@ #main .swagger-ui .scheme-container { - background: #20212B; + background: var(--background-secondary); } #main .swagger-ui .opblock .opblock-section-header { @@ -7,7 +7,7 @@ } #main .swagger-ui .opblock-body pre.microlight { - background: #20212B!important; + background: var(--background-secondary) !important; } #main .swagger-ui, @@ -22,6 +22,10 @@ color: var(--text-primary); } + +#main .swagger-ui .opblock .opblock-summary-operation-id, +#main .swagger-ui .opblock .opblock-summary-path, +#main .swagger-ui .opblock .opblock-summary-path__deprecated, #main .swagger-ui .btn, #main .swagger-ui .model, #main .swagger-ui .tab li, @@ -35,11 +39,11 @@ } #main .swagger-ui .model .property.primitive { - color: var(--text-muted); + color: var(--text-menu); } #main .swagger-ui .prop-type { - color: var(--accent-color); + color: var(--text-link); } #main .swagger-ui svg { @@ -82,4 +86,32 @@ #main .swagger-ui .authorization__btn { padding-left: 0; +} + +.swagger-ui .info .title small { + top: 0; + padding: 4px 8px; + background: var(--background-highlight); +} + +#main .swagger-ui .info .title { + display: flex; + align-items: center; + gap: 10px; +} + +#main .swagger-ui .info .title small pre { + color: var(--text-heading); + min-width: 0; + border: 0; + background: none; +} + +#main .swagger-ui .info .title > span { + display: flex; + align-items: center; +} + +.swagger-ui .info .title small.version-stamp { + background: var(--background-highlight); } \ No newline at end of file diff --git a/src/styles/toc.css b/src/styles/toc.css index 6ae6962c4..234c25eef 100644 --- a/src/styles/toc.css +++ b/src/styles/toc.css @@ -1,12 +1,23 @@ -#toc { - flex: var(--pane-size); - position: sticky; - top: calc(72px + 1rem); +#toc-pane { + display: flex; + flex-direction: column; + height: fit-content; + position: sticky; + top: 0; + order: 3; +/* padding: 16px 16px 16px 32px; */ +} + +#toc-pane h3 { + text-transform: uppercase; +} + +#toc { + position: relative; height: fit-content; - background: var(--top-layer); margin: 0; - padding: 16px 16px 16px 32px; border-radius: 6px; + padding: 0 0 0 16px; } #toc, #toc ul { @@ -18,22 +29,59 @@ } #toc li a { +/* position: relative; */ + display: flex; + align-items: center; color: var(--text-heading); transition: color 200ms ease; + white-space: nowrap; } #toc li a:hover, #toc li a.active { - color: var(--accent-color); + color: var(--text-link); + text-decoration: none; +} + +#toc li a::before { + content: ""; + display: flex; + position: absolute; + width: 2px; + height: 16px; + background: transparent; + left: 0; + transition: background-color 200ms ease; +} + +#toc li a.active::before { + background: var(--text-link); } #toc::before { content: ""; display: block; position: absolute; - left: 16px; - top: 20px; + left: 0px; + top: 4px; width: 2px; - height: calc(100% - 40px); - background: rgba(255, 255, 255, 0.1); + height: calc(100% - 8px); + background: var(--background-highlight); +} + +#content h1 a.toc-anchor, +#content h2 a.toc-anchor, +#content h3 a.toc-anchor, +#content h4 a.toc-anchor, +#content h5 a.toc-anchor, +#content h6 a.toc-anchor { + margin-left: 10px; +} + + + +@media (max-width: 1200px) { + #toc-pane { + display: none; + } } \ No newline at end of file From a7edc5e03ef99ffb2cc29904389266da2dc0b11d Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Wed, 27 Sep 2023 22:54:13 -0400 Subject: [PATCH 12/28] Slightly adjust page template --- scripts/build.ts | 2 +- src/templates/page.ejs | 39 +++++++++++++++++++++++++++------------ tsconfig.json | 2 +- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/scripts/build.ts b/scripts/build.ts index d8fea2d7a..a4f3c39b0 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -33,7 +33,7 @@ async function sendTemplates() { } if (process.argv.includes("--only-templates")) { - sendTemplates().catch(console.error); + await sendTemplates(); process.exit(0); } diff --git a/src/templates/page.ejs b/src/templates/page.ejs index 5abba1535..fcf619044 100644 --- a/src/templates/page.ejs +++ b/src/templates/page.ejs @@ -24,7 +24,7 @@ document.addEventListener("DOMContentLoaded", function() { SwaggerUIBundle({ url: `<%= note.getLabelValue("shareSwagger") %>`, - domNode: "#content" + dom_id: "#content" }); }); @@ -43,6 +43,23 @@ <% } %> <%- header %> <%= note.title %><% if (note.noteId !== subRoot.noteId) { %> - <%= subRoot.title %><% } %> + + + + + + "> + + + + + + + "> + + + <% const currentTheme = note.getLabel("shareTheme") === "light" ? "light" : "dark"; @@ -97,12 +114,15 @@ const themeClass = currentTheme === "light" ? " theme-light" : " theme-dark";
    This note was originally clipped from "><%= note.getLabelValue("pageUrl") %>
    <% } %> - <% if (!isEmpty) { %> -
    -

    <%= note.title %>

    + +
    +

    <%= note.title %>

    + <% if (isEmpty && !note.hasVisibleChildren()) { %> +

    This note has no content.

    + <% } else { %> <%- content %> -
    - <% } %> + <% } %> +
    <% if (note.hasVisibleChildren()) { %> - <% } else if (isEmpty) { %> -
    -

    <%= note.title %>

    -

    This note has no content.

    -
    - <% } %> + <% } else %> diff --git a/tsconfig.json b/tsconfig.json index ef08e0c07..ac91e669c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ "resolveJsonModule": true, "strictNullChecks": true, "moduleResolution": "Node16", - "target": "ES2021", + "target": "ES2022", "rootDir": "src", "outDir": "lib/cjs", "module": "Node16" From 4147f2b8d881aaa446256f1762eb70611faa7213 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Wed, 27 Sep 2023 23:18:03 -0400 Subject: [PATCH 13/28] Switch scripts to redesign --- .eslintrc | 3 ++ src/scripts/common/debounce.ts | 11 ++++++ src/scripts/common/parents.ts | 7 ++++ src/scripts/common/parsehtml.ts | 7 ++++ src/scripts/expanders.ts | 23 +++++++++++++ src/scripts/index.ts | 61 +++++++++++++++++++-------------- src/scripts/mobile.ts | 25 ++++++++++++++ src/scripts/navigation/toc.ts | 15 ++++++-- src/scripts/search.ts | 53 ++++++++++++++++++++++++++++ src/scripts/theme.ts | 27 +++++++++++++++ 10 files changed, 204 insertions(+), 28 deletions(-) create mode 100644 src/scripts/common/debounce.ts create mode 100644 src/scripts/common/parents.ts create mode 100644 src/scripts/common/parsehtml.ts create mode 100644 src/scripts/expanders.ts create mode 100644 src/scripts/mobile.ts create mode 100644 src/scripts/search.ts create mode 100644 src/scripts/theme.ts diff --git a/.eslintrc b/.eslintrc index 23653b106..19abc5113 100644 --- a/.eslintrc +++ b/.eslintrc @@ -87,5 +87,8 @@ "wrap-iife": ["error", "inside"], "yield-star-spacing": "error", "yoda": "error" + }, + "globals": { + "NodeJS": "readonly" } } \ No newline at end of file diff --git a/src/scripts/common/debounce.ts b/src/scripts/common/debounce.ts new file mode 100644 index 000000000..a4537d963 --- /dev/null +++ b/src/scripts/common/debounce.ts @@ -0,0 +1,11 @@ +export default function debounce unknown>(executor: T, delay: number) { + let timeout: NodeJS.Timeout | null; + return function(...args: Parameters): void { + const callback = () => { + timeout = null; + Reflect.apply(executor, null, args); + }; + if (timeout) clearTimeout(timeout); + timeout = setTimeout(callback, delay); + }; +} \ No newline at end of file diff --git a/src/scripts/common/parents.ts b/src/scripts/common/parents.ts new file mode 100644 index 000000000..cfe0a7fce --- /dev/null +++ b/src/scripts/common/parents.ts @@ -0,0 +1,7 @@ +export default function parents(el: T, selector: string) { + const result = []; + for (let p = el && el.parentElement; p; p = p.parentElement) { + if (p.matches(selector)) result.push(p); + } + return result; +} \ No newline at end of file diff --git a/src/scripts/common/parsehtml.ts b/src/scripts/common/parsehtml.ts new file mode 100644 index 000000000..2563e1c87 --- /dev/null +++ b/src/scripts/common/parsehtml.ts @@ -0,0 +1,7 @@ +export default function parseHTML(html: string, fragment = false) { + const template = document.createElement("template"); + template.innerHTML = html; + const node = template.content.cloneNode(true); + if (fragment) return node; + return node.childNodes.length > 1 ? node.childNodes : node.childNodes[0]; +} \ No newline at end of file diff --git a/src/scripts/expanders.ts b/src/scripts/expanders.ts new file mode 100644 index 000000000..6749b6852 --- /dev/null +++ b/src/scripts/expanders.ts @@ -0,0 +1,23 @@ +export default function setupExpanders() { + const expanders = Array.from(document.querySelectorAll("#menu .collapse-button")); + for (const ex of expanders) { + ex.addEventListener("click", e => { + e.preventDefault(); + e.stopPropagation(); + // ex.parentElement.parentElement.classList.toggle("expanded"); + ex.closest(".submenu-item")?.classList.toggle("expanded"); + }); + } + + const activeLink = document.querySelector("#menu a.active"); + if (activeLink) { + let parent = activeLink.parentElement; + const mainMenu = document.getElementById("#menu"); + while (parent && parent !== mainMenu) { + if (parent.matches(".submenu-item")) { + parent.classList.add("expanded"); + } + parent = parent.parentElement; + } + } +} \ No newline at end of file diff --git a/src/scripts/index.ts b/src/scripts/index.ts index 88879347b..d95f5f124 100644 --- a/src/scripts/index.ts +++ b/src/scripts/index.ts @@ -1,25 +1,29 @@ -import fixActiveLink from "./fixes/activelink"; -import fixTableHeaders from "./fixes/tableheaders"; +// import fixActiveLink from "./fixes/activelink"; +// import fixTableHeaders from "./fixes/tableheaders"; import highlight from "./other/highlight"; -import buildSidenav from "./navigation/sidenav"; -import buildBreadcrumbs from "./navigation/breadcrumbs"; -import fixSubMenus from "./fixes/submenu"; +// import buildSidenav from "./navigation/sidenav"; +// import buildBreadcrumbs from "./navigation/breadcrumbs"; +// import fixSubMenus from "./fixes/submenu"; import generateTOC from "./navigation/toc"; -import addExternalLinks from "./fixes/externallinks"; -import injectSwagger from "./other/swagger"; -import makeMobileMenu from "./other/mobile"; +// import addExternalLinks from "./fixes/externallinks"; +// import injectSwagger from "./other/swagger"; +// import makeMobileMenu from "./other/mobile"; +import setupExpanders from "./expanders"; +import setupMobileMenu from "./mobile"; +import setupSearch from "./search"; +import setupThemeSelector from "./theme"; -const ETAPI_REF_NOTE_ID = "pPIXi0uwF5GX"; -const HIDDEN_SUBMENUS = ["blog"]; -const EXTERNAL_LINKS = { - EGFtX8Uw96FQ: "https://github.com/zadam/trilium", - dXAKFE0fJtom: "https://discord.gg/eTaTXUgcBr" -}; -const ALIASES = { - WqBnya4Ye8rS: "", - ZapIU17QNEyU: "blog" -}; +// const ETAPI_REF_NOTE_ID = "pPIXi0uwF5GX"; +// const HIDDEN_SUBMENUS = ["blog"]; +// const EXTERNAL_LINKS = { +// EGFtX8Uw96FQ: "https://github.com/zadam/trilium", +// dXAKFE0fJtom: "https://discord.gg/eTaTXUgcBr" +// }; +// const ALIASES = { +// WqBnya4Ye8rS: "", +// ZapIU17QNEyU: "blog" +// }; function $try unknown>(func: T, ...args: Parameters) { try { @@ -44,20 +48,25 @@ function $try unknown>(func: T, ...args: Paramete */ // Perform fixes first -$try(fixActiveLink, ALIASES); -$try(fixTableHeaders); -$try(fixSubMenus, HIDDEN_SUBMENUS); -$try(addExternalLinks, EXTERNAL_LINKS); +// $try(fixActiveLink, ALIASES); +// $try(fixTableHeaders); +// $try(fixSubMenus, HIDDEN_SUBMENUS); +// $try(addExternalLinks, EXTERNAL_LINKS); // Now layout changes -$try(buildBreadcrumbs); -$try(buildSidenav); +// $try(buildBreadcrumbs); +// $try(buildSidenav); $try(generateTOC); // Finally, other features $try(highlight); -$try(injectSwagger, ETAPI_REF_NOTE_ID); -$try(makeMobileMenu); +// $try(injectSwagger, ETAPI_REF_NOTE_ID); + +$try(setupExpanders); +$try(setupMobileMenu); +$try(setupSearch); +$try(setupThemeSelector); +// $try(makeMobileMenu); /** * This was removed because both the title change and the opengraph diff --git a/src/scripts/mobile.ts b/src/scripts/mobile.ts new file mode 100644 index 000000000..721e5946d --- /dev/null +++ b/src/scripts/mobile.ts @@ -0,0 +1,25 @@ +import parents from "./common/parents"; + + +export default function setupMobileMenu() { + function toggleMobileMenu(event: MouseEvent) { + event.stopPropagation(); // Don't prevent default for links + + const isOpen = document.body.classList.contains("menu-open"); + if (isOpen) return document.body.classList.remove("menu-open"); + return document.body.classList.add("menu-open"); + } + + const showMenuButton = document.getElementById("show-menu-button"); + showMenuButton?.addEventListener("click", toggleMobileMenu); + + window.addEventListener("click", e => { + const isOpen = document.body.classList.contains("menu-open"); + if (!isOpen) return; // This listener is only to close + + // If the click was anywhere in the mobile nav, don't close + if (parents(e.target as HTMLElement, "#left-pane").length) return; + return toggleMobileMenu(e); + }); + +} \ No newline at end of file diff --git a/src/scripts/navigation/toc.ts b/src/scripts/navigation/toc.ts index 052da9583..f9dbb830f 100644 --- a/src/scripts/navigation/toc.ts +++ b/src/scripts/navigation/toc.ts @@ -6,6 +6,7 @@ const buildItem = (heading: Element) => { const slug = slugify(heading.textContent ?? ""); const anchor = document.createElement("a"); + anchor.className = "toc-anchor"; anchor.setAttribute("href", `#${slug}`); anchor.setAttribute("name", slug); anchor.setAttribute("id", slug); @@ -108,8 +109,18 @@ export default function generateTOC() { changeLinkState(); window.addEventListener("scroll", changeLinkState); + // Create the toc wrapper + const pane = document.createElement("div"); + pane.id = "toc-pane"; + + // Create the header + const header = document.createElement("h3"); + header.textContent = "On This Page"; + pane.append(header); + pane.append(toc); + // Finally, add the ToC to the end of layout. Give the layout a class for adjusting widths. - const layout = document.querySelector("#layout"); + const layout = document.querySelector("#right-pane"); layout?.classList.add("toc"); - layout?.append(toc); + layout?.append(pane); } \ No newline at end of file diff --git a/src/scripts/search.ts b/src/scripts/search.ts new file mode 100644 index 000000000..d992b430e --- /dev/null +++ b/src/scripts/search.ts @@ -0,0 +1,53 @@ +import debounce from "./common/debounce"; +import parents from "./common/parents"; +import parseHTML from "./common/parsehtml"; + + +interface SearchResults { + results: SearchResult[]; +} + +interface SearchResult { + id: string; + title: string; + score: number; + path: string; +} + + +export default function setupSearch() { + const searchInput: HTMLInputElement = document.querySelector(".search-input")!; + searchInput.addEventListener("keyup", debounce(async () => { + // console.log("CHANGE EVENT"); + const current = document.body.dataset.noteId; + const query = searchInput.value; + if (query.length < 3) return; + const resp = await fetch(`api/search/${current}?query=${query}`); + const json = await resp.json() as SearchResults; + const results = json.results.slice(0, 5); + const lines = [`
    `]; + for (const result of results) { + lines.push(`
    ${result.title}
    ${result.path || "Home"}
    `); + } + lines.push("
    "); + + const container = parseHTML(lines.join("")) as HTMLDivElement; + // console.log(container, lines); + const rect = searchInput.getBoundingClientRect(); + container.style.top = `${rect.bottom}px`; + container.style.left = `${rect.left}px`; + container.style.minWidth = `${rect.width}px`; + + const existing = document.querySelector(".search-results"); + if (existing) existing.replaceWith(container); + else document.body.append(container); + }, 500)); + + window.addEventListener("click", e => { + const existing = document.querySelector(".search-results"); + if (!existing) return; + // If the click was anywhere search components ignore it + if (parents(e.target as HTMLElement, ".search-results,.search-item").length) return; + if (existing) existing.remove(); + }); +} \ No newline at end of file diff --git a/src/scripts/theme.ts b/src/scripts/theme.ts new file mode 100644 index 000000000..06aec7edb --- /dev/null +++ b/src/scripts/theme.ts @@ -0,0 +1,27 @@ +export default function setupThemeSelector() { + const themeSwitch: HTMLInputElement = document.querySelector(".theme-selection input")!; + themeSwitch?.addEventListener("change", () => { + if (themeSwitch.checked) { + document.body.classList.add("theme-dark"); + document.body.classList.remove("theme-light"); + localStorage.setItem("theme", "dark"); + } + else { + document.body.classList.remove("theme-dark"); + document.body.classList.add("theme-light"); + localStorage.setItem("theme", "light"); + } + }); + + const preference = localStorage.getItem("theme"); + if (preference) { + if (preference === "dark") { + document.body.classList.add("theme-dark"); + document.body.classList.remove("theme-light"); + } + else { + document.body.classList.remove("theme-dark"); + document.body.classList.add("theme-light"); + } + } +} \ No newline at end of file From a8bb2f110bcc8dc04977591bbe20f5019726e196 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Thu, 28 Sep 2023 00:14:44 -0400 Subject: [PATCH 14/28] Some important QoL changes --- src/scripts/expanders.ts | 32 +++++++++++++++++++++++++++++++- src/scripts/search.ts | 13 +++++++++++-- src/scripts/theme.ts | 25 +++++++++++++------------ src/styles/index.css | 18 +++++++++++++++++- src/styles/navbar/header.css | 1 + 5 files changed, 73 insertions(+), 16 deletions(-) diff --git a/src/scripts/expanders.ts b/src/scripts/expanders.ts index 6749b6852..5c835c2a0 100644 --- a/src/scripts/expanders.ts +++ b/src/scripts/expanders.ts @@ -1,3 +1,28 @@ +function anchorToId(anchor: HTMLAnchorElement) { + return anchor.href.replace("./", ""); +} + +const stored = localStorage.getItem("expanded") ?? "[]"; +let parsed: string[]; +try { + parsed = JSON.parse(stored) as string[]; +} +catch (e) { + parsed = []; +} +const state = new Set(parsed); +const submenus = Array.from(document.querySelectorAll("#menu .submenu-item")); +for (const sub of submenus) { + try { + if (state.has(anchorToId(sub.children[0] as HTMLAnchorElement))) sub.classList.add("expanded"); + } + catch (e) { + // TODO: create logger + console.warn("Could not restore expanded state"); // eslint-disable-line no-console + console.error(e); // eslint-disable-line no-console + } +} + export default function setupExpanders() { const expanders = Array.from(document.querySelectorAll("#menu .collapse-button")); for (const ex of expanders) { @@ -6,15 +31,20 @@ export default function setupExpanders() { e.stopPropagation(); // ex.parentElement.parentElement.classList.toggle("expanded"); ex.closest(".submenu-item")?.classList.toggle("expanded"); + const id = anchorToId(ex.closest("a")!); + if (state.has(id)) state.delete(id); + else state.add(id); + localStorage.setItem("expanded", JSON.stringify([...state])); }); } + // In case a linked article lead to a new tree const activeLink = document.querySelector("#menu a.active"); if (activeLink) { let parent = activeLink.parentElement; const mainMenu = document.getElementById("#menu"); while (parent && parent !== mainMenu) { - if (parent.matches(".submenu-item")) { + if (parent.matches(".submenu-item") && !parent.classList.contains("expanded")) { parent.classList.add("expanded"); } parent = parent.parentElement; diff --git a/src/scripts/search.ts b/src/scripts/search.ts index d992b430e..fe25390f4 100644 --- a/src/scripts/search.ts +++ b/src/scripts/search.ts @@ -14,9 +14,18 @@ interface SearchResult { path: string; } +function buildResultItem(result: SearchResult) { + return ` +
    ${result.title}
    +
    ${result.path || "Home"}
    +
    `; +} + export default function setupSearch() { const searchInput: HTMLInputElement = document.querySelector(".search-input")!; + + // TODO: move listener to another function searchInput.addEventListener("keyup", debounce(async () => { // console.log("CHANGE EVENT"); const current = document.body.dataset.noteId; @@ -27,7 +36,7 @@ export default function setupSearch() { const results = json.results.slice(0, 5); const lines = [`
    `]; for (const result of results) { - lines.push(`
    ${result.title}
    ${result.path || "Home"}
    `); + lines.push(buildResultItem(result)); } lines.push("
    "); @@ -39,7 +48,7 @@ export default function setupSearch() { container.style.minWidth = `${rect.width}px`; const existing = document.querySelector(".search-results"); - if (existing) existing.replaceWith(container); + if (existing) existing.replaceWith(container); // TODO: consider updating existing container and never removing else document.body.append(container); }, 500)); diff --git a/src/scripts/theme.ts b/src/scripts/theme.ts index 06aec7edb..1e3d57a3b 100644 --- a/src/scripts/theme.ts +++ b/src/scripts/theme.ts @@ -1,5 +1,18 @@ +const preference = localStorage.getItem("theme"); +if (preference) { + if (preference === "dark") { + document.body.classList.add("theme-dark"); + document.body.classList.remove("theme-light"); + } + else { + document.body.classList.remove("theme-dark"); + document.body.classList.add("theme-light"); + } +} + export default function setupThemeSelector() { const themeSwitch: HTMLInputElement = document.querySelector(".theme-selection input")!; + // TODO: consolidate this with initialization (DRY) themeSwitch?.addEventListener("change", () => { if (themeSwitch.checked) { document.body.classList.add("theme-dark"); @@ -12,16 +25,4 @@ export default function setupThemeSelector() { localStorage.setItem("theme", "light"); } }); - - const preference = localStorage.getItem("theme"); - if (preference) { - if (preference === "dark") { - document.body.classList.add("theme-dark"); - document.body.classList.remove("theme-light"); - } - else { - document.body.classList.remove("theme-dark"); - document.body.classList.add("theme-light"); - } - } } \ No newline at end of file diff --git a/src/styles/index.css b/src/styles/index.css index 8ae5f3043..d8d9e6824 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -86,9 +86,25 @@ a:hover { } - +.ck-content code, .ck-content pre { color: var(--text-primary); + background-color: var(--background-secondary); + border: 1px solid var(--background-active); + border-radius: 6px; + white-space: pre; +} + +.ck-content code { + padding: 2px 5px; +} + +.ck-content pre code { + border: 0; +} + +.ck-content pre { + overflow: auto; } #content h1, diff --git a/src/styles/navbar/header.css b/src/styles/navbar/header.css index b17e873d1..c43c1326c 100644 --- a/src/styles/navbar/header.css +++ b/src/styles/navbar/header.css @@ -130,6 +130,7 @@ input:checked ~ .dark-icon { flex: 1; padding: 5px 5px 5px 32px; width: 200px; + border-radius: 6px; } .search-icon { From bdfe86ba1aec4ee685d795c8c58e3c0ba6af411c Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Thu, 28 Sep 2023 00:24:52 -0400 Subject: [PATCH 15/28] Remove dead code and reorganize slightly --- src/scripts/fixes/activelink.ts | 27 -------- src/scripts/fixes/externallinks.ts | 19 ------ src/scripts/fixes/submenu.ts | 45 ------------- src/scripts/fixes/tableheaders.ts | 10 --- src/scripts/index.ts | 14 ++-- src/scripts/{ => modules}/expanders.ts | 0 src/scripts/{other => modules}/highlight.ts | 0 src/scripts/{ => modules}/mobile.ts | 2 +- src/scripts/{ => modules}/search.ts | 6 +- src/scripts/{ => modules}/theme.ts | 0 src/scripts/{navigation => modules}/toc.ts | 2 +- src/scripts/navigation/breadcrumbs.ts | 53 --------------- src/scripts/navigation/sidenav.ts | 30 --------- src/scripts/other/mobile.ts | 74 --------------------- src/scripts/other/swagger.ts | 35 ---------- 15 files changed, 12 insertions(+), 305 deletions(-) delete mode 100644 src/scripts/fixes/activelink.ts delete mode 100644 src/scripts/fixes/externallinks.ts delete mode 100644 src/scripts/fixes/submenu.ts delete mode 100644 src/scripts/fixes/tableheaders.ts rename src/scripts/{ => modules}/expanders.ts (100%) rename src/scripts/{other => modules}/highlight.ts (100%) rename src/scripts/{ => modules}/mobile.ts (95%) rename src/scripts/{ => modules}/search.ts (94%) rename src/scripts/{ => modules}/theme.ts (100%) rename src/scripts/{navigation => modules}/toc.ts (99%) delete mode 100644 src/scripts/navigation/breadcrumbs.ts delete mode 100644 src/scripts/navigation/sidenav.ts delete mode 100644 src/scripts/other/mobile.ts delete mode 100644 src/scripts/other/swagger.ts diff --git a/src/scripts/fixes/activelink.ts b/src/scripts/fixes/activelink.ts deleted file mode 100644 index 1f9506173..000000000 --- a/src/scripts/fixes/activelink.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * For some reason Trilium share chooses to have the - * active link be just a rather than a true - * link with a special style. This fixes that and - * turns the back into an actual link - * with the correct note id. - * - * TODO: make it fix aliases too - */ -export default function fixActiveLink(aliases: Record) { - const active = document.querySelector("#menu strong"); - if (!active) return; // Something is really wrong - - // Currently active note id is stored on body - const id = document.body.dataset.noteId!; - const href = aliases[id] ?? id; - - // Create the new link - const link = document.createElement("a"); - link.className = "type-text active"; - link.href = ``; - link.textContent = active.textContent; - link.href = `./${href}`; - - // Replace the - active.replaceWith(link); -} \ No newline at end of file diff --git a/src/scripts/fixes/externallinks.ts b/src/scripts/fixes/externallinks.ts deleted file mode 100644 index bdbac9333..000000000 --- a/src/scripts/fixes/externallinks.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * This function just lets us map some note links to be external links. - * This was originally designed to link the Trilium GitHub via a note. - */ -export default function addExternalLinks(mapping: Record) { - for (const id in mapping) { - const links = document.querySelectorAll(`a[href*="${id}"]`); - if (!links.length) { - // eslint-disable-next-line no-console - console.warn(`Could not find link to note id ${id}`); - continue; - } - for (const link of links) { - link.href = mapping[id]; - link.target = "_blank"; - link.rel = "noopener noreferrer"; - } - } -} \ No newline at end of file diff --git a/src/scripts/fixes/submenu.ts b/src/scripts/fixes/submenu.ts deleted file mode 100644 index 6b167769b..000000000 --- a/src/scripts/fixes/submenu.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * General premise here is to find all submenus/sublists - * and give them a submenu class. Then any list item - * that contains one of these submenus gets a submenu-item - * class. Additionally, any sublist that itself has a - * sublist is given the class hasSubmenu. - */ -export default function fixSubMenu(submenuBlacklist: string[]) { - // Get every list item in the navigation - const items = document.querySelectorAll("#menu ul li"); - for (const item of items) { - const sublist = item.querySelector("ul"); - - // If the list item has no submenu, ignore and move on - if (!sublist) continue; - - // There seems to be some weird edge cases where a - // sublist ul is added but has no list items and - // in trilium the corresponding note has no children - if (!sublist.children.length) { - sublist.remove(); - continue; - } - - // If the submenu is part of our blacklist, then - // give it the hidden class. This is for pages - // that have no subcategories and only a long - // list of subpages. - const ihtml = item.innerHTML; - for (const bl of submenuBlacklist) { - if (!ihtml.includes(bl)) continue; - item.classList.add("hidden"); - } - - // Finally, add the corresponding classes to the elements - item.classList.add("submenu-item"); - sublist.classList.add("submenu"); - - // Currently, this is only used by the sidebar styles to - // adjust margins. TODO: Might be worth investigating a - // different method. - if (sublist.querySelector("ul")?.children.length) sublist.classList.add("hasSubmenu"); - - } -} \ No newline at end of file diff --git a/src/scripts/fixes/tableheaders.ts b/src/scripts/fixes/tableheaders.ts deleted file mode 100644 index d67174fe1..000000000 --- a/src/scripts/fixes/tableheaders.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * This specifically fixes when you have empty corners - * like on tables with column and row headers - */ -export default function fixTableHeaders() { - const headers = document.querySelectorAll("th"); - for (const header of headers) { - if (!header.textContent?.trim()) header.classList.add("empty"); - } -} \ No newline at end of file diff --git a/src/scripts/index.ts b/src/scripts/index.ts index d95f5f124..cc1ce5158 100644 --- a/src/scripts/index.ts +++ b/src/scripts/index.ts @@ -1,17 +1,17 @@ // import fixActiveLink from "./fixes/activelink"; // import fixTableHeaders from "./fixes/tableheaders"; -import highlight from "./other/highlight"; +import highlight from "./modules/highlight"; // import buildSidenav from "./navigation/sidenav"; // import buildBreadcrumbs from "./navigation/breadcrumbs"; // import fixSubMenus from "./fixes/submenu"; -import generateTOC from "./navigation/toc"; +import setupToC from "./modules/toc"; // import addExternalLinks from "./fixes/externallinks"; // import injectSwagger from "./other/swagger"; // import makeMobileMenu from "./other/mobile"; -import setupExpanders from "./expanders"; -import setupMobileMenu from "./mobile"; -import setupSearch from "./search"; -import setupThemeSelector from "./theme"; +import setupExpanders from "./modules/expanders"; +import setupMobileMenu from "./modules/mobile"; +import setupSearch from "./modules/search"; +import setupThemeSelector from "./modules/theme"; // const ETAPI_REF_NOTE_ID = "pPIXi0uwF5GX"; @@ -56,7 +56,7 @@ function $try unknown>(func: T, ...args: Paramete // Now layout changes // $try(buildBreadcrumbs); // $try(buildSidenav); -$try(generateTOC); +$try(setupToC); // Finally, other features $try(highlight); diff --git a/src/scripts/expanders.ts b/src/scripts/modules/expanders.ts similarity index 100% rename from src/scripts/expanders.ts rename to src/scripts/modules/expanders.ts diff --git a/src/scripts/other/highlight.ts b/src/scripts/modules/highlight.ts similarity index 100% rename from src/scripts/other/highlight.ts rename to src/scripts/modules/highlight.ts diff --git a/src/scripts/mobile.ts b/src/scripts/modules/mobile.ts similarity index 95% rename from src/scripts/mobile.ts rename to src/scripts/modules/mobile.ts index 721e5946d..a5c211106 100644 --- a/src/scripts/mobile.ts +++ b/src/scripts/modules/mobile.ts @@ -1,4 +1,4 @@ -import parents from "./common/parents"; +import parents from "../common/parents"; export default function setupMobileMenu() { diff --git a/src/scripts/search.ts b/src/scripts/modules/search.ts similarity index 94% rename from src/scripts/search.ts rename to src/scripts/modules/search.ts index fe25390f4..c7c77c014 100644 --- a/src/scripts/search.ts +++ b/src/scripts/modules/search.ts @@ -1,6 +1,6 @@ -import debounce from "./common/debounce"; -import parents from "./common/parents"; -import parseHTML from "./common/parsehtml"; +import debounce from "../common/debounce"; +import parents from "../common/parents"; +import parseHTML from "../common/parsehtml"; interface SearchResults { diff --git a/src/scripts/theme.ts b/src/scripts/modules/theme.ts similarity index 100% rename from src/scripts/theme.ts rename to src/scripts/modules/theme.ts diff --git a/src/scripts/navigation/toc.ts b/src/scripts/modules/toc.ts similarity index 99% rename from src/scripts/navigation/toc.ts rename to src/scripts/modules/toc.ts index f9dbb830f..5bd8b3dc2 100644 --- a/src/scripts/navigation/toc.ts +++ b/src/scripts/modules/toc.ts @@ -39,7 +39,7 @@ const buildItem = (heading: Element) => { * h2 > h4 > h1 but rather h2 > h3 > h2 so you change by 1 and end * up at the same level as before. */ -export default function generateTOC() { +export default function setupToC() { // Get all headings from the page and map them to already built elements const headings = Array.from(document.querySelectorAll("h1, h2, h3, h4, h5, h6")); if (headings.length <= 1) return; // But if there are none, let's do nothing diff --git a/src/scripts/navigation/breadcrumbs.ts b/src/scripts/navigation/breadcrumbs.ts deleted file mode 100644 index 16bd58409..000000000 --- a/src/scripts/navigation/breadcrumbs.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * This build breadcrumb-style navigation using the existing - * tree menu generated by Trilium. The concept is to find - * the currently active link (fixed by an earlier script) - * and follow that up to the root part of the menu - */ -export default function buildBreadcrumbsFromNav() { - const container = document.createElement("ul"); - container.id = "breadcrumbs"; - - // Find currently active link - const current = document.querySelector("#menu .active"); - if (!current) return; // Something went really wrong - - // Clone the link and add it to the front of the breadcrumb list - const firstItem = document.createElement("li"); - firstItem.append(current.cloneNode(true)); - container.prepend(firstItem); - - // Walk the sublists upward until the root - let next = current.closest("ul"); - while (next) { - const clone = next?.previousElementSibling?.querySelector("a")?.cloneNode(true); - if (!clone) continue; // This also means something went very wrong - - // Get the parent of the previous and add to front of breadcrumbs - const ancestorItem = document.createElement("li"); - ancestorItem.append(clone); - container.prepend(ancestorItem); - - // Get the next sublist upward - next = next?.parentElement?.closest("ul") ?? null; - - // We are not yet at root, continue - if (next) continue; - - // Since next == null, we are at root, let's ue a pretty logo - clone.textContent = ""; - const logo = document.createElement("img"); - logo.src = "https://raw.githubusercontent.com/zadam/trilium/master/images/icon-black.svg"; - clone.appendChild(logo); - } - - // We don't need this at root - if (container.children.length === 1) return; - - // Add breadcrumb container to the main layout - const main = document.getElementById("main"); - main?.prepend(container); - - // Scroll to the active breadcrumb in case of overflow - container.scrollLeft = container.scrollWidth - container.clientWidth; -} \ No newline at end of file diff --git a/src/scripts/navigation/sidenav.ts b/src/scripts/navigation/sidenav.ts deleted file mode 100644 index 0663155f4..000000000 --- a/src/scripts/navigation/sidenav.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * This generates a docs-style sidebar navigation using the Trilium tree - */ -export default function addSideNav() { - // Give all tier 2 list items category class... TODO: should this be done elsewhere? - const categories = document.querySelectorAll("#menu > ul > li > ul > li"); - for (const cat of categories) cat.classList.add("category"); - - // Use the active link as our reference point - const active = document.querySelector("#menu .active"); - - // From the active link, find the nearest category that is also a submenu item - // If there is none, try to grab the nearest hidden submenu item (for non- - // category style pages) - const treeToClone = active?.closest(".category.submenu-item") ?? active?.closest(".submenu-item.hidden"); - if (!treeToClone) return; // If neither exist, 99% chance it's the homepage - - // Clone the found node and add it to the sidebar - const sidebar = document.createElement("ul"); - sidebar.id = "sidebar"; - const clone = treeToClone.cloneNode(true); - sidebar.append(clone); - - // If there's only a single item... probably not worth having a sidebar - if (sidebar.querySelectorAll("li").length <= 1) return; - - // Add the sidebar to the front of the layout container - const layout = document.querySelector("#layout"); - layout?.prepend(sidebar); -} \ No newline at end of file diff --git a/src/scripts/other/mobile.ts b/src/scripts/other/mobile.ts deleted file mode 100644 index 857ca1268..000000000 --- a/src/scripts/other/mobile.ts +++ /dev/null @@ -1,74 +0,0 @@ -const parseHTML = (html: string, fragment = false) => { - const template = document.createElement("template"); - template.innerHTML = html; - const node = template.content.cloneNode(true); - if (fragment) return node; - return node.childNodes.length > 1 ? node.childNodes : node.childNodes[0]; -}; - -const goBackString = `
  • ↩ Back to Main Menu

  • `; -const menuButtonString = ``; - -const isMobileAgent = /Mobi/.test(navigator.userAgent); -const isMobileSize = window.matchMedia("only screen and (max-width: 760px)").matches; - -// TODO: maybe re-work this to have #sidebar be later than #menu -// then use #menu.expanded ~ #sidebar for showing the sidebar -// that way less JS is involved to make mobile work properly -export default function makeMobileMenu() { - if (!isMobileAgent && !isMobileSize) return; // If nothing indicates mobile, bail out - const menuWrap = document.querySelector("#menu"); - const mainMenu = document.querySelector("#menu > ul"); - if (!menuWrap || !mainMenu) return; // Something went really wrong... - const sidebar = document.querySelector("#sidebar"); - - const toggleMenu = (event: MouseEvent) => { - event.stopPropagation(); // Don't preventDefault to allow links - - const isVisible = menuWrap.classList.contains("expanded"); - - if (isVisible) { - menuWrap.classList.remove("expanded"); - if (sidebar) { - mainMenu.classList.remove("active"); - if (!sidebar.classList.contains("active")) sidebar.classList.add("active"); - } - } - else { - menuWrap.classList.add("expanded"); - } - }; - - const menuButton = parseHTML(menuButtonString) as HTMLButtonElement; - menuButton.addEventListener("click", toggleMenu); - - window.addEventListener("click", e => { - const isVisible = menuWrap.classList.contains("expanded"); - if (!isVisible) return; // This is only for when the menu is open - toggleMenu(e); - }); - - - if (sidebar) { - const goBackButton = parseHTML(goBackString) as HTMLLIElement; - goBackButton.addEventListener("click", (e) => { - e.preventDefault(); - e.stopPropagation(); - mainMenu.classList.add("active"); - sidebar.classList.remove("active"); - }); - - sidebar.prepend(goBackButton); - } - - - if (sidebar) sidebar.classList.add("active"); - else mainMenu.classList.add("active"); - - menuWrap.append(menuButton); - if (sidebar) menuWrap.append(sidebar); -} \ No newline at end of file diff --git a/src/scripts/other/swagger.ts b/src/scripts/other/swagger.ts deleted file mode 100644 index 572250bd8..000000000 --- a/src/scripts/other/swagger.ts +++ /dev/null @@ -1,35 +0,0 @@ -import SwaggerUI, {SwaggerUIOptions} from "swagger-ui"; - - -declare const SwaggerUIBundle: typeof SwaggerUI; -const opts: SwaggerUIOptions = { - url: "https://raw.githubusercontent.com/zadam/trilium/master/src/etapi/etapi.openapi.yaml" -}; - -/** - * Add swagger to the ETAPI page! - */ -export default function injectSwagger(expectedNoteId: string) { - // Check if it's the ETAPI page, otherwise avoid dependency - const noteId = document.body.dataset.noteId; - if (noteId !== expectedNoteId) return; // TODO: make this a param - const main = document.getElementById("main")!; - main.innerHTML = ""; - opts.domNode = main; - // Add the swagger-ui styles from unpkg - // TODO: make this hosted by trilium - const link = document.createElement("link"); - link.rel = "stylesheet"; - link.href = "https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui.css"; - document.head.append(link); - - // Add the swagger-ui script too - // TODO: make this hosted by trilium - const script = document.createElement("script"); - script.src = "https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui-bundle.js"; - script.addEventListener("load", () => { - // This immediately generated the swagger-ui in the main element - SwaggerUIBundle(opts); // eslint-disable-line new-cap - }); - document.head.append(script); -} \ No newline at end of file From bde6d836256ba9f22405fda0aac477d30db42701 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Thu, 28 Sep 2023 20:59:04 -0400 Subject: [PATCH 16/28] Fix css priority for mobile --- src/styles/content.css | 31 +++++++++++++++++++ src/styles/index.css | 67 ++++-------------------------------------- src/styles/layout.css | 32 ++++++++++++++++++++ src/styles/swagger.css | 26 ++++++++++++++++ 4 files changed, 94 insertions(+), 62 deletions(-) create mode 100644 src/styles/content.css create mode 100644 src/styles/layout.css diff --git a/src/styles/content.css b/src/styles/content.css new file mode 100644 index 000000000..46f4c64b6 --- /dev/null +++ b/src/styles/content.css @@ -0,0 +1,31 @@ +.ck-content code, +.ck-content pre { + color: var(--text-primary); + background-color: var(--background-secondary); + border: 1px solid var(--background-active); + border-radius: 6px; + white-space: pre; +} + +.ck-content code { + padding: 2px 5px; +} + +.ck-content pre code { + border: 0; +} + +.ck-content pre { + overflow: auto; +} + +#content h1, +#content h2, +#content h3, +#content h4, +#content h5, +#content h6 { + color: var(--text-heading); + border-bottom: 1px solid var(--background-highlight); + padding-bottom: 5px; +} \ No newline at end of file diff --git a/src/styles/index.css b/src/styles/index.css index d8d9e6824..8fc4a2fc9 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -6,6 +6,9 @@ @import "./navbar/index.css"; @import "./popouts/index.css"; +@import "./layout.css"; +@import "./content.css"; + @import "./mobile.css"; @@ -23,6 +26,7 @@ --text-menu: #AAAAAA; --text-link: #87CEFA; --text-menu-active: #000000; + color-scheme: dark; } body.theme-light { @@ -34,6 +38,7 @@ body.theme-light { --text-heading: #000000; --text-menu: #333333; --text-link: #0000ff; + color-scheme: light; } body { @@ -52,68 +57,6 @@ a:hover { text-decoration: underline; } -#split-pane { - display: flex; - flex-direction: row; - gap: 50px; -} - -#left-pane { - display: flex; - width: calc((100vw - 900px) / 2); - min-width: fit-content; - height: calc(100vh); - background: var(--background-secondary); - border-right: 5px solid var(--background-highlight); - justify-content: flex-end; - position: sticky; - top: 0; -} - -#right-pane { - display: flex; - margin: 0 auto; - gap: 40px; - flex: 1; - padding-bottom: 500px; - padding-right: 50px; -} - -#main { - order: 2; - max-width: 900px; - flex: 1; -} -.ck-content code, -.ck-content pre { - color: var(--text-primary); - background-color: var(--background-secondary); - border: 1px solid var(--background-active); - border-radius: 6px; - white-space: pre; -} -.ck-content code { - padding: 2px 5px; -} - -.ck-content pre code { - border: 0; -} - -.ck-content pre { - overflow: auto; -} - -#content h1, -#content h2, -#content h3, -#content h4, -#content h5, -#content h6 { - color: var(--text-heading); - border-bottom: 1px solid var(--background-highlight); - padding-bottom: 5px; -} \ No newline at end of file diff --git a/src/styles/layout.css b/src/styles/layout.css new file mode 100644 index 000000000..d22dff133 --- /dev/null +++ b/src/styles/layout.css @@ -0,0 +1,32 @@ +#split-pane { + display: flex; + flex-direction: row; + gap: 50px; +} + +#left-pane { + display: flex; + width: calc((100vw - 900px) / 2); + min-width: fit-content; + height: calc(100vh); + background: var(--background-secondary); + border-right: 5px solid var(--background-highlight); + justify-content: flex-end; + position: sticky; + top: 0; +} + +#right-pane { + display: flex; + margin: 0 auto; + gap: 40px; + flex: 1; + padding-bottom: 500px; + padding-right: 50px; +} + +#main { + order: 2; + max-width: 900px; + flex: 1; +} \ No newline at end of file diff --git a/src/styles/swagger.css b/src/styles/swagger.css index 310ece9d3..3db300f2a 100644 --- a/src/styles/swagger.css +++ b/src/styles/swagger.css @@ -6,6 +6,11 @@ background: rgba(0, 0, 0, 0.2); } +#main .swagger-ui section.models { + border: 1px solid var(--background-highlight); +} + +#main .swagger-ui section.models .model-container, #main .swagger-ui .opblock-body pre.microlight { background: var(--background-secondary) !important; } @@ -114,4 +119,25 @@ .swagger-ui .info .title small.version-stamp { background: var(--background-highlight); +} + + +#main .swagger-ui h1, +#main .swagger-ui h2, +#main .swagger-ui h3, +#main .swagger-ui h4, +#main .swagger-ui h5, +#main .swagger-ui h6 { + border: 0; +} + + +#main .swagger-ui input[type="email"], +#main .swagger-ui input[type="file"], +#main .swagger-ui input[type="password"], +#main .swagger-ui input[type="search"], +#main .swagger-ui input[type="text"], +#main .swagger-ui textarea { + background-color: var(--background-highlight); + border: 0; } \ No newline at end of file From 074ac0b725122ba04fb7f7158b98839ae87697d9 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Thu, 28 Sep 2023 20:59:17 -0400 Subject: [PATCH 17/28] Make swagger load from trilium directly --- src/scripts/index.ts | 19 ++++--- src/scripts/modules/expanders.ts | 84 ++++++++++++++++--------------- src/templates/page.ejs | 86 ++++++++++++++++++++------------ 3 files changed, 109 insertions(+), 80 deletions(-) diff --git a/src/scripts/index.ts b/src/scripts/index.ts index cc1ce5158..6164f04fc 100644 --- a/src/scripts/index.ts +++ b/src/scripts/index.ts @@ -56,12 +56,19 @@ function $try unknown>(func: T, ...args: Paramete // Now layout changes // $try(buildBreadcrumbs); // $try(buildSidenav); + +// TODO: Determine the difficulty of adding this in +// trilium directly using JSDOM. $try(setupToC); // Finally, other features +// TODO: how difficult this would be to implement via +// templates or trilium $try(highlight); // $try(injectSwagger, ETAPI_REF_NOTE_ID); +// "Standard" Modules I would recommend the new share +// theme have $try(setupExpanders); $try(setupMobileMenu); $try(setupSearch); @@ -69,14 +76,10 @@ $try(setupThemeSelector); // $try(makeMobileMenu); /** - * This was removed because both the title change and the opengraph - * additions are now handled by a traefik plugin that rewrites - * the body of the http request, that way the change does not - * require client-side JS. This is important for sites wishing - * to display that information. + * This no longer uses a traefik plugin and instead a custom + * template being served through Trilium. * - * TODO: Determine how reasonable it would be to move more - * of these modules over to a traefik rewrite plugin. This gives - * a better experience to end users, SEO, etc. + * TODO: Figure out some good attributes to use to populate + * this inside the template to make it more dynamic */ // $try(addOpenGraphMeta); diff --git a/src/scripts/modules/expanders.ts b/src/scripts/modules/expanders.ts index 5c835c2a0..ebcb2ba77 100644 --- a/src/scripts/modules/expanders.ts +++ b/src/scripts/modules/expanders.ts @@ -1,53 +1,59 @@ -function anchorToId(anchor: HTMLAnchorElement) { - return anchor.href.replace("./", ""); -} +// function anchorToId(anchor: HTMLAnchorElement) { +// return anchor.href.replace("./", ""); +// } +// +// const stored = localStorage.getItem("expanded") ?? "[]"; +// let parsed: string[]; +// try { +// parsed = JSON.parse(stored) as string[]; +// } +// catch (e) { +// parsed = []; +// } +// const state = new Set(parsed); +// const submenus = Array.from(document.querySelectorAll("#menu .submenu-item")); +// for (const sub of submenus) { +// try { +// if (state.has(anchorToId(sub.children[0] as HTMLAnchorElement))) sub.classList.add("expanded"); +// } +// catch (e) { +// // TODO: create logger +// console.warn("Could not restore expanded state"); // eslint-disable-line no-console +// console.error(e); // eslint-disable-line no-console +// } +// } -const stored = localStorage.getItem("expanded") ?? "[]"; -let parsed: string[]; -try { - parsed = JSON.parse(stored) as string[]; -} -catch (e) { - parsed = []; -} -const state = new Set(parsed); -const submenus = Array.from(document.querySelectorAll("#menu .submenu-item")); -for (const sub of submenus) { - try { - if (state.has(anchorToId(sub.children[0] as HTMLAnchorElement))) sub.classList.add("expanded"); - } - catch (e) { - // TODO: create logger - console.warn("Could not restore expanded state"); // eslint-disable-line no-console - console.error(e); // eslint-disable-line no-console +// TODO: Swap this system to use type-book for full-link category + +// In case a linked article lead to a new tree +const activeLink = document.querySelector("#menu a.active"); +if (activeLink) { + let parent = activeLink.parentElement; + const mainMenu = document.getElementById("#menu"); + while (parent && parent !== mainMenu) { + if (parent.matches(".submenu-item") && !parent.classList.contains("expanded")) { + parent.classList.add("expanded"); + } + parent = parent.parentElement; } } export default function setupExpanders() { - const expanders = Array.from(document.querySelectorAll("#menu .collapse-button")); + const expanders = Array.from(document.querySelectorAll("#menu .submenu-item")); for (const ex of expanders) { ex.addEventListener("click", e => { + if ((e.target as Element).closest(".submenu-item,.item") !== ex) return; e.preventDefault(); e.stopPropagation(); // ex.parentElement.parentElement.classList.toggle("expanded"); - ex.closest(".submenu-item")?.classList.toggle("expanded"); - const id = anchorToId(ex.closest("a")!); - if (state.has(id)) state.delete(id); - else state.add(id); - localStorage.setItem("expanded", JSON.stringify([...state])); + ex.classList.toggle("expanded"); + // const id = anchorToId(ex.closest("a")!); + // if (state.has(id)) state.delete(id); + // else state.add(id); + // // TODO: be able to remove all submenus of currently collapsed + // localStorage.setItem("expanded", JSON.stringify([...state])); }); } - // In case a linked article lead to a new tree - const activeLink = document.querySelector("#menu a.active"); - if (activeLink) { - let parent = activeLink.parentElement; - const mainMenu = document.getElementById("#menu"); - while (parent && parent !== mainMenu) { - if (parent.matches(".submenu-item") && !parent.classList.contains("expanded")) { - parent.classList.add("expanded"); - } - parent = parent.parentElement; - } - } + } \ No newline at end of file diff --git a/src/templates/page.ejs b/src/templates/page.ejs index fcf619044..b702131ac 100644 --- a/src/templates/page.ejs +++ b/src/templates/page.ejs @@ -18,13 +18,35 @@ <% } %> <% if (note.hasLabel("shareSwagger")) { %> - - + + @@ -43,6 +65,7 @@ <% } %> <%- header %> <%= note.title %><% if (note.noteId !== subRoot.noteId) { %> - <%= subRoot.title %><% } %> + @@ -107,40 +130,37 @@ const themeClass = currentTheme === "light" ? " theme-light" : " theme-dark";
    -
    - +
    - <% if (note.hasLabel("pageUrl")) { %> -
    This note was originally clipped from "><%= note.getLabelValue("pageUrl") %>
    - <% } %> - - -
    -

    <%= note.title %>

    - <% if (isEmpty && !note.hasVisibleChildren()) { %> -

    This note has no content.

    - <% } else { %> - <%- content %> - <% } %> -
    - - <% if (note.hasVisibleChildren()) { %> - - <% } else %> -
    + +
      + <% + const action = note.type === "book" ? "getChildNotes" : "getVisibleChildNotes"; + for (const childNote of note[action]()) { + %> +
    • + <%= childNote.title %> +
    • + <% } %> +
    + + <% } else %> +
    From e7a3f6d17e95e0d3be56224641276da54dae8dfb Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Fri, 29 Sep 2023 01:43:39 -0400 Subject: [PATCH 18/28] Generate ToC in templates --- package.json | 2 +- scripts/build.ts | 7 +- src/scripts/index.ts | 2 - src/scripts/modules/toc.ts | 126 +++++++---------------------------- src/scripts/test.ts | 83 +++++++++++++++++++++++ src/styles/childlinks.css | 35 +++++----- src/styles/externallinks.css | 2 +- src/templates/page.ejs | 53 ++++++++++++++- src/templates/toc_item.ejs | 19 ++++++ 9 files changed, 200 insertions(+), 129 deletions(-) create mode 100644 src/scripts/test.ts create mode 100644 src/templates/toc_item.ejs diff --git a/package.json b/package.json index 75d438fc0..1b94b9e61 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "build-styles": "esrun scripts/build.ts -- --module=styles", "templates": "esrun scripts/build.ts -- --only-templates", "dist": "esrun scripts/build.ts -- --minify", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "esrun src/scripts/test.ts" }, "author": "", "license": "ISC", diff --git a/scripts/build.ts b/scripts/build.ts index a4f3c39b0..ac17d931a 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -20,13 +20,14 @@ if (process.env.TRILIUM_ETAPI_TOKEN) tepi.token(process.env.TRILIUM_ETAPI_TOKEN) const templateMap: Record = { - "src/templates/page.ejs": process.env.PAGE_TEMPLATE_ID!, - "src/templates/tree_item.ejs": process.env.ITEM_TEMPLATE_ID!, + page: process.env.PAGE_TEMPLATE_ID!, + tree_item: process.env.ITEM_TEMPLATE_ID!, + toc_item: process.env.TOC_TEMPLATE_ID!, }; async function sendTemplates() { for (const template in templateMap) { - const templatePath = path.join(rootDir, template); + const templatePath = path.join(rootDir, "src", "templates", `${template}.ejs`); const contents = fs.readFileSync(templatePath).toString(); await tepi.putNoteContentById(templateMap[template], contents); } diff --git a/src/scripts/index.ts b/src/scripts/index.ts index 6164f04fc..b05c31441 100644 --- a/src/scripts/index.ts +++ b/src/scripts/index.ts @@ -57,8 +57,6 @@ function $try unknown>(func: T, ...args: Paramete // $try(buildBreadcrumbs); // $try(buildSidenav); -// TODO: Determine the difficulty of adding this in -// trilium directly using JSDOM. $try(setupToC); // Finally, other features diff --git a/src/scripts/modules/toc.ts b/src/scripts/modules/toc.ts index 5bd8b3dc2..f38b1de1d 100644 --- a/src/scripts/modules/toc.ts +++ b/src/scripts/modules/toc.ts @@ -1,99 +1,34 @@ -const slugify = (text: string) => text.toLowerCase().replace(/[^\w]/g, "-"); - -const getDepth = (el: Element) => parseInt(el.tagName.replace("H","").replace("h","")); - -const buildItem = (heading: Element) => { - const slug = slugify(heading.textContent ?? ""); - - const anchor = document.createElement("a"); - anchor.className = "toc-anchor"; - anchor.setAttribute("href", `#${slug}`); - anchor.setAttribute("name", slug); - anchor.setAttribute("id", slug); - anchor.textContent = "#"; - - const link = document.createElement("a"); - link.setAttribute("href", `#${slug}`); - link.textContent = heading.textContent; - link.addEventListener("click", e => { - const target = document.querySelector(`#${slug}`); - if (!target) return; - - e.preventDefault(); - e.stopPropagation(); - - target.scrollIntoView({behavior: "smooth"}); - }); - - heading.append(anchor); - - const li = document.createElement("li"); - li.append(link); - return li; -}; - /** - * Generate a ToC from all heading elements in the main content area. - * This should go to full h6 depth and not be too opinionated. It - * does assume a "sensible" structure in that you don't go from - * h2 > h4 > h1 but rather h2 > h3 > h2 so you change by 1 and end - * up at the same level as before. + * The ToC is now generated in the page template so + * it even exists for users without client-side js + * and that means it loads with the page so it avoids + * all potential reshuffling or layout recalculations. + * + * So, all this function needs to do is make the links + * perform smooth animation, and adjust the "active" + * entry as the user scrolls. */ export default function setupToC() { - // Get all headings from the page and map them to already built elements - const headings = Array.from(document.querySelectorAll("h1, h2, h3, h4, h5, h6")); - if (headings.length <= 1) return; // But if there are none, let's do nothing - const items = headings.map(h => buildItem(h)); - - // Setup the ToC list - const toc = document.createElement("ul"); - toc.id = "toc"; + const toc = document.getElementById("toc"); + if (!toc) return; - // Get the depth of the first content heading on the page. - // This depth will be used as reference for all other headings. - // headings[0] === the

    from Trilium - const firstDepth = getDepth(headings[1]); + // Get all relevant elements + const sections = document.getElementById("content")!.querySelectorAll("h2, h3, h4, h5, h6"); + const links = toc.querySelectorAll("a"); - // Loop over ALL headings including the first - for (let h = 0; h < headings.length; h++) { - // Get current heading and determine depth - const current = headings[h]; - const currentDepth = getDepth(current); - - // If it's the same depth as our first heading, add to ToC - if (currentDepth === firstDepth) toc.append(items[h]); - - // If this is the last element then it will have already - // been added as a child or as same depth as first - let nextIndex = h + 1; - if (nextIndex >= headings.length) continue; - - // Time to find all children of this heading - const children = []; - const childDepth = currentDepth + 1; - let depthOfNext = getDepth(headings[nextIndex]); - while (depthOfNext > currentDepth) { - // If it's the expected depth, add as child - if (depthOfNext === childDepth) children.push(nextIndex); - nextIndex++; - - // If the next index is valid, grab the depth for next loop - // TODO: could this be done cleaner with a for loop? - if (nextIndex < headings.length) depthOfNext = getDepth(headings[nextIndex]); - else depthOfNext = currentDepth; // If the index was invalid, break loop - } - - // If this heading had children, add them as children - if (children.length) { - const ul = document.createElement("ul"); - for (const c of children) ul.append(items[c]); - items[h].append(ul); - } + // Setup smooth scroll on click + for (const link of links) { + link.addEventListener("click", e => { + const target = document.querySelector(link.getAttribute("href")!); + if (!target) return; + e.preventDefault(); + e.stopPropagation(); + + target.scrollIntoView({behavior: "smooth"}); + }); } // Setup a moving "active" in the ToC that adjusts with the scroll state - const sections = headings.slice(1); - const links = toc.querySelectorAll("a"); function changeLinkState() { let index = sections.length; @@ -108,19 +43,4 @@ export default function setupToC() { // Initial render changeLinkState(); window.addEventListener("scroll", changeLinkState); - - // Create the toc wrapper - const pane = document.createElement("div"); - pane.id = "toc-pane"; - - // Create the header - const header = document.createElement("h3"); - header.textContent = "On This Page"; - pane.append(header); - pane.append(toc); - - // Finally, add the ToC to the end of layout. Give the layout a class for adjusting widths. - const layout = document.querySelector("#right-pane"); - layout?.classList.add("toc"); - layout?.append(pane); } \ No newline at end of file diff --git a/src/scripts/test.ts b/src/scripts/test.ts new file mode 100644 index 000000000..01a0c62a4 --- /dev/null +++ b/src/scripts/test.ts @@ -0,0 +1,83 @@ +/* eslint-disable no-console */ + +/** + * This script was used for testing ToC generation in the page template... + * TODO: find a better way to integrate ts/js into the templates so I'm + * not debugging on the fly constantly. + */ +// const data = `

    Trilium  really does rock! Don't believe me? Well this entire website was made using the shared notes feature inside Trilium with a little bit of extra CSS and JS also contained in Trilium.

    It turns Trilium into an insanely powerful WYSIWYG website creator. But that's just a side feature of Trilium, it's so much more powerful with endless possibilities.

    Why It Rocks

    If somehow you aren't already convinced, take a look below for even more reasons why Trilium rocks!

    Built-in Features

    This section is shamelessly borrowed from Trilium's README.

    Community Addons

    Nriver maintains an awesome list of addons for Trilium made by the community. Check out the official list on GitHub. We do mirror the list here on the Showcase page if you just want a quick look.

    Custom Scripts

    In addition to using community made scripts, widgets, themes, and everything in between, Trilium leaves things open-ended for you the end-user. You can script as much or as little as you like inside Trilium. You can automate all kinds of workflows, do data analysis, or even simple things like set a keybind to open a specific note. The world is your oyster as they say, and Trilium is your world. Pretend that made sense.

     

    About This Site

    This website is not at all affiliated with Trilium Notes or its creator(s). The site is broken up into a few main sections that you can see in the navigation bar at the top of the page. At a high level, there's two sections targeting end-users, two sections targeting developers, and one meant for everyone.

    Status

    This site is still a work-in-progress! Writing documentation isn't the most fun thing in the world so this will just be something I work on when I have free time. You'll usually find me working on one of my Trilium-related addons, Trilium itself, or my other open-source project: BetterDiscord.

    Goals

    Rather than saying some specific goals of what this site strives to be, I'll say what it strives not to be; This site is not meant to be a complete recreation of the Wiki with every detail and page included. It is meant to be a (mostly) one-stop shop for users and developers alike looking to supplement their knowledge. It may at some point expand and include everything from the wiki because users tend to prefer a fancier UI like this, but it is not the end-goal.

    Contributing

    Since this entire site is just a share from my personal Trilium instance, there is no easy way to contribute new pages or fixes for typos. At some point I will create a GitHub repository for this site's supplementary CSS and JS, and that repository can also act as a home for issues and discussion. But who knows, maybe within that time frame I'll think of some clever way to introduce contributions.

     

    `; +const data = `

    Frontend API

    The frontend api supports two styles, regular scripts that are run with the current app and note context, and widgets that export an object to Trilium to be used in the UI. In both cases, the frontend api of Trilium is available to scripts running in the frontend context as global variable api. The members and methods of the api can be seen on the FrontendScriptApi page.

    Scripts

    Scripts don't have any special requirements. They can be run at will using the execute button in the UI or they can be configured to run at certain times using Attributes on the note containing the script.

    Global Events

    This attribute is called #run and it can have any of the following values:

    • frontendStartup - executes on frontend upon startup.
    • mobileStartup - executes on mobile frontend upon startup.
    • backendStartup - executes on backend upon startup.
    • hourly - executes once an hour on backend.
    • daily - executes once a day on backend.

    Entity Events

    These events are triggered by certain relations to other notes. Meaning that the script is triggered only if the note has this script attached to it through relations (or it can inherit it).

    • runOnNoteCreation - executes when note is created on backend.
    • runOnNoteTitleChange - executes when note title is changed (includes note creation as well).
    • runOnNoteContentChange - executes when note content is changed (includes note creation as well).
    • runOnNoteChange - executes when note is changed (includes note creation as well).
    • runOnNoteDeletion - executes when note is being deleted.
    • runOnBranchCreation - executes when a branch is created. Branch is a link between parent note and child note and is created e.g. when cloning or moving note.
    • runOnBranchDeletion - executes when a branch is delete. Branch is a link between parent note and child note and is deleted e.g. when moving note (old branch/link is deleted).
    • runOnChildNoteCreation - executes when new note is created under this note.
    • runOnAttributeCreation - executes when new attribute is created under this note.
    • runOnAttributeChange - executes when attribute is changed under this note.

    Widgets

    Conversely to scripts, widgets do have some specific requirements in order to work. A widget must:

    • Extend BasicWidget or one of it's subclasses.
    • Create a new instance and assign it to module.exports.
    • Define a parentWidget member to determine where it should be displayed.
    • Define a position (integer) that determines the location via sort order.
    • Have a #widget attribute on the containing note.
    • Create, render, and return your element in the render function.

    parentWidget

    • left-pane - This renders the widget on the left side of the screen where the note tree lives.
    • center-pane - This renders the widget in the center of the layout in the same location that notes and splits appear.
    • note-detail-pane - This renders the widget with the note in the center pane. This means it can appear multiple times with splits.
    • right-pane - This renders the widget to the right of any opened notes.

    Tutorial

    For more information on building widgets, take a look at Widget Basics.

    `; +const headingRe = /()(.+?)(<\/h[1-6]>)/g; + + +// const slugify = (text: string) => text.toLowerCase().replace(/[^\w]/g, "-"); +// const modified = data2.replaceAll(headingRe, (...match: RegExpMatchArray) => { +// match[0] = match[0].replace(match[3], `#${match[3]}`); +// return match[0]; +// }); + +// console.log(modified); + + +const headingMatches = [...data.matchAll(headingRe)]; + +interface ToCEntry { + level: number; + name: string; + children: ToCEntry[]; +} + +const level = (m: RegExpMatchArray) => parseInt(m[1].replace(/[]+/g, "")); + +const toc: ToCEntry[] = [ + { + level: level(headingMatches[0]), + name: headingMatches[0][2], + children: [] + } +]; +const last = (arr = toc) => arr[arr.length - 1]; +const makeEntry = (m: RegExpMatchArray): ToCEntry => ({level: level(m), name: m[2], children: []}); + +const getLevelArr = (lvl: number, arr = toc): ToCEntry[] => { + if (arr[0].level === lvl) return arr; + const top = last(arr); + return top.children.length ? getLevelArr(lvl, top.children) : top.children; +}; + + +for (let m = 1; m < headingMatches.length; m++) { + const target = getLevelArr(level(headingMatches[m])); + target.push(makeEntry(headingMatches[m])); +} + +console.log(JSON.stringify(toc, null, 4)); + +// const end = (arr = toc): ToCEntry => { +// const top = last(arr); +// return top.children.length ? end(top.children) : top; +// }; +// console.log(end()); + +// const previousEntry = last(); +// if (previousEntry.level === cLvl) { +// toc.push(makeEntry(current)); +// } +// else if (previousEntry.level === cLvl - 1) { +// previousEntry.children.push(makeEntry(current)); +// } +// else if (previousEntry.level < cLvl) { +// const target = findParentEntry(previous[2]) ?? end(); +// // console.log(previous[2], target, current[2]); +// const plvl = level(previous); +// if (plvl === cLvl) { +// target.children.push(makeEntry(current)); +// } +// else if (plvl === cLvl - 1) { +// const subitem = target.children.find(e => e.name === previous[2])!; +// subitem.children.push(makeEntry(current)); +// } +// } +// else if (previousEntry.level > cLvl) { +// toc.push(makeEntry(current)); +// } \ No newline at end of file diff --git a/src/styles/childlinks.css b/src/styles/childlinks.css index 094f48efb..7bb2b6a3e 100644 --- a/src/styles/childlinks.css +++ b/src/styles/childlinks.css @@ -14,6 +14,10 @@ border-top: 1px solid var(--background-highlight); } +.no-content + #childLinks { + border: 0; +} + #childLinks ul { padding: 0; gap: 10px; @@ -22,32 +26,31 @@ } #childLinks li { - background: var(--background-highlight); - padding: 2px 12px; - border-radius: 12px; -} - - -.no-content + #childLinks { - border: 0; -} - -#childLinks.grid li { padding: 0; + background: var(--background-highlight); + border-radius: 12px; } -#childLinks.grid li a { - padding: 50px; - border-radius: 12px; +#childLinks li a { + padding: 2px 12px; background: var(--background-highlight); - color: var(--text-primary); + border-radius: 12px; transform: translateY(0); transition: transform 200ms ease, background-color 200ms ease, color 200ms ease; } -#childLinks.grid li a:hover { +#childLinks li a:hover { background: var(--background-active); color: var(--background-secondary); text-decoration: none; + transform: translateY(-2px); +} + +#childLinks.grid li a { + padding: 50px; + color: var(--text-primary); +} + +#childLinks.grid li a:hover { transform: translateY(-5px); } \ No newline at end of file diff --git a/src/styles/externallinks.css b/src/styles/externallinks.css index 9beffff9a..f8e5b6eda 100644 --- a/src/styles/externallinks.css +++ b/src/styles/externallinks.css @@ -4,7 +4,7 @@ a[href^="https://"] { gap: 6px; } -#main a[href^="https://"] { +#content a[href^="https://"] { padding-right: 6px; } diff --git a/src/templates/page.ejs b/src/templates/page.ejs index b702131ac..480f65c3c 100644 --- a/src/templates/page.ejs +++ b/src/templates/page.ejs @@ -87,6 +87,13 @@ const customServerYml = `- url: "{protocol}://{domain}:{port}/etapi" <% const currentTheme = note.getLabel("shareTheme") === "light" ? "light" : "dark"; const themeClass = currentTheme === "light" ? " theme-light" : " theme-dark"; +const headingRe = /()(.+?)(<\/h[1-6]>)/g; +const headingMatches = [...content.matchAll(headingRe)]; +const slugify = (text) => text.toLowerCase().replace(/[^\w]/g, "-"); +content = content.replaceAll(headingRe, (...match) => { + match[0] = match[0].replace(match[3], `#${match[3]}`); + return match[0]; +}); %>
    @@ -151,16 +158,56 @@ const themeClass = currentTheme === "light" ? " theme-light" : " theme-dark"; <% const action = note.type === "book" ? "getChildNotes" : "getVisibleChildNotes"; for (const childNote of note[action]()) { + const isExternalLink = childNote.hasLabel("shareExternal"); + const linkHref = isExternalLink ? childNote.getLabelValue("shareExternal") : `./${childNote.shareId}`; + const target = isExternalLink ? ` target="_blank" rel="noopener noreferrer"` : ""; %>
  • - <%= childNote.title %> + ><%= childNote.title %>
  • <% } %> - <% } else %> + <% } %>
    + <% + if (headingMatches.length > 1) { + const level = (m) => parseInt(m[1].replace(/[]+/g, "")); + + const toc = [ + { + level: level(headingMatches[0]), + name: headingMatches[0][2], + children: [] + } + ]; + const last = (arr = toc) => arr[arr.length - 1]; + const makeEntry = (m) => ({level: level(m), name: m[2], children: []}); + const getLevelArr = (lvl, arr = toc) => { + if (arr[0].level === lvl) return arr; + const top = last(arr); + return top.children.length ? getLevelArr(lvl, top.children) : top.children; + }; + + + for (let m = 1; m < headingMatches.length; m++) { + const target = getLevelArr(level(headingMatches[m])); + target.push(makeEntry(headingMatches[m])); + } + %> +
    +

    On This Page

    +
      + <% + let active = true; + for (const entry of toc) { + %> + <%- include('toc_item', {entry, active}) %> + <% active = false %> + <% } %> +
    +
    + <% } %> diff --git a/src/templates/toc_item.ejs b/src/templates/toc_item.ejs new file mode 100644 index 000000000..d613e52f9 --- /dev/null +++ b/src/templates/toc_item.ejs @@ -0,0 +1,19 @@ +<% +const slugify = (text) => text.toLowerCase().replace(/[^\w]/g, "-"); +const slug = slugify(entry.name); +%> + + +
  • + class="active"<% } %>> + <%= entry.name %> + + + <% if (entry.children.length) { %> +
      + <% for (const subentry of entry.children) { %> + <%- include('toc_item', {entry: subentry, active: false}) %> + <% } %> +
    + <% } %> +
  • From 5391521c0855869807ff1210e7cd6ab25d665755 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Sat, 30 Sep 2023 00:13:37 -0400 Subject: [PATCH 19/28] Enable sliding categories and extract TODOs --- TODO.md | 29 +++++++++++++++ src/scripts/index.ts | 61 -------------------------------- src/scripts/modules/expanders.ts | 40 +++------------------ src/scripts/modules/highlight.ts | 3 -- src/scripts/modules/search.ts | 3 +- src/scripts/modules/theme.ts | 1 - src/scripts/test.ts | 5 --- src/styles/navbar/navbar.css | 2 +- src/templates/page.ejs | 18 ++++------ src/templates/toc_item.ejs | 4 +-- src/templates/tree_item.ejs | 8 ++--- 11 files changed, 48 insertions(+), 126 deletions(-) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 000000000..4abd37e19 --- /dev/null +++ b/TODO.md @@ -0,0 +1,29 @@ +# TODOs + +This doc contains a list of TODOs taken from the code and organized. This _does not_ includes things like upcoming features or fixes. + +## Scripts + +- Create a logger +- Modify esbuild to allow for a development build to contain debug logs +- Modify custom highlight.js plugin to include highlighting for jQuery global functions +- Either move highlight.js inclusion to template or use a mapping of note IDs to files +- Adjust search to use separate function (clean code) +- Consider never removing the search results from the page and update container instead +- Consolidate theme initialization (DRY) + +## Styles + +- + +## Templates + +- Consider adding highlight.js into the templates instead of scripts +- Find a better way to integrate ts/js to templates (maybe via includes?) + +## Other + +- Create a logical set of attributes for setting open-graph/twitter metadata +- Consider making book type notes explicitly required for full-link category + - This lets text type notes still have content but require clicking arrow to expand +- Find a way to better map template to notes and allow for automatically creating new ones \ No newline at end of file diff --git a/src/scripts/index.ts b/src/scripts/index.ts index b05c31441..38f0933b1 100644 --- a/src/scripts/index.ts +++ b/src/scripts/index.ts @@ -1,30 +1,11 @@ -// import fixActiveLink from "./fixes/activelink"; -// import fixTableHeaders from "./fixes/tableheaders"; import highlight from "./modules/highlight"; -// import buildSidenav from "./navigation/sidenav"; -// import buildBreadcrumbs from "./navigation/breadcrumbs"; -// import fixSubMenus from "./fixes/submenu"; import setupToC from "./modules/toc"; -// import addExternalLinks from "./fixes/externallinks"; -// import injectSwagger from "./other/swagger"; -// import makeMobileMenu from "./other/mobile"; import setupExpanders from "./modules/expanders"; import setupMobileMenu from "./modules/mobile"; import setupSearch from "./modules/search"; import setupThemeSelector from "./modules/theme"; -// const ETAPI_REF_NOTE_ID = "pPIXi0uwF5GX"; -// const HIDDEN_SUBMENUS = ["blog"]; -// const EXTERNAL_LINKS = { -// EGFtX8Uw96FQ: "https://github.com/zadam/trilium", -// dXAKFE0fJtom: "https://discord.gg/eTaTXUgcBr" -// }; -// const ALIASES = { -// WqBnya4Ye8rS: "", -// ZapIU17QNEyU: "blog" -// }; - function $try unknown>(func: T, ...args: Parameters) { try { func.apply(func, args); @@ -33,51 +14,9 @@ function $try unknown>(func: T, ...args: Paramete console.error(e); // eslint-disable-line no-console } } - -/** - * Lots of these functions seem to depend on each other indirectly - * through DOM changes or classes or what-have-you. This is - * obviously not ideal as it makes things less clear, and also - * makes TypeScript less helpful. - * - * TODO: Find a good way of restructuring that allows things - * to act a bit more harmoniously. - * - * TODO: Make use of esbuild's define api to enable a debug - * build that contains all the console logs and such. - */ - -// Perform fixes first -// $try(fixActiveLink, ALIASES); -// $try(fixTableHeaders); -// $try(fixSubMenus, HIDDEN_SUBMENUS); -// $try(addExternalLinks, EXTERNAL_LINKS); - -// Now layout changes -// $try(buildBreadcrumbs); -// $try(buildSidenav); - $try(setupToC); - -// Finally, other features -// TODO: how difficult this would be to implement via -// templates or trilium $try(highlight); -// $try(injectSwagger, ETAPI_REF_NOTE_ID); - -// "Standard" Modules I would recommend the new share -// theme have $try(setupExpanders); $try(setupMobileMenu); $try(setupSearch); $try(setupThemeSelector); -// $try(makeMobileMenu); - -/** - * This no longer uses a traefik plugin and instead a custom - * template being served through Trilium. - * - * TODO: Figure out some good attributes to use to populate - * this inside the template to make it more dynamic - */ -// $try(addOpenGraphMeta); diff --git a/src/scripts/modules/expanders.ts b/src/scripts/modules/expanders.ts index ebcb2ba77..276f34507 100644 --- a/src/scripts/modules/expanders.ts +++ b/src/scripts/modules/expanders.ts @@ -1,30 +1,3 @@ -// function anchorToId(anchor: HTMLAnchorElement) { -// return anchor.href.replace("./", ""); -// } -// -// const stored = localStorage.getItem("expanded") ?? "[]"; -// let parsed: string[]; -// try { -// parsed = JSON.parse(stored) as string[]; -// } -// catch (e) { -// parsed = []; -// } -// const state = new Set(parsed); -// const submenus = Array.from(document.querySelectorAll("#menu .submenu-item")); -// for (const sub of submenus) { -// try { -// if (state.has(anchorToId(sub.children[0] as HTMLAnchorElement))) sub.classList.add("expanded"); -// } -// catch (e) { -// // TODO: create logger -// console.warn("Could not restore expanded state"); // eslint-disable-line no-console -// console.error(e); // eslint-disable-line no-console -// } -// } - -// TODO: Swap this system to use type-book for full-link category - // In case a linked article lead to a new tree const activeLink = document.querySelector("#menu a.active"); if (activeLink) { @@ -45,15 +18,10 @@ export default function setupExpanders() { if ((e.target as Element).closest(".submenu-item,.item") !== ex) return; e.preventDefault(); e.stopPropagation(); - // ex.parentElement.parentElement.classList.toggle("expanded"); - ex.classList.toggle("expanded"); - // const id = anchorToId(ex.closest("a")!); - // if (state.has(id)) state.delete(id); - // else state.add(id); - // // TODO: be able to remove all submenus of currently collapsed - // localStorage.setItem("expanded", JSON.stringify([...state])); + const ul = ex.querySelector("ul")!; + ul.style.height = `${ul.scrollHeight}px`; + setTimeout(() => ex.classList.toggle("expanded"), 1); + setTimeout(() => ul.style.height = ``, 200); }); } - - } \ No newline at end of file diff --git a/src/scripts/modules/highlight.ts b/src/scripts/modules/highlight.ts index 2465c5a68..5d59e7708 100644 --- a/src/scripts/modules/highlight.ts +++ b/src/scripts/modules/highlight.ts @@ -19,7 +19,6 @@ const highlightJQuery: HLJSPlugin = { result.value = result.value.replaceAll(/([^A-Za-z0-9.])\$\((.+)\)/g, function(match, prefix, variable) { return `${prefix}$(${variable})`; }); - // TODO: add highlighting for static calls like $.ajax } }; @@ -32,14 +31,12 @@ export default function addHljs() { if (!codeblocks.length) return; // If there are none, don't add dependency // Add the hightlight.js styles from the child note of this script - // TODO: make this a mapping const link = document.createElement("link"); link.rel = "stylesheet"; link.href = "api/notes/cVaK9ZJwx5Hs/download"; document.head.append(link); // Add the highlight.js script too - // TODO: make this a mappin as well const script = document.createElement("script"); script.src = "api/notes/6PVElIem02b5/download"; script.addEventListener("load", () => { diff --git a/src/scripts/modules/search.ts b/src/scripts/modules/search.ts index c7c77c014..f9c362da5 100644 --- a/src/scripts/modules/search.ts +++ b/src/scripts/modules/search.ts @@ -25,7 +25,6 @@ function buildResultItem(result: SearchResult) { export default function setupSearch() { const searchInput: HTMLInputElement = document.querySelector(".search-input")!; - // TODO: move listener to another function searchInput.addEventListener("keyup", debounce(async () => { // console.log("CHANGE EVENT"); const current = document.body.dataset.noteId; @@ -48,7 +47,7 @@ export default function setupSearch() { container.style.minWidth = `${rect.width}px`; const existing = document.querySelector(".search-results"); - if (existing) existing.replaceWith(container); // TODO: consider updating existing container and never removing + if (existing) existing.replaceWith(container); else document.body.append(container); }, 500)); diff --git a/src/scripts/modules/theme.ts b/src/scripts/modules/theme.ts index 1e3d57a3b..35a2400c2 100644 --- a/src/scripts/modules/theme.ts +++ b/src/scripts/modules/theme.ts @@ -12,7 +12,6 @@ if (preference) { export default function setupThemeSelector() { const themeSwitch: HTMLInputElement = document.querySelector(".theme-selection input")!; - // TODO: consolidate this with initialization (DRY) themeSwitch?.addEventListener("change", () => { if (themeSwitch.checked) { document.body.classList.add("theme-dark"); diff --git a/src/scripts/test.ts b/src/scripts/test.ts index 01a0c62a4..d809aa06a 100644 --- a/src/scripts/test.ts +++ b/src/scripts/test.ts @@ -1,10 +1,5 @@ /* eslint-disable no-console */ -/** - * This script was used for testing ToC generation in the page template... - * TODO: find a better way to integrate ts/js into the templates so I'm - * not debugging on the fly constantly. - */ // const data = `

    Trilium  really does rock! Don't believe me? Well this entire website was made using the shared notes feature inside Trilium with a little bit of extra CSS and JS also contained in Trilium.

    It turns Trilium into an insanely powerful WYSIWYG website creator. But that's just a side feature of Trilium, it's so much more powerful with endless possibilities.

    Why It Rocks

    If somehow you aren't already convinced, take a look below for even more reasons why Trilium rocks!

    Built-in Features

    This section is shamelessly borrowed from Trilium's README.

    Community Addons

    Nriver maintains an awesome list of addons for Trilium made by the community. Check out the official list on GitHub. We do mirror the list here on the Showcase page if you just want a quick look.

    Custom Scripts

    In addition to using community made scripts, widgets, themes, and everything in between, Trilium leaves things open-ended for you the end-user. You can script as much or as little as you like inside Trilium. You can automate all kinds of workflows, do data analysis, or even simple things like set a keybind to open a specific note. The world is your oyster as they say, and Trilium is your world. Pretend that made sense.

     

    About This Site

    This website is not at all affiliated with Trilium Notes or its creator(s). The site is broken up into a few main sections that you can see in the navigation bar at the top of the page. At a high level, there's two sections targeting end-users, two sections targeting developers, and one meant for everyone.

    Status

    This site is still a work-in-progress! Writing documentation isn't the most fun thing in the world so this will just be something I work on when I have free time. You'll usually find me working on one of my Trilium-related addons, Trilium itself, or my other open-source project: BetterDiscord.

    Goals

    Rather than saying some specific goals of what this site strives to be, I'll say what it strives not to be; This site is not meant to be a complete recreation of the Wiki with every detail and page included. It is meant to be a (mostly) one-stop shop for users and developers alike looking to supplement their knowledge. It may at some point expand and include everything from the wiki because users tend to prefer a fancier UI like this, but it is not the end-goal.

    Contributing

    Since this entire site is just a share from my personal Trilium instance, there is no easy way to contribute new pages or fixes for typos. At some point I will create a GitHub repository for this site's supplementary CSS and JS, and that repository can also act as a home for issues and discussion. But who knows, maybe within that time frame I'll think of some clever way to introduce contributions.

     

    `; const data = `

    Frontend API

    The frontend api supports two styles, regular scripts that are run with the current app and note context, and widgets that export an object to Trilium to be used in the UI. In both cases, the frontend api of Trilium is available to scripts running in the frontend context as global variable api. The members and methods of the api can be seen on the FrontendScriptApi page.

    Scripts

    Scripts don't have any special requirements. They can be run at will using the execute button in the UI or they can be configured to run at certain times using Attributes on the note containing the script.

    Global Events

    This attribute is called #run and it can have any of the following values:

    • frontendStartup - executes on frontend upon startup.
    • mobileStartup - executes on mobile frontend upon startup.
    • backendStartup - executes on backend upon startup.
    • hourly - executes once an hour on backend.
    • daily - executes once a day on backend.

    Entity Events

    These events are triggered by certain relations to other notes. Meaning that the script is triggered only if the note has this script attached to it through relations (or it can inherit it).

    • runOnNoteCreation - executes when note is created on backend.
    • runOnNoteTitleChange - executes when note title is changed (includes note creation as well).
    • runOnNoteContentChange - executes when note content is changed (includes note creation as well).
    • runOnNoteChange - executes when note is changed (includes note creation as well).
    • runOnNoteDeletion - executes when note is being deleted.
    • runOnBranchCreation - executes when a branch is created. Branch is a link between parent note and child note and is created e.g. when cloning or moving note.
    • runOnBranchDeletion - executes when a branch is delete. Branch is a link between parent note and child note and is deleted e.g. when moving note (old branch/link is deleted).
    • runOnChildNoteCreation - executes when new note is created under this note.
    • runOnAttributeCreation - executes when new attribute is created under this note.
    • runOnAttributeChange - executes when attribute is changed under this note.

    Widgets

    Conversely to scripts, widgets do have some specific requirements in order to work. A widget must:

    • Extend BasicWidget or one of it's subclasses.
    • Create a new instance and assign it to module.exports.
    • Define a parentWidget member to determine where it should be displayed.
    • Define a position (integer) that determines the location via sort order.
    • Have a #widget attribute on the containing note.
    • Create, render, and return your element in the render function.

    parentWidget

    • left-pane - This renders the widget on the left side of the screen where the note tree lives.
    • center-pane - This renders the widget in the center of the layout in the same location that notes and splits appear.
    • note-detail-pane - This renders the widget with the note in the center pane. This means it can appear multiple times with splits.
    • right-pane - This renders the widget to the right of any opened notes.

    Tutorial

    For more information on building widgets, take a look at Widget Basics.

    `; const headingRe = /()(.+?)(<\/h[1-6]>)/g; diff --git a/src/styles/navbar/navbar.css b/src/styles/navbar/navbar.css index 7e1adf0e8..5b93d1d3e 100644 --- a/src/styles/navbar/navbar.css +++ b/src/styles/navbar/navbar.css @@ -50,7 +50,7 @@ } #menu li > ul { - transition: height 1000ms ease; + transition: height 200ms ease; } #menu li:not(.expanded) > ul { diff --git a/src/templates/page.ejs b/src/templates/page.ejs index 480f65c3c..afbded51a 100644 --- a/src/templates/page.ejs +++ b/src/templates/page.ejs @@ -65,7 +65,7 @@ const customServerYml = `- url: "{protocol}://{domain}:{port}/etapi" <% } %> <%- header %> <%= note.title %><% if (note.noteId !== subRoot.noteId) { %> - <%= subRoot.title %><% } %> - + @@ -97,9 +97,9 @@ content = content.replaceAll(headingRe, (...match) => { %>
    - + <% if (subRoot.hasRelation("shareLogo")) { %> - /download" alt="Logo" /> + /logo.svg" alt="Logo" /> <% } %> <%= subRoot.title %> @@ -109,9 +109,9 @@ content = content.replaceAll(headingRe, (...match) => {