diff --git a/apps/tauri/src-tauri/Cargo.lock b/apps/tauri/src-tauri/Cargo.lock index 593c31a..85c001f 100644 --- a/apps/tauri/src-tauri/Cargo.lock +++ b/apps/tauri/src-tauri/Cargo.lock @@ -924,23 +924,7 @@ dependencies = [ "cpufeatures 0.2.17", "curve25519-dalek-derive", "digest 0.10.7", - "fiat-crypto 0.2.9", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.0", - "curve25519-dalek-derive", - "digest 0.11.3", - "fiat-crypto 0.3.0", + "fiat-crypto", "rustc_version", "subtle", "zeroize", @@ -1015,7 +999,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid 0.9.6", - "pem-rfc7468 0.7.0", + "pem-rfc7468", "zeroize", ] @@ -1026,7 +1010,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d" dependencies = [ "const-oid 0.10.2", - "pem-rfc7468 1.0.0", "zeroize", ] @@ -1263,45 +1246,20 @@ dependencies = [ "signature 2.2.0", ] -[[package]] -name = "ed25519" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29fcf32e6c73d1079f83ab4d782de2d81620346a5f38c6237a86a22f8368980a" -dependencies = [ - "pkcs8 0.11.0", - "signature 3.0.0", -] - [[package]] name = "ed25519-dalek" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ - "curve25519-dalek 4.1.3", - "ed25519 2.2.3", + "curve25519-dalek", + "ed25519", "serde", "sha2 0.10.9", "subtle", "zeroize", ] -[[package]] -name = "ed25519-dalek" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ebaa1a2bf1290ab3bfe5a7b771d050ebffab2711c19a81691c683a5144a25de" -dependencies = [ - "curve25519-dalek 5.0.0", - "ed25519 3.0.0", - "serde", - "sha2 0.11.0", - "signature 3.0.0", - "subtle", - "zeroize", -] - [[package]] name = "embed-resource" version = "3.0.11" @@ -1448,12 +1406,6 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" -[[package]] -name = "fiat-crypto" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" - [[package]] name = "field-offset" version = "0.3.6" @@ -2870,12 +2822,12 @@ version = "0.2.0" source = "git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22#7182272e3edb0079d7b74b45b2f62a298d2fee22" dependencies = [ "mtp-client", - "mtp-codec 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "mtp-common 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "mtp-crypto 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", + "mtp-codec", + "mtp-common", + "mtp-crypto", "mtp-host", - "mtp-transport 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "mtp-type-map 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", + "mtp-transport", + "mtp-type-map", "mtp-webserver", ] @@ -2884,10 +2836,10 @@ name = "mtp-client" version = "0.2.0" source = "git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22#7182272e3edb0079d7b74b45b2f62a298d2fee22" dependencies = [ - "mtp-codec 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "mtp-common 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "mtp-crypto 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "mtp-transport 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", + "mtp-codec", + "mtp-common", + "mtp-crypto", + "mtp-transport", "rand 0.10.2", "tokio", ] @@ -2899,21 +2851,9 @@ source = "git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7 dependencies = [ "base64 0.23.1", "byteorder", - "mtp-common 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "mtp-crypto 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "mtp-type-map 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "rand 0.10.2", -] - -[[package]] -name = "mtp-codec" -version = "0.2.0" -source = "git+https://git.methanium.net/methanium/mtp.git?rev=b067614a684eb1856bc5db7b3fd82148c036ce6b#b067614a684eb1856bc5db7b3fd82148c036ce6b" -dependencies = [ - "base64 0.23.1", - "byteorder", - "mtp-common 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=b067614a684eb1856bc5db7b3fd82148c036ce6b)", - "mtp-type-map 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=b067614a684eb1856bc5db7b3fd82148c036ce6b)", + "mtp-common", + "mtp-crypto", + "mtp-type-map", "rand 0.10.2", ] @@ -2928,17 +2868,6 @@ dependencies = [ "wtransport", ] -[[package]] -name = "mtp-common" -version = "0.2.0" -source = "git+https://git.methanium.net/methanium/mtp.git?rev=b067614a684eb1856bc5db7b3fd82148c036ce6b#b067614a684eb1856bc5db7b3fd82148c036ce6b" -dependencies = [ - "quinn", - "rustls", - "thiserror 2.0.19", - "wtransport", -] - [[package]] name = "mtp-crypto" version = "0.2.0" @@ -2946,7 +2875,7 @@ source = "git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7 dependencies = [ "base64 0.23.1", "chacha20poly1305", - "ed25519-dalek 2.2.0", + "ed25519-dalek", "getrandom 0.4.3", "hkdf", "ml-dsa", @@ -2961,35 +2890,15 @@ dependencies = [ "zeroize", ] -[[package]] -name = "mtp-crypto" -version = "0.2.0" -source = "git+https://git.methanium.net/methanium/mtp.git?rev=b067614a684eb1856bc5db7b3fd82148c036ce6b#b067614a684eb1856bc5db7b3fd82148c036ce6b" -dependencies = [ - "base64 0.23.1", - "chacha20poly1305", - "ed25519-dalek 3.0.0", - "getrandom 0.4.3", - "hkdf", - "ml-dsa", - "rand 0.10.2", - "rand_core 0.10.1", - "rustls", - "sha2 0.11.0", - "thiserror 2.0.19", - "tokio", - "zeroize", -] - [[package]] name = "mtp-host" version = "0.2.0" source = "git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22#7182272e3edb0079d7b74b45b2f62a298d2fee22" dependencies = [ - "mtp-codec 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "mtp-common 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "mtp-crypto 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "mtp-transport 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", + "mtp-codec", + "mtp-common", + "mtp-crypto", + "mtp-transport", "rand 0.10.2", "tokio", "tracing", @@ -3002,27 +2911,9 @@ version = "0.2.0" source = "git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22#7182272e3edb0079d7b74b45b2f62a298d2fee22" dependencies = [ "async-trait", - "mtp-codec 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "mtp-common 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "mtp-crypto 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "rcgen", - "rustls", - "rustls-native-certs", - "sha2 0.11.0", - "tokio", - "tracing", - "wtransport", -] - -[[package]] -name = "mtp-transport" -version = "0.2.0" -source = "git+https://git.methanium.net/methanium/mtp.git?rev=b067614a684eb1856bc5db7b3fd82148c036ce6b#b067614a684eb1856bc5db7b3fd82148c036ce6b" -dependencies = [ - "async-trait", - "mtp-codec 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=b067614a684eb1856bc5db7b3fd82148c036ce6b)", - "mtp-common 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=b067614a684eb1856bc5db7b3fd82148c036ce6b)", - "mtp-crypto 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=b067614a684eb1856bc5db7b3fd82148c036ce6b)", + "mtp-codec", + "mtp-common", + "mtp-crypto", "rcgen", "rustls", "rustls-native-certs", @@ -3041,15 +2932,6 @@ dependencies = [ "serde_yaml", ] -[[package]] -name = "mtp-type-map" -version = "0.2.0" -source = "git+https://git.methanium.net/methanium/mtp.git?rev=b067614a684eb1856bc5db7b3fd82148c036ce6b#b067614a684eb1856bc5db7b3fd82148c036ce6b" -dependencies = [ - "serde", - "serde_yaml", -] - [[package]] name = "mtp-webserver" version = "0.2.0" @@ -3064,11 +2946,11 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "mtp-codec 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "mtp-common 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", - "mtp-crypto 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", + "mtp-codec", + "mtp-common", + "mtp-crypto", "mtp-host", - "mtp-transport 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=7182272e3edb0079d7b74b45b2f62a298d2fee22)", + "mtp-transport", "quinn", "rand 0.10.2", "rustls", @@ -3581,15 +3463,6 @@ dependencies = [ "base64ct", ] -[[package]] -name = "pem-rfc7468" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" -dependencies = [ - "base64ct", -] - [[package]] name = "percent-encoding" version = "2.3.2" @@ -5502,7 +5375,7 @@ dependencies = [ "base64 0.23.1", "jni 0.22.4", "mtp", - "mtp-transport 0.2.0 (git+https://git.methanium.net/methanium/mtp.git?rev=b067614a684eb1856bc5db7b3fd82148c036ce6b)", + "mtp-transport", "reqwest 0.12.28", "serde", "serde_json", @@ -6828,7 +6701,7 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ - "curve25519-dalek 4.1.3", + "curve25519-dalek", "rand_core 0.6.4", "serde", "zeroize", diff --git a/apps/tauri/src-tauri/Cargo.toml b/apps/tauri/src-tauri/Cargo.toml index 55d7ed5..5df336b 100644 --- a/apps/tauri/src-tauri/Cargo.toml +++ b/apps/tauri/src-tauri/Cargo.toml @@ -25,7 +25,7 @@ base64 = "0.23" reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } tokio = { version = "1", features = ["rt-multi-thread", "sync", "time"] } mtp = { git = "https://git.methanium.net/methanium/mtp.git", rev = "7182272e3edb0079d7b74b45b2f62a298d2fee22", features = ["client", "crypto"] } -mtp-transport = { git = "https://git.methanium.net/methanium/mtp.git", rev = "b067614a684eb1856bc5db7b3fd82148c036ce6b" } +mtp-transport = { git = "https://git.methanium.net/methanium/mtp.git", rev = "7182272e3edb0079d7b74b45b2f62a298d2fee22" } webpki-root-certs = "1" tauri-plugin-deep-link = "2" tauri-plugin-notification = "2" diff --git a/apps/web/package.json b/apps/web/package.json index 107c6f7..e6c6bf8 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -23,6 +23,7 @@ "@tensamin/chat": "workspace:*", "@tensamin/crypto": "workspace:*", "@tensamin/hotkeys": "workspace:*", + "@tensamin/markdown": "workspace:*", "@tensamin/mtp": "workspace:*", "@tensamin/notifications": "workspace:*", "@tensamin/onboarding": "workspace:*", diff --git a/apps/web/src/components/modals/profile.tsx b/apps/web/src/components/modals/profile.tsx index 6324ab5..edd83a6 100644 --- a/apps/web/src/components/modals/profile.tsx +++ b/apps/web/src/components/modals/profile.tsx @@ -1,6 +1,6 @@ import type { User } from "@tensamin/user/context"; import { Avatar, AvatarFallback, AvatarImage, Button } from "@methanium/ui"; -import { Text } from "@methanium/ui/markdown"; +import Text from "@tensamin/markdown/text"; import { ChevronDown, ChevronUp } from "lucide-react"; import { useState } from "react"; diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index 464f57b..bb41448 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -1,4 +1,4 @@ -import { createReadStream, statSync } from "node:fs"; +import { createReadStream, realpathSync, statSync } from "node:fs"; import type { IncomingMessage, ServerResponse } from "node:http"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; @@ -11,6 +11,11 @@ import { methaniumUi } from "@methanium/ui/vite"; const host = process.env.TAURI_DEV_HOST; const appDir = dirname(fileURLToPath(import.meta.url)); +const markdownPackageDir = resolve(appDir, "../../packages/markdown"); + +function resolveMarkdownDependency(packageName: string): string { + return realpathSync(resolve(markdownPackageDir, "node_modules", packageName)); +} function deepFilterAssetHeaders(rootDir: string): Plugin { const serveModel = ( @@ -52,6 +57,24 @@ export default defineConfig({ clearScreen: false, resolve: { tsconfigPaths: true, + alias: [ + { + find: "@codemirror/commands", + replacement: resolveMarkdownDependency("@codemirror/commands"), + }, + { + find: "@codemirror/lang-markdown", + replacement: resolveMarkdownDependency("@codemirror/lang-markdown"), + }, + { + find: "@codemirror/state", + replacement: resolveMarkdownDependency("@codemirror/state"), + }, + { + find: "@codemirror/view", + replacement: resolveMarkdownDependency("@codemirror/view"), + }, + ], dedupe: [ "react", "react-dom", @@ -67,6 +90,10 @@ export default defineConfig({ "@tensamin/user", "@tensamin/tauri", "@tensamin/chat", + "@codemirror/commands", + "@codemirror/lang-markdown", + "@codemirror/state", + "@codemirror/view", ], }, server: { @@ -106,6 +133,7 @@ export default defineConfig({ "@tensamin/crypto", "@tensamin/crypto/context", "@tensamin/hotkeys", + "@tensamin/markdown", "@tensamin/mtp", "@tensamin/notifications", "@tensamin/onboarding", diff --git a/licenses/@codemirror_autocomplete@6.20.3/LICENSE b/licenses/@codemirror_autocomplete@6.20.3/LICENSE new file mode 100644 index 0000000..9a91f48 --- /dev/null +++ b/licenses/@codemirror_autocomplete@6.20.3/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (C) 2018-2021 by Marijn Haverbeke and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/@codemirror_commands@6.10.4/LICENSE b/licenses/@codemirror_commands@6.10.4/LICENSE new file mode 100644 index 0000000..9a91f48 --- /dev/null +++ b/licenses/@codemirror_commands@6.10.4/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (C) 2018-2021 by Marijn Haverbeke and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/@codemirror_lang-markdown@6.5.2/LICENSE b/licenses/@codemirror_lang-markdown@6.5.2/LICENSE new file mode 100644 index 0000000..9a91f48 --- /dev/null +++ b/licenses/@codemirror_lang-markdown@6.5.2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (C) 2018-2021 by Marijn Haverbeke and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/@codemirror_language@6.12.4/LICENSE b/licenses/@codemirror_language@6.12.4/LICENSE new file mode 100644 index 0000000..9a91f48 --- /dev/null +++ b/licenses/@codemirror_language@6.12.4/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (C) 2018-2021 by Marijn Haverbeke and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/@codemirror_state@6.7.1/LICENSE b/licenses/@codemirror_state@6.7.1/LICENSE new file mode 100644 index 0000000..9a91f48 --- /dev/null +++ b/licenses/@codemirror_state@6.7.1/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (C) 2018-2021 by Marijn Haverbeke and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/@codemirror_view@6.43.8/LICENSE b/licenses/@codemirror_view@6.43.8/LICENSE new file mode 100644 index 0000000..9a91f48 --- /dev/null +++ b/licenses/@codemirror_view@6.43.8/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (C) 2018-2021 by Marijn Haverbeke and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/@methanium_ui@0.0.28/LICENSE b/licenses/@methanium_ui@0.0.22/LICENSE similarity index 100% rename from licenses/@methanium_ui@0.0.28/LICENSE rename to licenses/@methanium_ui@0.0.22/LICENSE diff --git a/licenses/@twemoji_api@17.0.3/LICENSE b/licenses/@twemoji_api@17.0.3/LICENSE new file mode 100644 index 0000000..d2e3436 --- /dev/null +++ b/licenses/@twemoji_api@17.0.3/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2022–present Jason Sofonia & Justine De Caires +Copyright (c) 2014–2021 Twitter + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/THIRD_PARTY_NOTICES.md b/licenses/THIRD_PARTY_NOTICES.md index 2094186..bda5f0c 100644 --- a/licenses/THIRD_PARTY_NOTICES.md +++ b/licenses/THIRD_PARTY_NOTICES.md @@ -2,6 +2,60 @@ Generated from pnpm-lock.yaml and installed packages in workspace node_modules folders. +## @codemirror/autocomplete@6.20.3 + +- License: MIT +- Repository: git+https://code.haverbeke.berlin/codemirror/autocomplete.git +- Description: Autocompletion for the CodeMirror code editor +- Included files: LICENSE +- Folder: `licenses/@codemirror_autocomplete@6.20.3` +- Source package dir: `packages/markdown/node_modules/@codemirror/autocomplete` + +## @codemirror/commands@6.10.4 + +- License: MIT +- Repository: git+https://code.haverbeke.berlin/codemirror/commands.git +- Description: Collection of editing commands for the CodeMirror code editor +- Included files: LICENSE +- Folder: `licenses/@codemirror_commands@6.10.4` +- Source package dir: `packages/markdown/node_modules/@codemirror/commands` + +## @codemirror/lang-markdown@6.5.2 + +- License: MIT +- Repository: git+https://code.haverbeke.berlin/codemirror/lang-markdown.git +- Description: Markdown language support for the CodeMirror code editor +- Included files: LICENSE +- Folder: `licenses/@codemirror_lang-markdown@6.5.2` +- Source package dir: `packages/markdown/node_modules/@codemirror/lang-markdown` + +## @codemirror/language@6.12.4 + +- License: MIT +- Repository: git+https://code.haverbeke.berlin/codemirror/language.git +- Description: Language support infrastructure for the CodeMirror code editor +- Included files: LICENSE +- Folder: `licenses/@codemirror_language@6.12.4` +- Source package dir: `packages/markdown/node_modules/@codemirror/language` + +## @codemirror/state@6.7.1 + +- License: MIT +- Repository: git+https://code.haverbeke.berlin/codemirror/state.git +- Description: Editor state data structures for the CodeMirror code editor +- Included files: LICENSE +- Folder: `licenses/@codemirror_state@6.7.1` +- Source package dir: `packages/markdown/node_modules/@codemirror/state` + +## @codemirror/view@6.43.8 + +- License: MIT +- Repository: git+https://code.haverbeke.berlin/codemirror/view.git +- Description: DOM view component for the CodeMirror code editor +- Included files: LICENSE +- Folder: `licenses/@codemirror_view@6.43.8` +- Source package dir: `packages/markdown/node_modules/@codemirror/view` + ## @eslint/js@10.0.1 - License: MIT @@ -30,11 +84,11 @@ Generated from pnpm-lock.yaml and installed packages in workspace node_modules f - Folder: `licenses/@livekit_components-react@2.9.23` - Source package dir: `packages/call/node_modules/@livekit/components-react` -## @methanium/ui@0.0.28 +## @methanium/ui@0.0.22 - License: UNKNOWN - Included files: LICENSE -- Folder: `licenses/@methanium_ui@0.0.28` +- Folder: `licenses/@methanium_ui@0.0.22` - Source package dir: `apps/tauri/node_modules/@methanium/ui` ## @tailwindcss/vite@4.3.3 @@ -143,6 +197,16 @@ Generated from pnpm-lock.yaml and installed packages in workspace node_modules f - Folder: `licenses/@tauri-apps_plugin-notification@2.3.3` - Source package dir: `packages/notifications/node_modules/@tauri-apps/plugin-notification` +## @twemoji/api@17.0.3 + +- License: MIT AND CC-BY-4.0 +- Homepage: https://github.com/jdecked/twemoji +- Repository: git://github.com/jdecked/twemoji.git +- Description: A Unicode standard based way to implement emoji across all platforms. +- Included files: LICENSE +- Folder: `licenses/@twemoji_api@17.0.3` +- Source package dir: `packages/markdown/node_modules/@twemoji/api` + ## @types/node@26.2.0 - License: MIT @@ -241,6 +305,15 @@ Generated from pnpm-lock.yaml and installed packages in workspace node_modules f - Folder: `licenses/electron-builder@26.15.3` - Source package dir: `apps/electron/node_modules/electron-builder` +## emojibase-data@17.0.0 + +- License: MIT +- Repository: git@github.com:milesj/emojibase.git +- Description: Evergreen emoji datasets. +- Included files: LICENSE +- Folder: `licenses/emojibase-data@17.0.0` +- Source package dir: `packages/markdown/node_modules/emojibase-data` + ## esbuild@0.28.1 - License: MIT diff --git a/licenses/emojibase-data@17.0.0/LICENSE b/licenses/emojibase-data@17.0.0/LICENSE new file mode 100644 index 0000000..3e21d0b --- /dev/null +++ b/licenses/emojibase-data@17.0.0/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017-2019 Miles Johnson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/sbom.cyclonedx.json b/licenses/sbom.cyclonedx.json index ebb655c..4075c6b 100644 --- a/licenses/sbom.cyclonedx.json +++ b/licenses/sbom.cyclonedx.json @@ -3,7 +3,7 @@ "specVersion": "1.5", "version": 1, "metadata": { - "timestamp": "2026-08-09T22:09:04.469Z", + "timestamp": "2026-08-09T18:16:10.177Z", "tools": [ { "vendor": "OpenAI", @@ -16,6 +16,192 @@ } }, "components": [ + { + "type": "library", + "bomRef": "pkg:npm/%40codemirror/autocomplete@6.20.3", + "name": "@codemirror/autocomplete", + "version": "6.20.3", + "purl": "pkg:npm/%40codemirror/autocomplete@6.20.3", + "description": "Autocompletion for the CodeMirror code editor", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://code.haverbeke.berlin/codemirror/autocomplete.git" + } + ], + "properties": [ + { + "name": "local:licenseFolder", + "value": "licenses/@codemirror_autocomplete@6.20.3" + }, + { + "name": "local:sourcePackageDir", + "value": "packages/markdown/node_modules/@codemirror/autocomplete" + } + ] + }, + { + "type": "library", + "bomRef": "pkg:npm/%40codemirror/commands@6.10.4", + "name": "@codemirror/commands", + "version": "6.10.4", + "purl": "pkg:npm/%40codemirror/commands@6.10.4", + "description": "Collection of editing commands for the CodeMirror code editor", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://code.haverbeke.berlin/codemirror/commands.git" + } + ], + "properties": [ + { + "name": "local:licenseFolder", + "value": "licenses/@codemirror_commands@6.10.4" + }, + { + "name": "local:sourcePackageDir", + "value": "packages/markdown/node_modules/@codemirror/commands" + } + ] + }, + { + "type": "library", + "bomRef": "pkg:npm/%40codemirror/lang-markdown@6.5.2", + "name": "@codemirror/lang-markdown", + "version": "6.5.2", + "purl": "pkg:npm/%40codemirror/lang-markdown@6.5.2", + "description": "Markdown language support for the CodeMirror code editor", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://code.haverbeke.berlin/codemirror/lang-markdown.git" + } + ], + "properties": [ + { + "name": "local:licenseFolder", + "value": "licenses/@codemirror_lang-markdown@6.5.2" + }, + { + "name": "local:sourcePackageDir", + "value": "packages/markdown/node_modules/@codemirror/lang-markdown" + } + ] + }, + { + "type": "library", + "bomRef": "pkg:npm/%40codemirror/language@6.12.4", + "name": "@codemirror/language", + "version": "6.12.4", + "purl": "pkg:npm/%40codemirror/language@6.12.4", + "description": "Language support infrastructure for the CodeMirror code editor", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://code.haverbeke.berlin/codemirror/language.git" + } + ], + "properties": [ + { + "name": "local:licenseFolder", + "value": "licenses/@codemirror_language@6.12.4" + }, + { + "name": "local:sourcePackageDir", + "value": "packages/markdown/node_modules/@codemirror/language" + } + ] + }, + { + "type": "library", + "bomRef": "pkg:npm/%40codemirror/state@6.7.1", + "name": "@codemirror/state", + "version": "6.7.1", + "purl": "pkg:npm/%40codemirror/state@6.7.1", + "description": "Editor state data structures for the CodeMirror code editor", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://code.haverbeke.berlin/codemirror/state.git" + } + ], + "properties": [ + { + "name": "local:licenseFolder", + "value": "licenses/@codemirror_state@6.7.1" + }, + { + "name": "local:sourcePackageDir", + "value": "packages/markdown/node_modules/@codemirror/state" + } + ] + }, + { + "type": "library", + "bomRef": "pkg:npm/%40codemirror/view@6.43.8", + "name": "@codemirror/view", + "version": "6.43.8", + "purl": "pkg:npm/%40codemirror/view@6.43.8", + "description": "DOM view component for the CodeMirror code editor", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://code.haverbeke.berlin/codemirror/view.git" + } + ], + "properties": [ + { + "name": "local:licenseFolder", + "value": "licenses/@codemirror_view@6.43.8" + }, + { + "name": "local:sourcePackageDir", + "value": "packages/markdown/node_modules/@codemirror/view" + } + ] + }, { "type": "library", "bomRef": "pkg:npm/%40eslint/js@10.0.1", @@ -118,15 +304,15 @@ }, { "type": "library", - "bomRef": "pkg:npm/%40methanium/ui@0.0.28", + "bomRef": "pkg:npm/%40methanium/ui@0.0.22", "name": "@methanium/ui", - "version": "0.0.28", - "purl": "pkg:npm/%40methanium/ui@0.0.28", + "version": "0.0.22", + "purl": "pkg:npm/%40methanium/ui@0.0.22", "externalReferences": [], "properties": [ { "name": "local:licenseFolder", - "value": "licenses/@methanium_ui@0.0.28" + "value": "licenses/@methanium_ui@0.0.22" }, { "name": "local:sourcePackageDir", @@ -506,6 +692,41 @@ } ] }, + { + "type": "library", + "bomRef": "pkg:npm/%40twemoji/api@17.0.3", + "name": "@twemoji/api", + "version": "17.0.3", + "purl": "pkg:npm/%40twemoji/api@17.0.3", + "description": "A Unicode standard based way to implement emoji across all platforms.", + "licenses": [ + { + "license": { + "name": "MIT AND CC-BY-4.0" + } + } + ], + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jdecked/twemoji" + }, + { + "type": "vcs", + "url": "git://github.com/jdecked/twemoji.git" + } + ], + "properties": [ + { + "name": "local:licenseFolder", + "value": "licenses/@twemoji_api@17.0.3" + }, + { + "name": "local:sourcePackageDir", + "value": "packages/markdown/node_modules/@twemoji/api" + } + ] + }, { "type": "library", "bomRef": "pkg:npm/%40types/node@26.2.0", @@ -848,6 +1069,37 @@ } ] }, + { + "type": "library", + "bomRef": "pkg:npm/emojibase-data@17.0.0", + "name": "emojibase-data", + "version": "17.0.0", + "purl": "pkg:npm/emojibase-data@17.0.0", + "description": "Evergreen emoji datasets.", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "externalReferences": [ + { + "type": "vcs", + "url": "git@github.com:milesj/emojibase.git" + } + ], + "properties": [ + { + "name": "local:licenseFolder", + "value": "licenses/emojibase-data@17.0.0" + }, + { + "name": "local:sourcePackageDir", + "value": "packages/markdown/node_modules/emojibase-data" + } + ] + }, { "type": "library", "bomRef": "pkg:npm/esbuild@0.28.1", diff --git a/licenses/third-party-credits.json b/licenses/third-party-credits.json index 5ea2d3d..9f4b971 100644 --- a/licenses/third-party-credits.json +++ b/licenses/third-party-credits.json @@ -1,7 +1,85 @@ { - "generatedAt": "2026-08-09T22:09:04.466Z", - "packageCount": 52, + "generatedAt": "2026-08-09T18:16:10.172Z", + "packageCount": 60, "packages": [ + { + "name": "@codemirror/autocomplete", + "version": "6.20.3", + "license": "MIT", + "homepage": null, + "repository": "git+https://code.haverbeke.berlin/codemirror/autocomplete.git", + "description": "Autocompletion for the CodeMirror code editor", + "files": [ + "LICENSE" + ], + "licenseFolder": "licenses/@codemirror_autocomplete@6.20.3", + "sourcePackageDir": "packages/markdown/node_modules/@codemirror/autocomplete" + }, + { + "name": "@codemirror/commands", + "version": "6.10.4", + "license": "MIT", + "homepage": null, + "repository": "git+https://code.haverbeke.berlin/codemirror/commands.git", + "description": "Collection of editing commands for the CodeMirror code editor", + "files": [ + "LICENSE" + ], + "licenseFolder": "licenses/@codemirror_commands@6.10.4", + "sourcePackageDir": "packages/markdown/node_modules/@codemirror/commands" + }, + { + "name": "@codemirror/lang-markdown", + "version": "6.5.2", + "license": "MIT", + "homepage": null, + "repository": "git+https://code.haverbeke.berlin/codemirror/lang-markdown.git", + "description": "Markdown language support for the CodeMirror code editor", + "files": [ + "LICENSE" + ], + "licenseFolder": "licenses/@codemirror_lang-markdown@6.5.2", + "sourcePackageDir": "packages/markdown/node_modules/@codemirror/lang-markdown" + }, + { + "name": "@codemirror/language", + "version": "6.12.4", + "license": "MIT", + "homepage": null, + "repository": "git+https://code.haverbeke.berlin/codemirror/language.git", + "description": "Language support infrastructure for the CodeMirror code editor", + "files": [ + "LICENSE" + ], + "licenseFolder": "licenses/@codemirror_language@6.12.4", + "sourcePackageDir": "packages/markdown/node_modules/@codemirror/language" + }, + { + "name": "@codemirror/state", + "version": "6.7.1", + "license": "MIT", + "homepage": null, + "repository": "git+https://code.haverbeke.berlin/codemirror/state.git", + "description": "Editor state data structures for the CodeMirror code editor", + "files": [ + "LICENSE" + ], + "licenseFolder": "licenses/@codemirror_state@6.7.1", + "sourcePackageDir": "packages/markdown/node_modules/@codemirror/state" + }, + { + "name": "@codemirror/view", + "version": "6.43.8", + "license": "MIT", + "homepage": null, + "repository": "git+https://code.haverbeke.berlin/codemirror/view.git", + "description": "DOM view component for the CodeMirror code editor", + "files": [ + "LICENSE" + ], + "licenseFolder": "licenses/@codemirror_view@6.43.8", + "sourcePackageDir": "packages/markdown/node_modules/@codemirror/view" + }, { "name": "@eslint/js", "version": "10.0.1", @@ -43,7 +121,7 @@ }, { "name": "@methanium/ui", - "version": "0.0.28", + "version": "0.0.22", "license": "UNKNOWN", "homepage": null, "repository": null, @@ -51,7 +129,7 @@ "files": [ "LICENSE" ], - "licenseFolder": "licenses/@methanium_ui@0.0.28", + "licenseFolder": "licenses/@methanium_ui@0.0.22", "sourcePackageDir": "apps/tauri/node_modules/@methanium/ui" }, { @@ -199,6 +277,19 @@ "licenseFolder": "licenses/@tauri-apps_plugin-notification@2.3.3", "sourcePackageDir": "packages/notifications/node_modules/@tauri-apps/plugin-notification" }, + { + "name": "@twemoji/api", + "version": "17.0.3", + "license": "MIT AND CC-BY-4.0", + "homepage": "https://github.com/jdecked/twemoji", + "repository": "git://github.com/jdecked/twemoji.git", + "description": "A Unicode standard based way to implement emoji across all platforms.", + "files": [ + "LICENSE" + ], + "licenseFolder": "licenses/@twemoji_api@17.0.3", + "sourcePackageDir": "packages/markdown/node_modules/@twemoji/api" + }, { "name": "@types/node", "version": "26.2.0", @@ -330,6 +421,19 @@ "licenseFolder": "licenses/electron-builder@26.15.3", "sourcePackageDir": "apps/electron/node_modules/electron-builder" }, + { + "name": "emojibase-data", + "version": "17.0.0", + "license": "MIT", + "homepage": null, + "repository": "git@github.com:milesj/emojibase.git", + "description": "Evergreen emoji datasets.", + "files": [ + "LICENSE" + ], + "licenseFolder": "licenses/emojibase-data@17.0.0", + "sourcePackageDir": "packages/markdown/node_modules/emojibase-data" + }, { "name": "esbuild", "version": "0.28.1", diff --git a/packages/chat/package.json b/packages/chat/package.json index d164529..0e4f42f 100644 --- a/packages/chat/package.json +++ b/packages/chat/package.json @@ -24,6 +24,7 @@ "@tensamin/cache": "workspace:*", "@tensamin/crypto": "workspace:*", "@tensamin/hotkeys": "workspace:*", + "@tensamin/markdown": "workspace:*", "@tensamin/mtp": "workspace:*", "@tensamin/shared": "workspace:*", "@tensamin/storage": "workspace:*", diff --git a/packages/chat/src/components/emojiPicker.tsx b/packages/chat/src/components/emojiPicker.tsx index ed8f2a1..7f7ff0e 100644 --- a/packages/chat/src/components/emojiPicker.tsx +++ b/packages/chat/src/components/emojiPicker.tsx @@ -1,5 +1,5 @@ import { Button } from "@methanium/ui"; -import { Emoji } from "@methanium/ui/markdown"; +import Emoji from "@tensamin/markdown/emoji"; import { getRecentEmojis, useEmojiRanks } from "./emojiRanks"; export default function EmojiPicker({ diff --git a/packages/chat/src/components/emojiRanks.ts b/packages/chat/src/components/emojiRanks.ts index a30deb5..874c125 100644 --- a/packages/chat/src/components/emojiRanks.ts +++ b/packages/chat/src/components/emojiRanks.ts @@ -1,6 +1,6 @@ import { useStorage } from "@tensamin/storage/context"; import { useCallback, useEffect, useState } from "react"; -import { normalizeShortcode } from "@methanium/ui/markdown"; +import { normalizeShortcode } from "@tensamin/markdown/emoji"; const RANKS_CHANGED_EVENT = "tensamin-reaction-ranks-changed"; let recordQueue = Promise.resolve(); diff --git a/packages/chat/src/components/input.tsx b/packages/chat/src/components/input.tsx index 28752f0..e54235d 100644 --- a/packages/chat/src/components/input.tsx +++ b/packages/chat/src/components/input.tsx @@ -1,4 +1,4 @@ -import { Input, type InputController } from "@methanium/ui/markdown"; +import Input, { type InputController } from "@tensamin/markdown/input"; import { Card, CardHeader, @@ -276,7 +276,7 @@ export default function InputComponent({
)}
@@ -387,21 +388,20 @@ function MessageComponent({ )} {editing ? (
- - { - if (!editDraft.trim()) return; - if (editDraft !== message.Content) { - void submitEditMessage(editDraft); - } - onSetEditing(false); - }} - /> - + { + if (!editDraft.trim()) return; + if (editDraft !== message.Content) { + void submitEditMessage(editDraft); + } + onSetEditing(false); + }} + />
) : ( -
+
{isValidURL ? ( ) : ( -
- -
+ + )} + {message.Edited && ( +

+ (edited) +

)}
)} diff --git a/packages/chat/src/components/messageContextMenu.tsx b/packages/chat/src/components/messageContextMenu.tsx index 481e599..227afbb 100644 --- a/packages/chat/src/components/messageContextMenu.tsx +++ b/packages/chat/src/components/messageContextMenu.tsx @@ -44,7 +44,7 @@ import type { Ref, } from "react"; import { useChat } from "../context"; -import { Emoji } from "@methanium/ui/markdown"; +import Emoji from "@tensamin/markdown/emoji"; import EmojiPicker from "./emojiPicker"; import { getRecentEmojis, useEmojiRanks } from "./emojiRanks"; diff --git a/packages/chat/src/components/replyBox.tsx b/packages/chat/src/components/replyBox.tsx index 82417d0..03df6b0 100644 --- a/packages/chat/src/components/replyBox.tsx +++ b/packages/chat/src/components/replyBox.tsx @@ -6,7 +6,7 @@ import { cn, Skeleton, } from "@methanium/ui"; -import { Text } from "@methanium/ui/markdown"; +import Text from "@tensamin/markdown/text"; import type { User } from "@tensamin/user/context"; import Wrapper from "@tensamin/user/wrapper"; import { Forward, X } from "lucide-react"; @@ -77,11 +77,12 @@ export default function ReplyBox({ ) : null} {content !== undefined && (
- + + {edited && ( +

+ (edited) +

+ )}
)} diff --git a/packages/markdown/package.json b/packages/markdown/package.json new file mode 100644 index 0000000..247d715 --- /dev/null +++ b/packages/markdown/package.json @@ -0,0 +1,30 @@ +{ + "name": "@tensamin/markdown", + "private": true, + "version": "0.0.0", + "type": "module", + "exports": { + "./text": "./src/text.tsx", + "./input": "./src/input.tsx", + "./emoji": "./src/emoji.tsx" + }, + "scripts": { + "format": "pnpm exec prettier --write .", + "lint": "eslint src", + "build": "tsc -p tsconfig.json --noEmit" + }, + "dependencies": { + "@codemirror/autocomplete": "^6.20.3", + "@codemirror/commands": "^6.10.4", + "@codemirror/lang-markdown": "^6.5.2", + "@codemirror/language": "^6.12.4", + "@codemirror/state": "^6.7.1", + "@codemirror/view": "^6.43.8", + "@methanium/ui": "*", + "@twemoji/api": "^17.0.3", + "emojibase-data": "^17.0.0", + "lucide-react": "^1.30.0", + "react": "^19.2.8", + "react-dom": "^19.2.8" + } +} diff --git a/packages/markdown/src/emoji.test.ts b/packages/markdown/src/emoji.test.ts new file mode 100644 index 0000000..2a94b81 --- /dev/null +++ b/packages/markdown/src/emoji.test.ts @@ -0,0 +1,138 @@ +import { describe, expect, it, vi } from "vitest"; +import type { ReactNode } from "react"; + +vi.mock("@methanium/ui", () => ({ + Tooltip: ({ children }: { children: ReactNode }) => children, + TooltipContent: ({ children }: { children: ReactNode }) => children, + TooltipTrigger: ({ render }: { render: ReactNode }) => render, +})); +import { EditorState } from "@codemirror/state"; +import { CompletionContext } from "@codemirror/autocomplete"; +import { markdown } from "@codemirror/lang-markdown"; +import { normalizeShortcode, resolveEmoji, searchEmojis } from "./emojiData"; +import { parseEmojiText, parseInlineNodes } from "./markdown"; +import { + createEmojiCompletionSource, + findEmojiRanges, + MAX_RENDERED_EMOJI_OPTIONS, +} from "./input"; + +describe("emoji shortcodes", () => { + it("normalizes aliases to their canonical shortcode", () => { + expect(normalizeShortcode(":flame:")).toBe(":fire:"); + expect(normalizeShortcode("+1")).toBe(":thumbsup:"); + }); + + it("resolves every search result to a Twemoji hexcode", () => { + const results = searchEmojis("fire"); + expect(results[0]?.shortcode).toBe(":fire:"); + expect(results.every((emoji) => emoji.hexcode.length > 0)).toBe(true); + }); + + it("shows all emojis for an empty query", () => { + expect(searchEmojis("").length).toBeGreaterThan(1000); + }); + + it("bounds the number of mounted autocomplete rows", () => { + expect(MAX_RENDERED_EMOJI_OPTIONS).toBeLessThanOrEqual(100); + }); + + it("parses known shortcodes and preserves unknown ones", () => { + expect(parseEmojiText("a :fire: b :not_an_emoji:")).toEqual([ + { type: "text", value: "a " }, + { type: "emoji", shortcode: ":fire:" }, + { type: "text", value: " b :not_an_emoji:" }, + ]); + }); + + it("recognizes a valid shortcode sharing an unknown closing colon", () => { + expect(parseEmojiText(":bla:thumbsup:")).toEqual([ + { type: "text", value: ":bla" }, + { type: "emoji", shortcode: ":thumbsup:" }, + ]); + + const state = EditorState.create({ + doc: ":bla:thumbsup:", + extensions: [markdown()], + }); + expect(findEmojiRanges(state)[0]?.from).toBe(4); + }); + + it("does not parse underscores inside emoji shortcodes as emphasis", () => { + expect(parseInlineNodes("before :white_check_mark: after")).toEqual([ + { type: "text", value: "before " }, + { type: "emoji", shortcode: ":white_check_mark:" }, + { type: "text", value: " after" }, + ]); + }); + + it("contains the picker defaults", () => { + expect(resolveEmoji(":thumbsup:")).toBeDefined(); + expect(resolveEmoji(":white_check_mark:")).toBeDefined(); + }); + + it("finds completed emoji shortcodes in editor state", () => { + const state = EditorState.create({ + doc: "before :fire: after :not_an_emoji:", + extensions: [markdown()], + }); + + expect( + findEmojiRanges(state).map(({ from, shortcode, to }) => ({ + from, + shortcode, + to, + })), + ).toEqual([{ from: 7, shortcode: ":fire:", to: 13 }]); + }); + + it("does not replace emoji shortcodes inside code", () => { + const state = EditorState.create({ + doc: "`:fire:`\n\n```\n:fire:\n```\n\n:fire:", + extensions: [markdown()], + }); + + expect(findEmojiRanges(state)).toHaveLength(1); + expect(findEmojiRanges(state)[0]?.from).toBe(26); + }); + + it("ranks frequently used emojis first for a bare colon", async () => { + const state = EditorState.create({ doc: ":", extensions: [markdown()] }); + const result = await createEmojiCompletionSource({ + ":fire:": 50, + ":thumbsup:": 2, + })(new CompletionContext(state, 1, false)); + + expect(result?.options[0]?.displayLabel).toBe(":fire:"); + expect(result?.options[1]?.displayLabel).toBe(":thumbsup:"); + }); + + it("keeps typed relevance above usage frequency", async () => { + const state = EditorState.create({ + doc: ":fire", + extensions: [markdown()], + }); + const result = await createEmojiCompletionSource({ + ":fire_engine:": 10000, + ":fire:": 1, + })(new CompletionContext(state, 5, false)); + + expect(result?.options[0]?.displayLabel).toBe(":fire:"); + expect(result?.options[0]?.boost).toBeGreaterThan( + result?.options[1]?.boost ?? 0, + ); + }); + + it("merges alias frequencies into canonical completions", async () => { + const state = EditorState.create({ doc: ":", extensions: [markdown()] }); + const result = await createEmojiCompletionSource({ + ":fire:": 2, + ":flame:": 3, + })(new CompletionContext(state, 1, false)); + const fire = result?.options.find( + (option) => option.displayLabel === ":fire:", + ); + + expect(fire?.boost).toBe(15); + }); +}); diff --git a/packages/markdown/src/emoji.tsx b/packages/markdown/src/emoji.tsx new file mode 100644 index 0000000..c0eb397 --- /dev/null +++ b/packages/markdown/src/emoji.tsx @@ -0,0 +1,50 @@ +import twemoji from "@twemoji/api"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@methanium/ui"; +import { resolveEmoji } from "./emojiData"; + +export { + emojis, + findEmojiShortcodes, + normalizeShortcode, + resolveEmoji, + searchEmojis, +} from "./emojiData"; +export type { EmojiDefinition } from "./emojiData"; + +export function getEmojiUrl(shortcode: string): string | undefined { + const emoji = resolveEmoji(shortcode); + return emoji ? `${twemoji.base}svg/${emoji.hexcode}.svg` : undefined; +} + +export default function Emoji({ + className = "h-6 w-6", + shortcode, + tooltip = true, +}: { + className?: string; + shortcode: string; + tooltip?: boolean; +}) { + const emoji = resolveEmoji(shortcode); + if (!emoji) return {shortcode}; + + const image = ( + {emoji.shortcode} + ); + + if (!tooltip) return image; + + return ( + + + {emoji.shortcode} + + ); +} diff --git a/packages/markdown/src/emojiData.ts b/packages/markdown/src/emojiData.ts new file mode 100644 index 0000000..158b5b0 --- /dev/null +++ b/packages/markdown/src/emojiData.ts @@ -0,0 +1,94 @@ +import shortcodeData from "emojibase-data/en/shortcodes/joypixels.json"; + +export type EmojiDefinition = { + aliases: readonly string[]; + hexcode: string; + name: string; + shortcode: string; +}; + +function normalizeName(value: string) { + return value + .trim() + .replace(/^:+|:+$/g, "") + .toLowerCase(); +} + +export const emojis: readonly EmojiDefinition[] = Object.entries( + shortcodeData as Record, +).map(([hexcode, value]) => { + const aliases = Array.isArray(value) ? value : [value]; + const name = aliases[0]; + + return { + aliases, + hexcode: hexcode.toLowerCase().replaceAll("_", "-"), + name, + shortcode: `:${name}:`, + }; +}); + +const emojiByName = new Map(); +for (const emoji of emojis) { + for (const alias of emoji.aliases) { + emojiByName.set(normalizeName(alias), emoji); + } +} + +export function resolveEmoji(value: string): EmojiDefinition | undefined { + return emojiByName.get(normalizeName(value)); +} + +export function normalizeShortcode(value: string): string | undefined { + return resolveEmoji(value)?.shortcode; +} + +export function findEmojiShortcodes(value: string) { + const matches: Array<{ + emoji: EmojiDefinition; + from: number; + to: number; + }> = []; + let searchFrom = 0; + + while (searchFrom < value.length) { + const from = value.indexOf(":", searchFrom); + if (from === -1) break; + + const candidate = value.slice(from).match(/^:([a-z0-9_+-]+):/i); + if (!candidate) { + searchFrom = from + 1; + continue; + } + + const emoji = resolveEmoji(candidate[1]); + if (!emoji) { + // The closing colon may also open the next valid shortcode. + searchFrom = from + candidate[0].length - 1; + continue; + } + + const to = from + candidate[0].length; + matches.push({ emoji, from, to }); + searchFrom = to; + } + + return matches; +} + +export function searchEmojis(query: string): EmojiDefinition[] { + const normalizedQuery = normalizeName(query); + if (!normalizedQuery) return [...emojis]; + + return emojis + .map((emoji) => { + const names = emoji.aliases.map(normalizeName); + const exact = names.includes(normalizedQuery); + const prefix = names.some((name) => name.startsWith(normalizedQuery)); + const contains = names.some((name) => name.includes(normalizedQuery)); + return { emoji, rank: exact ? 0 : prefix ? 1 : contains ? 2 : 3 }; + }) + .filter(({ rank }) => rank < 3) + .sort((a, b) => a.rank - b.rank || a.emoji.name.localeCompare(b.emoji.name)) + .map(({ emoji }) => emoji); +} diff --git a/packages/markdown/src/input.tsx b/packages/markdown/src/input.tsx new file mode 100644 index 0000000..e16c84d --- /dev/null +++ b/packages/markdown/src/input.tsx @@ -0,0 +1,850 @@ +import { markdown } from "@codemirror/lang-markdown"; +import { syntaxTree } from "@codemirror/language"; +import { + acceptCompletion, + autocompletion, + completionStatus, + pickedCompletion, + startCompletion, + type Completion, + type CompletionContext, + type CompletionResult, +} from "@codemirror/autocomplete"; +import { + EditorState, + EditorSelection, + Annotation, + Compartment, + Prec, + Transaction, + type Extension, + type Range, + type SelectionRange, +} from "@codemirror/state"; +import { + Decoration, + EditorView, + keymap, + placeholder, + ViewPlugin, + WidgetType, + type DecorationSet, + type KeyBinding, + type ViewUpdate, +} from "@codemirror/view"; +import { + defaultKeymap, + history, + historyKeymap, + indentWithTab, +} from "@codemirror/commands"; +import { useEffect, useRef } from "react"; +import type { CSSProperties } from "react"; +import { createRoot, type Root } from "react-dom/client"; + +import { collectInlineRanges, ensureMarkdownStyles } from "./markdown"; +import Emoji, { + findEmojiShortcodes, + getEmojiUrl, + resolveEmoji, + searchEmojis, +} from "./emoji"; + +export const MAX_RENDERED_EMOJI_OPTIONS = 100; + +export type InputController = { + focus: () => void; + hasFocus: () => boolean; + insertText: (text: string) => void; +}; + +export type InputProps = { + ref?: HTMLDivElement; + placeholder?: string; + value: string; + setValue: (value: string) => void; + onSubmit?: () => void; + invertEnterBehavior?: boolean; + styled?: boolean; + fontSize?: CSSProperties["fontSize"]; + paddingX?: CSSProperties["padding"]; + paddingY?: CSSProperties["padding"]; + className?: string; + emojiFrequencies?: Readonly>; + onEmojiSelect?: (shortcode: string) => void; + autoFocus?: boolean; + onControllerChange?: (controller: InputController | null) => void; +}; + +function toCssLength(value: CSSProperties["padding"]): string | undefined { + if (value === undefined) { + return undefined; + } + + return typeof value === "number" ? `${value}px` : value; +} + +function toCssPadding( + vertical: CSSProperties["padding"], + horizontal: CSSProperties["padding"], + styled: boolean, +): string { + const defaultVertical = styled ? "0.25rem" : "0"; + const defaultHorizontal = styled ? "0.625rem" : "0"; + + return `${toCssLength(vertical) ?? defaultVertical} ${toCssLength(horizontal) ?? defaultHorizontal}`; +} + +const hiddenTokenDecoration = Decoration.mark({ class: "tm-md-hidden-token" }); +const strongDecoration = Decoration.mark({ class: "tm-md-strong" }); +const emDecoration = Decoration.mark({ class: "tm-md-em" }); +const delDecoration = Decoration.mark({ class: "tm-md-del" }); +const codeDecoration = Decoration.mark({ class: "tm-md-code" }); +const linkDecoration = Decoration.mark({ class: "tm-md-link" }); +const codeLineDecoration = Decoration.line({ class: "tm-md-code-line" }); +const externalValueSync = Annotation.define(); +const widgetRoots = new WeakMap(); + +type EmojiRange = { + from: number; + shortcode: string; + to: number; + url: string; +}; + +class EmojiWidget extends WidgetType { + readonly shortcode: string; + readonly url: string; + + constructor(shortcode: string, url: string) { + super(); + this.shortcode = shortcode; + this.url = url; + } + + eq(other: EmojiWidget) { + return other.shortcode === this.shortcode && other.url === this.url; + } + + toDOM() { + const container = document.createElement("span"); + const root = createRoot(container); + root.render( + , + ); + widgetRoots.set(container, root); + return container; + } + + destroy(dom: HTMLElement) { + widgetRoots.get(dom)?.unmount(); + widgetRoots.delete(dom); + } + + ignoreEvent() { + return true; + } +} + +function codeRanges(state: EditorState) { + const ranges: Array<{ from: number; to: number }> = []; + + syntaxTree(state).iterate({ + enter(node) { + if ( + node.name === "InlineCode" || + node.name === "FencedCode" || + node.name === "CodeBlock" + ) { + ranges.push({ from: node.from, to: node.to }); + return false; + } + }, + }); + + return ranges; +} + +export function findEmojiRanges(state: EditorState): EmojiRange[] { + const document = state.doc.toString(); + const excluded = codeRanges(state); + const ranges: EmojiRange[] = []; + + for (const match of findEmojiShortcodes(document)) { + const { from, to } = match; + const inCode = excluded.some((range) => from < range.to && to > range.from); + const emoji = inCode ? undefined : match.emoji; + const url = emoji ? getEmojiUrl(emoji.shortcode) : undefined; + + if (emoji && url) { + ranges.push({ from, shortcode: emoji.shortcode, to, url }); + } + } + + return ranges; +} + +class EmojiPluginValue { + decorations: DecorationSet; + ranges: EmojiRange[]; + + constructor(view: EditorView) { + this.ranges = findEmojiRanges(view.state); + this.decorations = this.buildDecorations(); + } + + update(update: ViewUpdate) { + if ( + update.docChanged || + syntaxTree(update.startState) !== syntaxTree(update.state) + ) { + this.ranges = findEmojiRanges(update.state); + this.decorations = this.buildDecorations(); + } + } + + private buildDecorations() { + return Decoration.set( + this.ranges.map((range) => + Decoration.replace({ + inclusive: false, + widget: new EmojiWidget(range.shortcode, range.url), + }).range(range.from, range.to), + ), + true, + ); + } +} + +const emojiDecorations = ViewPlugin.fromClass(EmojiPluginValue, { + decorations: (instance) => instance.decorations, + provide: (plugin) => + EditorView.atomicRanges.of( + (view) => view.plugin(plugin)?.decorations ?? Decoration.none, + ), +}); + +function deleteEmoji(view: EditorView, direction: "backward" | "forward") { + const ranges = view.plugin(emojiDecorations)?.ranges ?? []; + const deletions: Array<{ from: number; to: number }> = []; + + for (const selection of view.state.selection.ranges) { + if (selection.empty) { + const emoji = ranges.find((range) => + direction === "backward" + ? selection.from > range.from && selection.from <= range.to + : selection.from >= range.from && selection.from < range.to, + ); + if (emoji) deletions.push({ from: emoji.from, to: emoji.to }); + continue; + } + + let from = selection.from; + let to = selection.to; + let changed = false; + + for (const emoji of ranges) { + if (from < emoji.to && to > emoji.from) { + from = Math.min(from, emoji.from); + to = Math.max(to, emoji.to); + changed = true; + } + } + + if (changed) deletions.push({ from, to }); + } + + if (deletions.length === 0) return false; + + const merged = deletions + .sort((a, b) => a.from - b.from) + .reduce>((result, deletion) => { + const previous = result.at(-1); + if (previous && deletion.from <= previous.to) { + previous.to = Math.max(previous.to, deletion.to); + } else { + result.push({ ...deletion }); + } + return result; + }, []); + + view.dispatch({ + changes: merged.map((range) => ({ from: range.from, to: range.to })), + selection: EditorSelection.cursor(merged[0].from), + scrollIntoView: true, + userEvent: direction === "backward" ? "delete.backward" : "delete.forward", + }); + return true; +} + +/** + * Builds markdown styling decorations every time the document or cursor selection changes. + * Token delimiters are hidden unless the cursor is currently intersecting that token range. + */ +const markdownDecorations = ViewPlugin.fromClass( + class { + decorations: DecorationSet; + + constructor(view: EditorView) { + this.decorations = buildDecorations(view); + } + + update(update: ViewUpdate) { + if (update.docChanged || update.selectionSet || update.viewportChanged) { + this.decorations = buildDecorations(update.view); + } + } + }, + { + decorations: (instance: { decorations: DecorationSet }) => + instance.decorations, + }, +); + +/** + * Executes Input. + * @param props Parameter props. + * @returns unknown. + */ +export default function Input(props: InputProps) { + ensureMarkdownStyles(); + + const shellClassName = props.styled + ? "min-h-8 w-full min-w-0 rounded-lg border border-input bg-transparent text-base transition-colors outline-none placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40" + : ""; + + const elementRef = useRef(null); + const viewRef = useRef(undefined); + const setValueRef = useRef(props.setValue); + const onSubmitRef = useRef(props.onSubmit); + const onEmojiSelectRef = useRef( + props.onEmojiSelect, + ); + const invertEnterBehaviorRef = useRef(Boolean(props.invertEnterBehavior)); + const completionCompartmentRef = useRef(null); + completionCompartmentRef.current ??= new Compartment(); + const completionCompartment = completionCompartmentRef.current; + + useEffect(() => { + setValueRef.current = props.setValue; + onSubmitRef.current = props.onSubmit; + onEmojiSelectRef.current = props.onEmojiSelect; + invertEnterBehaviorRef.current = Boolean(props.invertEnterBehavior); + }, [ + props.onEmojiSelect, + props.onSubmit, + props.invertEnterBehavior, + props.setValue, + ]); + + useEffect(() => { + if (!elementRef.current) return; + + const state = EditorState.create({ + doc: props.value, + extensions: createEditorExtensions( + (value) => { + setValueRef.current(value); + }, + () => props.placeholder, + () => invertEnterBehaviorRef.current, + () => onSubmitRef.current?.(), + completionCompartment, + props.emojiFrequencies, + (shortcode) => onEmojiSelectRef.current?.(shortcode), + ), + }); + + viewRef.current = new EditorView({ + state, + parent: elementRef.current, + }); + props.onControllerChange?.({ + focus: () => viewRef.current?.contentDOM.focus({ preventScroll: true }), + hasFocus: () => viewRef.current?.hasFocus ?? false, + insertText: (text) => { + const editor = viewRef.current; + if (!editor) return; + editor.dispatch({ + ...editor.state.replaceSelection(text), + annotations: Transaction.userEvent.of("input.type"), + scrollIntoView: true, + }); + }, + }); + if (props.autoFocus) viewRef.current.focus(); + + return () => { + props.onControllerChange?.(null); + viewRef.current?.destroy(); + viewRef.current = undefined; + }; + // Run once to initialize/destroy the editor instance. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + const editor = viewRef.current; + if (!editor) return; + + const next = props.value; + const current = editor.state.doc.toString(); + + if (next === current) return; + + editor.dispatch({ + changes: { + from: 0, + to: current.length, + insert: next, + }, + annotations: [ + externalValueSync.of(true), + Transaction.addToHistory.of(false), + ], + filter: false, + }); + }, [props.value]); + + useEffect(() => { + const editor = viewRef.current; + const compartment = completionCompartmentRef.current; + if (!editor || !compartment) return; + + const wasActive = completionStatus(editor.state) === "active"; + editor.dispatch({ + effects: compartment.reconfigure( + createEmojiAutocomplete(props.emojiFrequencies, (shortcode) => + onEmojiSelectRef.current?.(shortcode), + ), + ), + }); + if (wasActive) startCompletion(editor); + }, [props.emojiFrequencies]); + + return ( +
+ ); +} + +/** + * Executes createEditorExtensions. + * @param onChange Parameter onChange. + * @param getPlaceholder Parameter getPlaceholder. + * @param getInvertEnterBehavior Parameter getInvertEnterBehavior. + * @param onSubmit Parameter onSubmit. + * @returns Extension[]. + */ +function createEditorExtensions( + onChange: (value: string) => void, + getPlaceholder: () => string | undefined, + getInvertEnterBehavior: () => boolean, + onSubmit: () => void, + completionCompartment: Compartment, + emojiFrequencies: Readonly> | undefined, + onEmojiSelect: (shortcode: string) => void, +): Extension[] { + const editorKeymap = [ + ...defaultKeymap, + ...historyKeymap, + indentWithTab, + ] as unknown as readonly KeyBinding[]; + + const customEnterKeymap = keymap.of([ + { + key: "Shift-Enter", + run: (view) => { + if (completionStatus(view.state) === "active") { + return acceptCompletion(view); + } + if (!getInvertEnterBehavior()) { + return false; + } + + onSubmit(); + return true; + }, + }, + { + key: "Enter", + run: (view) => { + if (completionStatus(view.state) === "active") { + return acceptCompletion(view); + } + if (getInvertEnterBehavior()) { + return false; + } + + onSubmit(); + return true; + }, + }, + ]); + const completionTabKeymap = keymap.of([ + { + key: "Tab", + run: (view) => + completionStatus(view.state) === "active" + ? acceptCompletion(view) + : false, + }, + ]); + const emojiDeletionKeymap = keymap.of([ + { + key: "Backspace", + run: (view) => deleteEmoji(view, "backward"), + }, + { + key: "Delete", + run: (view) => deleteEmoji(view, "forward"), + }, + ]); + + return [ + history(), + markdown(), + completionCompartment.of( + createEmojiAutocomplete(emojiFrequencies, onEmojiSelect), + ), + emojiDecorations, + keymap.of(editorKeymap), + Prec.highest(completionTabKeymap), + Prec.highest(emojiDeletionKeymap), + Prec.highest(customEnterKeymap), + EditorView.lineWrapping, + placeholder(getPlaceholder() ?? ""), + EditorView.updateListener.of((update: ViewUpdate) => { + if (!update.docChanged) return; + if ( + update.transactions.some( + (transaction) => transaction.annotation(externalValueSync) === true, + ) + ) { + return; + } + onChange(update.state.doc.toString()); + }), + EditorView.theme({ + "&": { + fontSize: "inherit", + }, + "&.cm-editor": { + width: "100%", + }, + }), + EditorView.editorAttributes.of({ + class: "tm-md-editor", + spellcheck: "true", + "aria-label": "Markdown input", + }), + markdownDecorations, + ]; +} + +function createEmojiAutocomplete( + frequencies: Readonly> | undefined, + onEmojiSelect: (shortcode: string) => void, +) { + return autocompletion({ + activateOnTyping: true, + addToOptions: [ + { + position: 20, + render(completion) { + const container = document.createElement("span"); + createRoot(container).render( + , + ); + return container; + }, + }, + ], + maxRenderedOptions: MAX_RENDERED_EMOJI_OPTIONS, + override: [createEmojiCompletionSource(frequencies, onEmojiSelect)], + }); +} + +function normalizedFrequencies( + frequencies: Readonly> | undefined, +) { + const normalized = new Map(); + for (const [value, frequency] of Object.entries(frequencies ?? {})) { + const shortcode = resolveEmoji(value)?.shortcode; + if (!shortcode || !Number.isFinite(frequency) || frequency <= 0) continue; + normalized.set(shortcode, (normalized.get(shortcode) ?? 0) + frequency); + } + return normalized; +} + +export function createEmojiCompletionSource( + frequencies?: Readonly>, + onEmojiSelect: (shortcode: string) => void = () => undefined, +) { + const normalized = normalizedFrequencies(frequencies); + const maxFrequency = Math.max(0, ...normalized.values()); + + return (context: CompletionContext): CompletionResult | null => { + const token = context.matchBefore(/:[a-z0-9_+-]*$/i); + if (!token) return null; + if ( + codeRanges(context.state).some( + (range) => token.from < range.to && token.to > range.from, + ) + ) { + return null; + } + + const characterBefore = context.state.sliceDoc( + Math.max(0, token.from - 1), + token.from, + ); + if (characterBefore && /[a-z0-9_]/i.test(characterBefore)) return null; + + const query = token.text.slice(1).toLowerCase(); + const options: Completion[] = searchEmojis(query) + .map((emoji) => { + const aliases = emoji.aliases.map((alias) => alias.toLowerCase()); + const matchedAlias = + aliases.find((alias) => alias === query) ?? + aliases.find((alias) => alias.startsWith(query)) ?? + aliases.find((alias) => alias.includes(query)) ?? + emoji.name; + const relevance = !query + ? 0 + : matchedAlias === query + ? 80 + : matchedAlias.startsWith(query) + ? 40 + : 0; + const frequency = normalized.get(emoji.shortcode) ?? 0; + const usage = + maxFrequency > 0 + ? (15 * Math.log1p(frequency)) / Math.log1p(maxFrequency) + : 0; + + return { + apply(view, completion, from, to) { + view.dispatch({ + annotations: pickedCompletion.of(completion), + changes: { from, insert: `${emoji.shortcode} `, to }, + selection: EditorSelection.cursor( + from + emoji.shortcode.length + 1, + ), + }); + onEmojiSelect(emoji.shortcode); + }, + boost: relevance + usage, + displayLabel: emoji.shortcode, + label: `:${matchedAlias}:`, + type: "text", + frequency, + relevance, + } satisfies Completion & { frequency: number; relevance: number }; + }) + .sort( + (a, b) => + b.relevance - a.relevance || + b.frequency - a.frequency || + (a.displayLabel ?? a.label).localeCompare(b.displayLabel ?? b.label), + ); + + return { + from: token.from, + options, + validFor: /^:[a-z0-9_+-]*$/i, + }; + }; +} + +export const emojiCompletionSource = createEmojiCompletionSource(); + +/** + * Executes buildDecorations. + * @param view Parameter view. + * @returns DecorationSet. + */ +function buildDecorations(view: EditorView): DecorationSet { + const builder: Range[] = []; + const selections = view.state.selection.ranges.map( + (range: SelectionRange) => ({ + from: range.from, + to: range.to, + }), + ); + + let codeFenceOpen = false; + + for ( + let lineNumber = 1; + lineNumber <= view.state.doc.lines; + lineNumber += 1 + ) { + const line = view.state.doc.line(lineNumber); + const text = line.text; + const lineFrom = line.from; + const trimmed = text.trim(); + + const fence = text.match(/^```\s*([^`]*)$/); + if (fence) { + const ticksStart = lineFrom + text.indexOf("```"); + const ticksEnd = ticksStart + 3; + addHiddenToken(builder, selections, { from: ticksStart, to: ticksEnd }); + if (trimmed.length > 3) { + addHiddenToken(builder, selections, { + from: ticksEnd, + to: line.to, + }); + } + codeFenceOpen = !codeFenceOpen; + continue; + } + + if (codeFenceOpen) { + builder.push(codeLineDecoration.range(lineFrom)); + continue; + } + + const heading = text.match(/^(#{1,6})\s+/); + if (heading) { + const markerLength = heading[0].length; + addHiddenToken(builder, selections, { + from: lineFrom, + to: lineFrom + markerLength, + }); + + const level = heading[1].length; + const headingClass = Decoration.mark({ + class: `tm-md-heading tm-md-h${String(level)}`, + }); + const contentFrom = lineFrom + markerLength; + if (contentFrom < line.to) { + builder.push(headingClass.range(contentFrom, line.to)); + } + } + + const quote = text.match(/^>\s?/); + if (quote) { + addHiddenToken(builder, selections, { + from: lineFrom, + to: lineFrom + quote[0].length, + }); + } + + const unordered = text.match(/^(\s*)([-+*])\s+(?:\[( |x|X)\]\s+)?/); + if (unordered) { + const markerStart = lineFrom + unordered[1].length; + const markerEnd = markerStart + unordered[2].length + 1; + addHiddenToken(builder, selections, { from: markerStart, to: markerEnd }); + + const checkbox = unordered[0].match(/\[( |x|X)\]\s+$/); + if (checkbox) { + const checkboxStart = lineFrom + unordered[0].lastIndexOf("["); + addHiddenToken(builder, selections, { + from: checkboxStart, + to: checkboxStart + checkbox[0].length, + }); + } + } + + const ordered = text.match(/^(\s*)(\d+\.)\s+/); + if (ordered) { + const markerStart = lineFrom + ordered[1].length; + addHiddenToken(builder, selections, { + from: markerStart, + to: markerStart + ordered[2].length + 1, + }); + } + + if (/^(?:\*\s*){3,}$|^(?:-\s*){3,}$|^(?:_\s*){3,}$/.test(trimmed)) { + if (lineFrom < line.to) { + builder.push( + Decoration.mark({ class: "tm-md-hr" }).range(lineFrom, line.to), + ); + } + continue; + } + + const tableSeparator = /^\|?\s*:?-{3,}:?\s*(?:\|\s*:?-{3,}:?\s*)+\|?$/.test( + text, + ); + if (tableSeparator) { + if (lineFrom < line.to) { + builder.push( + Decoration.mark({ class: "tm-md-del" }).range(lineFrom, line.to), + ); + } + continue; + } + + const { styleRanges, tokenRanges } = collectInlineRanges(text, lineFrom); + + for (const range of styleRanges) { + if (range.from >= range.to) continue; + + if (range.className === "tm-md-strong") { + builder.push(strongDecoration.range(range.from, range.to)); + } else if (range.className === "tm-md-em") { + builder.push(emDecoration.range(range.from, range.to)); + } else if (range.className === "tm-md-del") { + builder.push(delDecoration.range(range.from, range.to)); + } else if (range.className === "tm-md-code") { + builder.push(codeDecoration.range(range.from, range.to)); + } else if (range.className === "tm-md-link") { + builder.push(linkDecoration.range(range.from, range.to)); + } + } + + for (const token of tokenRanges) { + addHiddenToken(builder, selections, token); + } + } + + return Decoration.set(builder, true); +} + +/** + * Keeps markdown syntax visible only when user selection intersects the token. + * This preserves cursor predictability and cross-token selection while still hiding syntax during reading. + */ +function addHiddenToken( + builder: Range[], + selections: ReadonlyArray<{ from: number; to: number }>, + token: { + from: number; + to: number; + }, +): void { + if (token.from >= token.to) return; + + const overlapsSelection = selections.some((selection) => { + const selectionFrom = Math.min(selection.from, selection.to); + const selectionTo = Math.max(selection.from, selection.to); + + if (selectionFrom === selectionTo) { + return selectionFrom >= token.from && selectionFrom <= token.to; + } + + return selectionFrom < token.to && selectionTo > token.from; + }); + + if (overlapsSelection) return; + builder.push(hiddenTokenDecoration.range(token.from, token.to)); +} diff --git a/packages/markdown/src/markdown.tsx b/packages/markdown/src/markdown.tsx new file mode 100644 index 0000000..cd2a210 --- /dev/null +++ b/packages/markdown/src/markdown.tsx @@ -0,0 +1,815 @@ +import { + Fragment, + useEffect, + useRef, + useState, + type ReactElement, + type ReactNode, +} from "react"; +import Emoji from "./emoji"; +import { findEmojiShortcodes } from "./emojiData"; +import { Check } from "lucide-react"; + +type InlineNode = + | { type: "text"; value: string } + | { type: "emoji"; shortcode: string } + | { type: "strong"; value: string } + | { type: "em"; value: string } + | { type: "del"; value: string } + | { type: "code"; value: string } + | { type: "link"; label: string; href: string } + | { type: "image"; alt: string; src: string }; + +type InlineDecorationRange = { + from: number; + to: number; + className: string; +}; + +type InlineTokenRange = { + from: number; + to: number; +}; + +type ListItem = { + text: string; + checked: boolean | null; +}; + +type TableBlock = { + type: "table"; + headers: string[]; + rows: string[][]; +}; + +type MarkdownBlock = + | { + type: "paragraph"; + text: string; + } + | { + type: "heading"; + level: number; + text: string; + } + | { + type: "hr"; + } + | { + type: "blockquote"; + text: string; + } + | { + type: "code"; + language: string; + code: string; + } + | { + type: "list"; + ordered: boolean; + items: ListItem[]; + } + | TableBlock; + +const INLINE_TOKEN_REGEX = + /!\[([^\]]*)\]\(([^)\s]+(?:\s+"[^"]*")?)\)|\[([^\]]+)\]\(([^)\s]+(?:\s+"[^"]*")?)\)|`([^`\n]+)`|~~([^~\n]+)~~|\*\*([^*\n]+)\*\*|__([^_\n]+)__|\*([^*\n]+)\*|(? + + + ); +} + +function CopyableCode({ + block = false, + language, + value, +}: { + block?: boolean; + language?: string; + value: string; +}) { + const [copied, setCopied] = useState(false); + const copiedTimer = useRef | undefined>( + undefined, + ); + + useEffect( + () => () => { + clearTimeout(copiedTimer.current); + }, + [], + ); + + async function copy() { + await navigator.clipboard.writeText(value); + setCopied(true); + clearTimeout(copiedTimer.current); + copiedTimer.current = setTimeout(() => setCopied(false), 1200); + } + + const code = ( + void copy()} + onKeyDown={(event) => { + if (event.key !== "Enter" && event.key !== " ") return; + event.preventDefault(); + void copy(); + }} + > + {value} + + ); + + if (block) { + return ( +
+
{code}
+ +
+ ); + } + + return ( + <> + {code} + + + ); +} + +/** + * Executes parseInlineNodes. + * @param input Parameter input. + * @returns InlineNode[]. + */ +export function parseInlineNodes(input: string): InlineNode[] { + const nodes: InlineNode[] = []; + + let cursor = 0; + let match = INLINE_TOKEN_REGEX.exec(input); + + while (match) { + const index = match.index; + const raw = match[0]; + + if (index > cursor) { + nodes.push(...parseEmojiText(input.slice(cursor, index))); + } + + if (match[1] !== undefined && match[2] !== undefined) { + nodes.push({ type: "image", alt: match[1], src: normalizeUrl(match[2]) }); + } else if (match[3] !== undefined && match[4] !== undefined) { + nodes.push({ + type: "link", + label: match[3], + href: normalizeUrl(match[4]), + }); + } else if (match[5] !== undefined) { + nodes.push({ type: "code", value: match[5] }); + } else if (match[6] !== undefined) { + nodes.push({ type: "del", value: match[6] }); + } else if (match[7] !== undefined || match[8] !== undefined) { + nodes.push({ type: "strong", value: match[7] ?? match[8] ?? "" }); + } else if (match[9] !== undefined || match[10] !== undefined) { + nodes.push({ type: "em", value: match[9] ?? match[10] ?? "" }); + } else { + nodes.push(...parseEmojiText(raw)); + } + + cursor = index + raw.length; + match = INLINE_TOKEN_REGEX.exec(input); + } + + if (cursor < input.length) { + nodes.push(...parseEmojiText(input.slice(cursor))); + } + + INLINE_TOKEN_REGEX.lastIndex = 0; + return nodes; +} + +export function parseEmojiText(input: string): InlineNode[] { + const nodes: InlineNode[] = []; + let cursor = 0; + + for (const match of findEmojiShortcodes(input)) { + if (match.from > cursor) { + nodes.push({ type: "text", value: input.slice(cursor, match.from) }); + } + + nodes.push({ type: "emoji", shortcode: match.emoji.shortcode }); + cursor = match.to; + } + + if (cursor < input.length) { + nodes.push({ type: "text", value: input.slice(cursor) }); + } + + return nodes; +} + +/** + * Executes collectInlineRanges. + * @param input Parameter input. + * @param offset Parameter offset. + * @returns { + styleRanges: InlineDecorationRange[]; + tokenRanges: InlineTokenRange[]; +}. + */ +export function collectInlineRanges( + input: string, + offset = 0, +): { + styleRanges: InlineDecorationRange[]; + tokenRanges: InlineTokenRange[]; +} { + const styleRanges: InlineDecorationRange[] = []; + const tokenRanges: InlineTokenRange[] = []; + + let match = INLINE_TOKEN_REGEX.exec(input); + + while (match) { + const raw = match[0]; + const start = offset + match.index; + const end = start + raw.length; + + if (match[1] !== undefined && match[2] !== undefined) { + const openLength = 2; + const closeLength = raw.endsWith(")") ? 1 : 0; + + const imageEnd = start + openLength + match[1].length; + tokenRanges.push({ from: start, to: start + openLength }); + tokenRanges.push({ from: imageEnd, to: imageEnd + 1 }); + + const srcStart = imageEnd + 1; + const srcEnd = end - closeLength; + tokenRanges.push({ from: srcStart, to: srcStart + 1 }); + tokenRanges.push({ from: srcEnd, to: srcEnd + closeLength }); + } else if (match[3] !== undefined && match[4] !== undefined) { + const label = match[3]; + const labelStart = start + 1; + const labelEnd = labelStart + label.length; + + tokenRanges.push({ from: start, to: start + 1 }); + tokenRanges.push({ from: labelEnd, to: labelEnd + 1 }); + tokenRanges.push({ from: labelEnd + 1, to: labelEnd + 2 }); + tokenRanges.push({ from: end - 1, to: end }); + + styleRanges.push({ + from: labelStart, + to: labelEnd, + className: "tm-md-link", + }); + } else if (match[5] !== undefined) { + const codeStart = start + 1; + const codeEnd = end - 1; + + tokenRanges.push({ from: start, to: start + 1 }); + tokenRanges.push({ from: end - 1, to: end }); + styleRanges.push({ + from: codeStart, + to: codeEnd, + className: "tm-md-code", + }); + } else if (match[6] !== undefined) { + const contentStart = start + 2; + const contentEnd = end - 2; + + tokenRanges.push({ from: start, to: start + 2 }); + tokenRanges.push({ from: end - 2, to: end }); + styleRanges.push({ + from: contentStart, + to: contentEnd, + className: "tm-md-del", + }); + } else if (match[7] !== undefined || match[8] !== undefined) { + const contentStart = start + 2; + const contentEnd = end - 2; + + tokenRanges.push({ from: start, to: start + 2 }); + tokenRanges.push({ from: end - 2, to: end }); + styleRanges.push({ + from: contentStart, + to: contentEnd, + className: "tm-md-strong", + }); + } else if (match[9] !== undefined || match[10] !== undefined) { + const contentStart = start + 1; + const contentEnd = end - 1; + + tokenRanges.push({ from: start, to: start + 1 }); + tokenRanges.push({ from: end - 1, to: end }); + styleRanges.push({ + from: contentStart, + to: contentEnd, + className: "tm-md-em", + }); + } + + match = INLINE_TOKEN_REGEX.exec(input); + } + + INLINE_TOKEN_REGEX.lastIndex = 0; + return { styleRanges, tokenRanges }; +} + +/** + * Executes parseMarkdownBlocks. + * @param markdown Parameter markdown. + * @returns MarkdownBlock[]. + */ +export function parseMarkdownBlocks(markdown: string): MarkdownBlock[] { + const lines = markdown.replace(/\r\n/g, "\n").split("\n"); + const blocks: MarkdownBlock[] = []; + + let index = 0; + + while (index < lines.length) { + const line = lines[index]; + + if (!line.trim()) { + index += 1; + continue; + } + + const codeFence = line.match(/^```\s*([^`]*)$/); + if (codeFence) { + const language = (codeFence[1] ?? "").trim(); + const codeLines: string[] = []; + index += 1; + + while (index < lines.length && !/^```\s*$/.test(lines[index])) { + codeLines.push(lines[index]); + index += 1; + } + + if (index < lines.length) { + index += 1; + } + + blocks.push({ type: "code", language, code: codeLines.join("\n") }); + continue; + } + + if (/^(?:\*\s*){3,}$|^(?:-\s*){3,}$|^(?:_\s*){3,}$/.test(line.trim())) { + blocks.push({ type: "hr" }); + index += 1; + continue; + } + + const heading = line.match(/^(#{1,6})\s+(.+)$/); + if (heading) { + blocks.push({ + type: "heading", + level: heading[1].length, + text: heading[2], + }); + index += 1; + continue; + } + + const quote = line.match(/^>\s?(.*)$/); + if (quote) { + const quoteLines: string[] = [quote[1]]; + index += 1; + + while (index < lines.length) { + const next = lines[index].match(/^>\s?(.*)$/); + if (!next) break; + quoteLines.push(next[1]); + index += 1; + } + + blocks.push({ type: "blockquote", text: quoteLines.join("\n") }); + continue; + } + + const tableCandidate = readTable(lines, index); + if (tableCandidate) { + blocks.push(tableCandidate.block); + index = tableCandidate.nextIndex; + continue; + } + + const unordered = line.match(/^\s*[-*+]\s+(.*)$/); + const ordered = line.match(/^\s*\d+\.\s+(.*)$/); + if (unordered || ordered) { + const orderedList = Boolean(ordered); + const items: ListItem[] = []; + + while (index < lines.length) { + const current = lines[index]; + const match = orderedList + ? current.match(/^\s*\d+\.\s+(.*)$/) + : current.match(/^\s*[-*+]\s+(.*)$/); + + if (!match) break; + + const task = match[1].match(/^\[( |x|X)\]\s+(.*)$/); + if (task) { + items.push({ + text: task[2], + checked: task[1].toLowerCase() === "x", + }); + } else { + items.push({ text: match[1], checked: null }); + } + + index += 1; + } + + blocks.push({ type: "list", ordered: orderedList, items }); + continue; + } + + const paragraphLines = [line]; + index += 1; + + while ( + index < lines.length && + lines[index].trim() && + !/^(#{1,6})\s+/.test(lines[index]) && + !/^```\s*/.test(lines[index]) && + !/^>\s?/.test(lines[index]) && + !/^\s*[-*+]\s+/.test(lines[index]) && + !/^\s*\d+\.\s+/.test(lines[index]) && + !/^(?:\*\s*){3,}$|^(?:-\s*){3,}$|^(?:_\s*){3,}$/.test(lines[index].trim()) + ) { + paragraphLines.push(lines[index]); + index += 1; + } + + blocks.push({ type: "paragraph", text: paragraphLines.join("\n") }); + } + + return blocks; +} + +/** + * Executes renderInline. + * @param nodes Parameter nodes. + * @returns React.ReactNode[]. + */ +function renderInline(nodes: InlineNode[]): ReactNode[] { + return nodes.map((node, index) => { + if (node.type === "text") { + return node.value; + } + + if (node.type === "emoji") { + return ( + + ); + } + + if (node.type === "strong") { + return ( + + {renderInline(parseEmojiText(node.value))} + + ); + } + + if (node.type === "em") { + return ( + + {renderInline(parseEmojiText(node.value))} + + ); + } + + if (node.type === "del") { + return ( + + {renderInline(parseEmojiText(node.value))} + + ); + } + + if (node.type === "code") { + return ; + } + + if (node.type === "link") { + return ( + + {renderInline(parseEmojiText(node.label))} + + ); + } + + return ( + {node.alt} + ); + }); +} + +/** + * Executes renderBlocks. + * @param blocks Parameter blocks. + * @returns React.ReactElement. + */ +export function renderBlocks(blocks: MarkdownBlock[]): ReactElement { + return ( + <> + {blocks.map((block, blockIndex) => { + if (block.type === "heading") { + const className = `tm-md-heading tm-md-h${String(block.level)}`; + if (block.level === 1) + return ( +

+ {renderInline(parseInlineNodes(block.text))} +

+ ); + if (block.level === 2) + return ( +

+ {renderInline(parseInlineNodes(block.text))} +

+ ); + if (block.level === 3) + return ( +

+ {renderInline(parseInlineNodes(block.text))} +

+ ); + if (block.level === 4) + return ( +

+ {renderInline(parseInlineNodes(block.text))} +

+ ); + if (block.level === 5) + return ( +
+ {renderInline(parseInlineNodes(block.text))} +
+ ); + return ( +
+ {renderInline(parseInlineNodes(block.text))} +
+ ); + } + + if (block.type === "blockquote") { + return ( +
+ {block.text.split("\n").map((line, lineIndex) => ( +

{renderInline(parseInlineNodes(line))}

+ ))} +
+ ); + } + + if (block.type === "code") { + return ( + + ); + } + + if (block.type === "list") { + const Tag = block.ordered ? "ol" : "ul"; + return ( + + {block.items.map((item, itemIndex) => ( +
  • + {item.checked !== null ? ( + + ) : null} + {renderInline(parseInlineNodes(item.text))} +
  • + ))} +
    + ); + } + + if (block.type === "table") { + return ( +
    + + + + {block.headers.map((header, headerIndex) => ( + + ))} + + + + {block.rows.map((row, rowIndex) => ( + + {row.map((cell, cellIndex) => ( + + ))} + + ))} + +
    + {renderInline(parseInlineNodes(header))} +
    + {renderInline(parseInlineNodes(cell))} +
    +
    + ); + } + + if (block.type === "hr") { + return
    ; + } + + return ( +

    + {block.text.split("\n").map((line, lineIndex) => ( + + {lineIndex > 0 ?
    : null} + {renderInline(parseInlineNodes(line))} +
    + ))} +

    + ); + })} + + ); +} + +/** + * Executes normalizeUrl. + * @param input Parameter input. + * @returns string. + */ +function normalizeUrl(input: string): string { + const value = input.trim(); + if (/^(https?:|mailto:|tel:|\/)/i.test(value)) { + return value; + } + + return "#"; +} + +/** + * Executes splitTableRow. + * @param row Parameter row. + * @returns string[]. + */ +function splitTableRow(row: string): string[] { + const cleaned = row.trim().replace(/^\|/, "").replace(/\|$/, ""); + return cleaned.split("|").map((cell) => cell.trim()); +} + +/** + * Executes readTable. + * @param lines Parameter lines. + * @param index Parameter index. + * @returns { block: TableBlock; nextIndex: number } | null. + */ +function readTable( + lines: string[], + index: number, +): { block: TableBlock; nextIndex: number } | null { + const header = lines[index] ?? ""; + const separator = lines[index + 1] ?? ""; + + if (!header.includes("|") || !separator.includes("|")) { + return null; + } + + const separatorCells = splitTableRow(separator); + const isSeparator = separatorCells.every((cell) => /^:?-{3,}:?$/.test(cell)); + if (!isSeparator) { + return null; + } + + const headers = splitTableRow(header); + const rows: string[][] = []; + let cursor = index + 2; + + while (cursor < lines.length && lines[cursor].includes("|")) { + rows.push(splitTableRow(lines[cursor])); + cursor += 1; + } + + return { + block: { type: "table", headers, rows }, + nextIndex: cursor, + }; +} + +const markdownStyles = ` +.tm-md-root { color: var(--foreground); line-height: 1.65; font-size: 1rem; } +.tm-md-heading { margin: 0.2rem 0 0.35rem; font-weight: 700; line-height: 1.25; } +.tm-md-h1 { font-size: 1.65rem; } +.tm-md-h2 { font-size: 1.45rem; } +.tm-md-h3 { font-size: 1.25rem; } +.tm-md-h4 { font-size: 1.1rem; } +.tm-md-h5 { font-size: 1rem; } +.tm-md-h6 { font-size: 0.95rem; opacity: 0.9; } +.tm-md-blockquote { margin: 0.45rem 0; padding-left: 0.75rem; opacity: 0.95; } +.tm-md-blockquote p { margin: 0.2rem 0; } +.tm-md-pre { margin: 0.45rem 0; padding: 0.65rem 0.75rem; border-radius: 0.5rem; background: var(--muted); overflow-x: auto; } +.tm-md-code, .tm-md-codeblock { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.87em; cursor: pointer; } +.tm-md-code { padding: 0.08rem 0.32rem; border: 1px solid var(--border); border-radius: 0.28rem; background: var(--muted); } +.tm-md-codeblock { display: block; } +.tm-md-code:focus-visible, .tm-md-codeblock:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; } +.tm-md-strong { font-weight: 700; } +.tm-md-em { font-style: italic; } +.tm-md-del { text-decoration: line-through; } +.tm-md-link { color: var(--primary); text-decoration: underline; text-underline-offset: 0.14rem; } +.tm-md-image { display: block; max-width: 100%; border-radius: 0.4rem; margin: 0.5rem 0; } +.tm-md-emoji { display: inline-block; width: 1.15em; height: 1.15em; vertical-align: -0.18em; } +.tm-md-ul, .tm-md-ol { margin: 0.3rem 0 0.35rem 1.2rem; padding: 0; } +.tm-md-li { margin: 0.2rem 0; } +.tm-md-checkbox { margin-right: 0.5rem; vertical-align: middle; } +.tm-md-table-wrap { overflow-x: auto; margin: 0.45rem 0; } +.tm-md-table { border-collapse: collapse; width: 100%; min-width: 16rem; } +.tm-md-table th, .tm-md-table td { padding: 0.4rem 0.5rem; text-align: left; } +.tm-md-table th { background: var(--muted); font-weight: 600; } +.tm-md-hr { margin: 0.55rem 0; } + +.cm-editor.tm-md-editor { border-radius: inherit; background: transparent; caret-color: var(--foreground); } +.cm-editor.tm-md-editor.cm-focused { outline: none; box-shadow: none; } +.cm-editor.tm-md-editor .cm-scroller { font-family: inherit; line-height: 1.55; max-height: 30vh; overflow-y: auto; overflow-x: hidden; } +.cm-editor.tm-md-editor .cm-content { caret-color: var(--foreground); } +.cm-editor.tm-md-editor .cm-content { padding: var(--tm-md-content-padding, 0.25rem 0.625rem); min-height: 2rem; } +.cm-editor.tm-md-editor .cm-line { padding: 0; color: var(--foreground); } +.cm-editor.tm-md-editor .tm-md-editor-emoji { display: inline-block; width: 1.15em; height: 1.15em; vertical-align: -0.18em; object-fit: contain; pointer-events: none; } +.cm-editor.tm-md-editor .tm-md-hidden-token { color: transparent; opacity: 0; font-size: inherit; } +.cm-editor.tm-md-editor .tm-md-code-line { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; background: var(--muted); border-radius: 0.3rem; } +.cm-tooltip.cm-tooltip-autocomplete { min-width: 18rem; max-width: min(26rem, calc(100vw - 1rem)); overflow: hidden; border: 1px solid var(--border); border-radius: var(--radius); background: var(--popover); color: var(--popover-foreground); box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); font-family: "Public Sans Variable", sans-serif; font-size: 0.875rem; } +.cm-editor.tm-md-editor .cm-tooltip.cm-tooltip-autocomplete > ul { max-height: min(20rem, 45vh); padding: 0.25rem; font-family: "Public Sans Variable", sans-serif; scrollbar-width: thin; scrollbar-color: var(--border) transparent; } +.cm-tooltip.cm-tooltip-autocomplete > ul::-webkit-scrollbar { width: 6px; height: 6px; } +.cm-tooltip.cm-tooltip-autocomplete > ul::-webkit-scrollbar-track { background: transparent; } +.cm-tooltip.cm-tooltip-autocomplete > ul::-webkit-scrollbar-thumb { border-radius: 9999px; background: var(--border); } +.cm-tooltip.cm-tooltip-autocomplete > ul > li { display: flex; min-height: 2.25rem; align-items: center; border-radius: calc(var(--radius) * 0.8); padding: 0.3rem 0.5rem; color: var(--popover-foreground); } +.cm-tooltip.cm-tooltip-autocomplete > ul > li:hover, +.cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected] { background: var(--accent); color: var(--accent-foreground); } +.cm-tooltip.cm-tooltip-autocomplete .cm-completionIcon { display: none; } +.cm-tooltip.cm-tooltip-autocomplete .cm-completionLabel { overflow: hidden; text-overflow: ellipsis; } +.cm-tooltip.cm-tooltip-autocomplete .cm-completionMatchedText { color: inherit; text-decoration: none; font-weight: 600; } +.cm-tooltip-autocomplete .tm-md-completion-emoji { display: inline-block; width: 1.35rem; height: 1.35rem; flex: 0 0 auto; margin-right: 0.5rem; vertical-align: middle; } +`; + +/** + * Executes ensureMarkdownStyles. + * @param none This function has no parameters. + * @returns void. + */ +export function ensureMarkdownStyles(): void { + if (typeof document === "undefined") return; + + const styleId = "tensamin-markdown-styles"; + let style = document.getElementById(styleId) as HTMLStyleElement | null; + + if (!style) { + style = document.createElement("style"); + style.id = styleId; + document.head.appendChild(style); + } + + if (style.textContent !== markdownStyles) { + style.textContent = markdownStyles; + } +} diff --git a/packages/markdown/src/text.tsx b/packages/markdown/src/text.tsx new file mode 100644 index 0000000..ca65fe0 --- /dev/null +++ b/packages/markdown/src/text.tsx @@ -0,0 +1,30 @@ +import { useMemo, type CSSProperties } from "react"; + +import { + ensureMarkdownStyles, + parseMarkdownBlocks, + renderBlocks, +} from "./markdown"; + +export type TextProps = { + value: string; + fontSize?: CSSProperties["fontSize"]; +}; + +/** + * Executes Text. + * @param props Parameter props. + * @returns unknown. + */ +export default function Text(props: TextProps) { + ensureMarkdownStyles(); + + const blocks = useMemo(() => parseMarkdownBlocks(props.value), [props.value]); + const renderedBlocks = useMemo(() => renderBlocks(blocks), [blocks]); + + return ( +
    + {renderedBlocks} +
    + ); +} diff --git a/packages/markdown/todo.md b/packages/markdown/todo.md new file mode 100644 index 0000000..b59da98 --- /dev/null +++ b/packages/markdown/todo.md @@ -0,0 +1 @@ +- Improve the Input box diff --git a/packages/markdown/tsconfig.json b/packages/markdown/tsconfig.json new file mode 100644 index 0000000..9714625 --- /dev/null +++ b/packages/markdown/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": true, + "skipLibCheck": true, + "noEmit": true + }, + "include": ["src"] +} diff --git a/packages/settings/package.json b/packages/settings/package.json index f3833a9..268974c 100644 --- a/packages/settings/package.json +++ b/packages/settings/package.json @@ -16,6 +16,7 @@ "@tanstack/react-router": "^1.170.21", "@tensamin/cache": "workspace:*", "@tensamin/hotkeys": "workspace:*", + "@tensamin/markdown": "workspace:*", "@tensamin/mtp": "workspace:*", "@tensamin/shared": "workspace:*", "@tensamin/storage": "workspace:*", diff --git a/packages/settings/src/pages/profile.tsx b/packages/settings/src/pages/profile.tsx index 7f8cd5a..080c343 100644 --- a/packages/settings/src/pages/profile.tsx +++ b/packages/settings/src/pages/profile.tsx @@ -1,4 +1,4 @@ -import { Input as MDInput } from "@methanium/ui/markdown"; +import MDInput from "@tensamin/markdown/input"; import { useMTP } from "@tensamin/mtp"; import { mtp } from "@tensamin/shared/data"; import { useStorage } from "@tensamin/storage/context"; @@ -145,6 +145,7 @@ export default function Page() { value={draftUser.Username || ""} /> =20'} - - '@shikijs/engine-javascript@4.4.2': - resolution: {integrity: sha512-MnIkeqWdVPUWsxlx8gKLVCJFTsqrQJgpTPBPpQwaFeJ56lOnJxj5aN2LUFnfxUEcvOQuNocmbaMnVrCEln6rkw==} - engines: {node: '>=20'} - - '@shikijs/engine-oniguruma@4.4.2': - resolution: {integrity: sha512-GLhowz1+jixjz+wiZ3wMnOn1jTxiFCGl2PkXufivbnwPHKuyw1AYqu5/hbWhZZ2oAb0NP05WUJhYeigY14drnw==} - engines: {node: '>=20'} - - '@shikijs/langs@4.4.2': - resolution: {integrity: sha512-8DfeusD+Zdv/eYIDdXyJTUnSMHt+aAWjAOCXV20HNGAHRlInXpG8wh421v6B91WOm9TFwRLN+b/LG5F2NAIojg==} - engines: {node: '>=20'} - - '@shikijs/primitive@4.4.2': - resolution: {integrity: sha512-l6fQQKsOMlz72n38fztmSgZ76MO6KSWuw8o+GJ+FhmqrpC9pIOJNQNXGgbb5yX2AwpzlEHwsaLPnk/8o4Fm+rA==} - engines: {node: '>=20'} - - '@shikijs/themes@4.4.2': - resolution: {integrity: sha512-H0CFoL07ddDC2Dd6EdrPYNkRhUR6YCkJlnuYFceYYUJJA5TIm2b5B33qqiDYryBExgbKMndFJPb2u1gTuqO37g==} - engines: {node: '>=20'} - - '@shikijs/types@4.4.2': - resolution: {integrity: sha512-PFYitV4vpDr/iPCIhnHp+Q4ftic5N5VeNJ3KQ1O8gn3h2ar8qgwMAXF7tq4m1CWaMS60fV4VqF6vfnWH4F7vqQ==} - engines: {node: '>=20'} - - '@shikijs/vscode-textmate@10.0.2': - resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} - '@sindresorhus/is@4.6.0': resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} @@ -1873,6 +1890,12 @@ packages: '@ts-morph/common@0.27.0': resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==} + '@twemoji/api@17.0.3': + resolution: {integrity: sha512-iwERjxY0QgPGVwT6b1OKG0Oa9nIfHhJw+Ij1TapTBMKTvVCU6qdXPXX/XKwxKx5QZIJW5GwELUCtw8wlaIQ2ug==} + + '@twemoji/parser@17.0.2': + resolution: {integrity: sha512-X/P7pHsGOxnrupQYUVetIeuxBGgffFu8CLwoPMMjH9CWmQvlXiCpbTW/BXxMOCWXQojgHdmgdvm6IsCqAQ5nxA==} + '@types/cacheable-request@6.0.3': resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} @@ -1918,33 +1941,21 @@ packages: '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} - '@types/estree-jsx@1.0.5': - resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} - '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} '@types/fs-extra@9.0.13': resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} - '@types/hast@3.0.5': - resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} - '@types/http-cache-semantics@4.2.0': resolution: {integrity: sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/katex@0.16.8': - resolution: {integrity: sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==} - '@types/keyv@3.1.4': resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} - '@types/mdast@4.0.4': - resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} @@ -1968,12 +1979,6 @@ packages: '@types/responselike@1.0.3': resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} - '@types/unist@2.0.11': - resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} - - '@types/unist@3.0.3': - resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@types/use-sync-external-store@0.0.6': resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} @@ -2159,9 +2164,6 @@ packages: cpu: [x64] os: [win32] - '@ungap/structured-clone@1.3.3': - resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} - '@vitejs/plugin-react@6.0.5': resolution: {integrity: sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2315,9 +2317,6 @@ packages: aws4@1.13.2: resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} - bail@2.0.2: - resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -2409,9 +2408,6 @@ packages: caniuse-lite@1.0.30001809: resolution: {integrity: sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==} - ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} @@ -2424,18 +2420,6 @@ packages: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - character-entities-html4@2.1.0: - resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} - - character-entities-legacy@3.0.0: - resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} - - character-entities@2.0.2: - resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} - - character-reference-invalid@2.0.1: - resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} - chownr@3.0.0: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} @@ -2493,9 +2477,6 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - commander@11.1.0: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} @@ -2508,10 +2489,6 @@ packages: resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} engines: {node: '>= 6'} - commander@8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} - engines: {node: '>= 12'} - commander@9.5.0: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} engines: {node: ^12.20.0 || >=14} @@ -2647,9 +2624,6 @@ packages: decimal.js-light@2.5.1: resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} - decode-named-character-reference@1.3.0: - resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} - decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -2711,10 +2685,6 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -2725,9 +2695,6 @@ packages: detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} - devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - diff@8.0.4: resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} engines: {node: '>=0.3.1'} @@ -2835,10 +2802,6 @@ packages: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} - entities@6.0.1: - resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} - engines: {node: '>=0.12'} - env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -2894,10 +2857,6 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - escape-string-regexp@5.0.0: - resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} - engines: {node: '>=12'} - eslint-plugin-react-hooks@7.1.1: resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} engines: {node: '>=18'} @@ -2947,9 +2906,6 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} - estree-util-is-identifier-name@3.0.0: - resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} - estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -3001,9 +2957,6 @@ packages: resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} - extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - fallow-type-aware@3.14.0: resolution: {integrity: sha512-D8+bNELjmoVV2dKpmHplQ1YzuheHltmZKaOVJ//hx7h8vFNyp5H3hcJV8rR26ofBU51z9K5eyA7FpgP3hebjwg==} engines: {node: '>=20'} @@ -3177,9 +3130,6 @@ packages: resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} engines: {node: '>=18'} - github-slugger@2.0.0: - resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} - glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -3234,45 +3184,6 @@ packages: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} - hast-util-from-dom@5.0.1: - resolution: {integrity: sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==} - - hast-util-from-html-isomorphic@2.0.0: - resolution: {integrity: sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==} - - hast-util-from-html@2.0.3: - resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} - - hast-util-from-parse5@8.0.3: - resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} - - hast-util-heading-rank@3.0.0: - resolution: {integrity: sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==} - - hast-util-is-element@3.0.0: - resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} - - hast-util-parse-selector@4.0.0: - resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} - - hast-util-to-html@9.0.5: - resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} - - hast-util-to-jsx-runtime@2.3.6: - resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} - - hast-util-to-string@3.0.1: - resolution: {integrity: sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==} - - hast-util-to-text@4.0.2: - resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} - - hast-util-whitespace@3.0.0: - resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} - - hastscript@9.0.1: - resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} - hermes-estree@0.25.1: resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} @@ -3287,12 +3198,6 @@ packages: resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} engines: {node: '>=10'} - html-url-attributes@3.0.1: - resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} - - html-void-elements@3.0.0: - resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} @@ -3350,9 +3255,6 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - inline-style-parser@0.2.7: - resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} - input-otp@1.4.2: resolution: {integrity: sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==} peerDependencies: @@ -3371,18 +3273,9 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} - is-alphabetical@2.0.1: - resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} - - is-alphanumerical@2.0.1: - resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} - is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-decimal@2.0.1: - resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} - is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} @@ -3405,9 +3298,6 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-hexadecimal@2.0.1: - resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - is-in-ssh@1.0.0: resolution: {integrity: sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==} engines: {node: '>=20'} @@ -3550,17 +3440,12 @@ packages: jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jsonfile@5.0.0: + resolution: {integrity: sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==} + jsonfile@6.2.1: resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} - katex@0.16.47: - resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} - hasBin: true - - katex@0.18.2: - resolution: {integrity: sha512-3snve4y0SXTMequLim1FMiPvLGElySam0blN5xQZBqEY3lOJz1TnuaaiugnBZBqHzlSAGmFTYL7MCQkORuLKCA==} - hasBin: true - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -3761,9 +3646,6 @@ packages: resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} engines: {node: '>= 0.6.0'} - longest-streak@3.1.0: - resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - lowercase-keys@2.0.0: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} @@ -3783,9 +3665,6 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - markdown-table@3.0.4: - resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - matcher@3.0.0: resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} engines: {node: '>=10'} @@ -3794,57 +3673,6 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - mdast-util-find-and-replace@3.0.2: - resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} - - mdast-util-from-markdown@2.0.3: - resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} - - mdast-util-gfm-autolink-literal@2.0.1: - resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} - - mdast-util-gfm-footnote@2.1.0: - resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} - - mdast-util-gfm-strikethrough@2.0.0: - resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} - - mdast-util-gfm-table@2.0.0: - resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} - - mdast-util-gfm-task-list-item@2.0.0: - resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} - - mdast-util-gfm@3.1.0: - resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} - - mdast-util-math@3.0.0: - resolution: {integrity: sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==} - - mdast-util-mdx-expression@2.0.1: - resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} - - mdast-util-mdx-jsx@3.2.0: - resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} - - mdast-util-mdxjs-esm@2.0.1: - resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} - - mdast-util-newline-to-break@2.0.0: - resolution: {integrity: sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==} - - mdast-util-phrasing@4.1.0: - resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} - - mdast-util-to-hast@13.2.1: - resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} - - mdast-util-to-markdown@2.1.2: - resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} - - mdast-util-to-string@4.0.0: - resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - media-typer@1.1.1: resolution: {integrity: sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==} engines: {node: '>= 0.8'} @@ -3860,93 +3688,6 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - micromark-core-commonmark@2.0.3: - resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} - - micromark-extension-gfm-autolink-literal@2.1.0: - resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} - - micromark-extension-gfm-footnote@2.1.0: - resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} - - micromark-extension-gfm-strikethrough@2.1.0: - resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} - - micromark-extension-gfm-table@2.1.1: - resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} - - micromark-extension-gfm-tagfilter@2.0.0: - resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} - - micromark-extension-gfm-task-list-item@2.1.0: - resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} - - micromark-extension-gfm@3.0.0: - resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} - - micromark-extension-math@3.1.0: - resolution: {integrity: sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==} - - micromark-factory-destination@2.0.1: - resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} - - micromark-factory-label@2.0.1: - resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} - - micromark-factory-space@2.0.1: - resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} - - micromark-factory-title@2.0.1: - resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} - - micromark-factory-whitespace@2.0.1: - resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} - - micromark-util-character@2.1.1: - resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} - - micromark-util-chunked@2.0.1: - resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} - - micromark-util-classify-character@2.0.1: - resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} - - micromark-util-combine-extensions@2.0.1: - resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} - - micromark-util-decode-numeric-character-reference@2.0.2: - resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} - - micromark-util-decode-string@2.0.1: - resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} - - micromark-util-encode@2.0.1: - resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} - - micromark-util-html-tag-name@2.0.1: - resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} - - micromark-util-normalize-identifier@2.0.1: - resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} - - micromark-util-resolve-all@2.0.1: - resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} - - micromark-util-sanitize-uri@2.0.1: - resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - - micromark-util-subtokenize@2.1.0: - resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} - - micromark-util-symbol@2.0.1: - resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} - - micromark-util-types@2.0.2: - resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} - - micromark@4.0.2: - resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} - micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -4135,12 +3876,6 @@ packages: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} - oniguruma-parser@0.12.2: - resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} - - oniguruma-to-es@4.3.6: - resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} - open@11.0.0: resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==} engines: {node: '>=20'} @@ -4185,9 +3920,6 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse-entities@4.0.2: - resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} - parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} @@ -4196,9 +3928,6 @@ packages: resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} engines: {node: '>=18'} - parse5@7.3.0: - resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -4315,9 +4044,6 @@ packages: proper-lockfile@4.1.2: resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} - property-information@7.2.0: - resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} - proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -4363,12 +4089,6 @@ packages: react-is@19.2.8: resolution: {integrity: sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==} - react-markdown@10.1.0: - resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} - peerDependencies: - '@types/react': '>=18' - react: '>=18' - react-redux@9.3.0: resolution: {integrity: sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==} peerDependencies: @@ -4448,42 +4168,6 @@ packages: redux@5.0.1: resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} - regex-recursion@6.0.2: - resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} - - regex-utilities@2.3.0: - resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} - - regex@6.1.0: - resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} - - rehype-autolink-headings@7.1.0: - resolution: {integrity: sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw==} - - rehype-katex@7.0.1: - resolution: {integrity: sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==} - - rehype-slug@6.0.0: - resolution: {integrity: sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==} - - remark-breaks@4.0.0: - resolution: {integrity: sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==} - - remark-gfm@4.0.1: - resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} - - remark-math@6.0.0: - resolution: {integrity: sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==} - - remark-parse@11.0.0: - resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} - - remark-rehype@11.1.2: - resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} - - remark-stringify@11.0.0: - resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -4631,10 +4315,6 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shiki@4.4.2: - resolution: {integrity: sha512-P8F/dFhRevaw2uSdeIYlq/5SXZNY85DPtmXQ947gD1Zj2JqO5AkNvVVBar0Me9JkFx3uzVud/qOtP5ek9NEQGA==} - engines: {node: '>=20'} - side-channel-list@1.0.1: resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} @@ -4685,9 +4365,6 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} @@ -4720,9 +4397,6 @@ packages: string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - stringify-entities@4.0.4: - resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - stringify-object@5.0.0: resolution: {integrity: sha512-zaJYxz2FtcMb4f+g60KsRNFOpVMUyuJgA51Zi5Z1DOTC3S59+OQiVOzE9GZt0x72uBGWKsQIuBKeF9iusmKFsg==} engines: {node: '>=14.16'} @@ -4750,12 +4424,6 @@ packages: style-mod@4.1.3: resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} - style-to-js@1.1.21: - resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} - - style-to-object@1.0.14: - resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} - sumchecker@3.0.1: resolution: {integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==} engines: {node: '>= 8.0'} @@ -4832,12 +4500,6 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - trim-lines@3.0.1: - resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} - - trough@2.2.0: - resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - truncate-utf8-bytes@1.0.2: resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==} @@ -4910,30 +4572,6 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} - unified@11.0.5: - resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} - - unist-util-find-after@5.0.0: - resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} - - unist-util-is@6.0.1: - resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} - - unist-util-position@5.0.0: - resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} - - unist-util-remove-position@5.0.0: - resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} - - unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - - unist-util-visit-parents@6.0.2: - resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} - - unist-util-visit@5.1.0: - resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} - universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -5009,15 +4647,6 @@ packages: react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc - vfile-location@5.0.3: - resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} - - vfile-message@4.0.3: - resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} - - vfile@6.0.3: - resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - victory-vendor@37.3.6: resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} @@ -5108,9 +4737,6 @@ packages: w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} - web-namespaces@2.0.1: - resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} - webcrypto-core@1.9.2: resolution: {integrity: sha512-gsXecm82UQNlTBURJGuqOWy1Ww08S3kZUcr3aOJS02Pk0xLtkfeUAVC0u0xhgdonFme80edSJUIJyuvL/7250Q==} @@ -5236,9 +4862,6 @@ packages: use-sync-external-store: optional: true - zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - snapshots: '@babel/code-frame@7.29.7': @@ -5926,44 +5549,25 @@ snapshots: '@marijn/find-cluster-break@1.0.3': {} - '@methanium/ui@https://git.methanium.net/methanium/ui/releases/download/0.0.28/methanium-ui.tgz(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(emojibase@17.0.0)(react-dom@19.2.8(react@19.2.8))(react-is@19.2.8)(react@19.2.8)(redux@5.0.1)(supports-color@7.2.0)(typescript@6.0.3)': + '@methanium/ui@https://git.methanium.net/methanium/ui/releases/download/0.0.22/methanium-ui.tgz(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react-is@19.2.8)(react@19.2.8)(redux@5.0.1)(supports-color@7.2.0)(typescript@6.0.3)': dependencies: '@base-ui/react': 1.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@codemirror/autocomplete': 6.20.3 - '@codemirror/commands': 6.10.4 - '@codemirror/lang-markdown': 6.5.2 - '@codemirror/language': 6.12.4 - '@codemirror/state': 6.7.1 - '@codemirror/view': 6.43.8 - '@shikijs/langs': 4.4.2 '@tauri-apps/api': 2.11.1 class-variance-authority: 0.7.1 clsx: 2.1.1 cmdk: 1.1.1(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) embla-carousel-react: 8.6.0(react@19.2.8) - emojibase-data: 17.0.0(emojibase@17.0.0) - hast-util-to-jsx-runtime: 2.3.6(supports-color@7.2.0) input-otp: 1.4.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - katex: 0.18.2 lucide-react: 1.30.0(react@19.2.8) next-themes: 0.4.6(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - react-markdown: 10.1.0(@types/react@19.2.18)(react@19.2.8)(supports-color@7.2.0) react-resizable-panels: 4.12.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8) recharts: 3.10.1(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react-is@19.2.8)(react@19.2.8)(redux@5.0.1) - rehype-autolink-headings: 7.1.0 - rehype-katex: 7.0.1 - rehype-slug: 6.0.0 - remark-breaks: 4.0.0 - remark-gfm: 4.0.1(supports-color@7.2.0) - remark-math: 6.0.0(supports-color@7.2.0) shadcn: 4.16.2(supports-color@7.2.0)(typescript@6.0.3) - shiki: 4.4.2 sonner: 2.0.7(react-dom@19.2.8(react@19.2.8))(react@19.2.8) tailwind-merge: 3.6.0 tw-animate-css: 1.4.0 - unist-util-visit: 5.1.0 vaul: 1.1.2(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) transitivePeerDependencies: - '@cfworker/json-schema' @@ -5972,7 +5576,38 @@ snapshots: - '@types/react-dom' - babel-plugin-macros - date-fns - - emojibase + - react-is + - redux + - supports-color + - typescript + + '@methanium/ui@https://git.methanium.net/methanium/ui/releases/download/0.0.22/methanium-ui.tgz(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react-is@19.2.8)(react@19.2.8)(redux@5.0.1)(supports-color@7.2.0)(typescript@7.0.2)': + dependencies: + '@base-ui/react': 1.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tauri-apps/api': 2.11.1 + class-variance-authority: 0.7.1 + clsx: 2.1.1 + cmdk: 1.1.1(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + embla-carousel-react: 8.6.0(react@19.2.8) + input-otp: 1.4.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + lucide-react: 1.30.0(react@19.2.8) + next-themes: 0.4.6(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + react-resizable-panels: 4.12.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + recharts: 3.10.1(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react-is@19.2.8)(react@19.2.8)(redux@5.0.1) + shadcn: 4.16.2(supports-color@7.2.0)(typescript@7.0.2) + sonner: 2.0.7(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + tailwind-merge: 3.6.0 + tw-animate-css: 1.4.0 + vaul: 1.1.2(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + transitivePeerDependencies: + - '@cfworker/json-schema' + - '@date-fns/tz' + - '@types/react' + - '@types/react-dom' + - babel-plugin-macros + - date-fns - react-is - redux - supports-color @@ -6235,46 +5870,6 @@ snapshots: '@sec-ant/readable-stream@0.4.1': {} - '@shikijs/core@4.4.2': - dependencies: - '@shikijs/primitive': 4.4.2 - '@shikijs/types': 4.4.2 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.5 - hast-util-to-html: 9.0.5 - - '@shikijs/engine-javascript@4.4.2': - dependencies: - '@shikijs/types': 4.4.2 - '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 4.3.6 - - '@shikijs/engine-oniguruma@4.4.2': - dependencies: - '@shikijs/types': 4.4.2 - '@shikijs/vscode-textmate': 10.0.2 - - '@shikijs/langs@4.4.2': - dependencies: - '@shikijs/types': 4.4.2 - - '@shikijs/primitive@4.4.2': - dependencies: - '@shikijs/types': 4.4.2 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.5 - - '@shikijs/themes@4.4.2': - dependencies: - '@shikijs/types': 4.4.2 - - '@shikijs/types@4.4.2': - dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.5 - - '@shikijs/vscode-textmate@10.0.2': {} - '@sindresorhus/is@4.6.0': {} '@sindresorhus/merge-streams@4.0.0': {} @@ -6491,6 +6086,15 @@ snapshots: minimatch: 10.2.6 path-browserify: 1.0.1 + '@twemoji/api@17.0.3': + dependencies: + '@twemoji/parser': 17.0.2 + fs-extra: 8.1.0 + jsonfile: 5.0.0 + universalify: 0.1.2 + + '@twemoji/parser@17.0.2': {} + '@types/cacheable-request@6.0.3': dependencies: '@types/http-cache-semantics': 4.2.0 @@ -6537,34 +6141,20 @@ snapshots: '@types/esrecurse@4.3.1': {} - '@types/estree-jsx@1.0.5': - dependencies: - '@types/estree': 1.0.9 - '@types/estree@1.0.9': {} '@types/fs-extra@9.0.13': dependencies: '@types/node': 26.2.0 - '@types/hast@3.0.5': - dependencies: - '@types/unist': 3.0.3 - '@types/http-cache-semantics@4.2.0': {} '@types/json-schema@7.0.15': {} - '@types/katex@0.16.8': {} - '@types/keyv@3.1.4': dependencies: '@types/node': 26.2.0 - '@types/mdast@4.0.4': - dependencies: - '@types/unist': 3.0.3 - '@types/ms@2.1.0': {} '@types/node@24.13.3': @@ -6591,10 +6181,6 @@ snapshots: dependencies: '@types/node': 26.2.0 - '@types/unist@2.0.11': {} - - '@types/unist@3.0.3': {} - '@types/use-sync-external-store@0.0.6': {} '@types/validate-npm-package-name@4.0.2': {} @@ -6750,8 +6336,6 @@ snapshots: '@typescript/typescript-win32-x64@7.0.2': optional: true - '@ungap/structured-clone@1.3.3': {} - '@vitejs/plugin-react@6.0.5(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': dependencies: '@rolldown/pluginutils': 1.0.1 @@ -6925,8 +6509,6 @@ snapshots: aws4@1.13.2: {} - bail@2.0.2: {} - balanced-match@1.0.2: {} balanced-match@4.0.4: {} @@ -7041,8 +6623,6 @@ snapshots: caniuse-lite@1.0.30001809: {} - ccount@2.0.1: {} - chai@6.2.2: {} chalk@4.1.2: @@ -7052,14 +6632,6 @@ snapshots: chalk@5.6.2: {} - character-entities-html4@2.1.0: {} - - character-entities-legacy@3.0.0: {} - - character-entities@2.0.2: {} - - character-reference-invalid@2.0.1: {} - chownr@3.0.0: {} chromium-pickle-js@0.2.0: {} @@ -7114,16 +6686,12 @@ snapshots: dependencies: delayed-stream: 1.0.0 - comma-separated-tokens@2.0.3: {} - commander@11.1.0: {} commander@14.0.3: {} commander@5.1.0: {} - commander@8.3.0: {} - commander@9.5.0: optional: true @@ -7174,6 +6742,15 @@ snapshots: optionalDependencies: typescript: 6.0.3 + cosmiconfig@9.0.2(typescript@7.0.2): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.3.1 + parse-json: 5.2.0 + optionalDependencies: + typescript: 7.0.2 + crelt@1.0.7: {} cross-dirname@0.1.0: @@ -7239,10 +6816,6 @@ snapshots: decimal.js-light@2.5.1: {} - decode-named-character-reference@1.3.0: - dependencies: - character-entities: 2.0.2 - decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 @@ -7288,8 +6861,6 @@ snapshots: depd@2.0.0: {} - dequal@2.0.3: {} - detect-libc@2.1.2: {} detect-node-es@1.1.0: {} @@ -7297,10 +6868,6 @@ snapshots: detect-node@2.1.0: optional: true - devlop@1.1.0: - dependencies: - dequal: 2.0.3 - diff@8.0.4: {} dir-compare@4.2.0: @@ -7445,8 +7012,6 @@ snapshots: ansi-colors: 4.1.3 strip-ansi: 6.0.1 - entities@6.0.1: {} - env-paths@2.2.1: {} env-paths@3.0.0: {} @@ -7514,8 +7079,6 @@ snapshots: escape-string-regexp@4.0.0: {} - escape-string-regexp@5.0.0: {} - eslint-plugin-react-hooks@7.1.1(eslint@10.8.1(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0): dependencies: '@babel/core': 7.29.7(supports-color@7.2.0) @@ -7593,8 +7156,6 @@ snapshots: estraverse@5.3.0: {} - estree-util-is-identifier-name@3.0.0: {} - estree-walker@3.0.3: dependencies: '@types/estree': 1.0.9 @@ -7685,8 +7246,6 @@ snapshots: transitivePeerDependencies: - supports-color - extend@3.0.2: {} - fallow-type-aware@3.14.0: dependencies: typescript: 7.0.2 @@ -7879,8 +7438,6 @@ snapshots: '@sec-ant/readable-stream': 0.4.1 is-stream: 4.0.1 - github-slugger@2.0.0: {} - glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -7951,108 +7508,6 @@ snapshots: dependencies: function-bind: 1.1.2 - hast-util-from-dom@5.0.1: - dependencies: - '@types/hast': 3.0.5 - hastscript: 9.0.1 - web-namespaces: 2.0.1 - - hast-util-from-html-isomorphic@2.0.0: - dependencies: - '@types/hast': 3.0.5 - hast-util-from-dom: 5.0.1 - hast-util-from-html: 2.0.3 - unist-util-remove-position: 5.0.0 - - hast-util-from-html@2.0.3: - dependencies: - '@types/hast': 3.0.5 - devlop: 1.1.0 - hast-util-from-parse5: 8.0.3 - parse5: 7.3.0 - vfile: 6.0.3 - vfile-message: 4.0.3 - - hast-util-from-parse5@8.0.3: - dependencies: - '@types/hast': 3.0.5 - '@types/unist': 3.0.3 - devlop: 1.1.0 - hastscript: 9.0.1 - property-information: 7.2.0 - vfile: 6.0.3 - vfile-location: 5.0.3 - web-namespaces: 2.0.1 - - hast-util-heading-rank@3.0.0: - dependencies: - '@types/hast': 3.0.5 - - hast-util-is-element@3.0.0: - dependencies: - '@types/hast': 3.0.5 - - hast-util-parse-selector@4.0.0: - dependencies: - '@types/hast': 3.0.5 - - hast-util-to-html@9.0.5: - dependencies: - '@types/hast': 3.0.5 - '@types/unist': 3.0.3 - ccount: 2.0.1 - comma-separated-tokens: 2.0.3 - hast-util-whitespace: 3.0.0 - html-void-elements: 3.0.0 - mdast-util-to-hast: 13.2.1 - property-information: 7.2.0 - space-separated-tokens: 2.0.2 - stringify-entities: 4.0.4 - zwitch: 2.0.4 - - hast-util-to-jsx-runtime@2.3.6(supports-color@7.2.0): - dependencies: - '@types/estree': 1.0.9 - '@types/hast': 3.0.5 - '@types/unist': 3.0.3 - comma-separated-tokens: 2.0.3 - devlop: 1.1.0 - estree-util-is-identifier-name: 3.0.0 - hast-util-whitespace: 3.0.0 - mdast-util-mdx-expression: 2.0.1(supports-color@7.2.0) - mdast-util-mdx-jsx: 3.2.0(supports-color@7.2.0) - mdast-util-mdxjs-esm: 2.0.1(supports-color@7.2.0) - property-information: 7.2.0 - space-separated-tokens: 2.0.2 - style-to-js: 1.1.21 - unist-util-position: 5.0.0 - vfile-message: 4.0.3 - transitivePeerDependencies: - - supports-color - - hast-util-to-string@3.0.1: - dependencies: - '@types/hast': 3.0.5 - - hast-util-to-text@4.0.2: - dependencies: - '@types/hast': 3.0.5 - '@types/unist': 3.0.3 - hast-util-is-element: 3.0.0 - unist-util-find-after: 5.0.0 - - hast-util-whitespace@3.0.0: - dependencies: - '@types/hast': 3.0.5 - - hastscript@9.0.1: - dependencies: - '@types/hast': 3.0.5 - comma-separated-tokens: 2.0.3 - hast-util-parse-selector: 4.0.0 - property-information: 7.2.0 - space-separated-tokens: 2.0.2 - hermes-estree@0.25.1: {} hermes-parser@0.25.1: @@ -8065,10 +7520,6 @@ snapshots: dependencies: lru-cache: 6.0.0 - html-url-attributes@3.0.1: {} - - html-void-elements@3.0.0: {} - http-cache-semantics@4.2.0: {} http-errors@2.0.1: @@ -8126,8 +7577,6 @@ snapshots: inherits@2.0.4: {} - inline-style-parser@0.2.7: {} - input-otp@1.4.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: react: 19.2.8 @@ -8139,17 +7588,8 @@ snapshots: ipaddr.js@1.9.1: {} - is-alphabetical@2.0.1: {} - - is-alphanumerical@2.0.1: - dependencies: - is-alphabetical: 2.0.1 - is-decimal: 2.0.1 - is-arrayish@0.2.1: {} - is-decimal@2.0.1: {} - is-docker@2.2.1: {} is-docker@3.0.0: {} @@ -8162,8 +7602,6 @@ snapshots: dependencies: is-extglob: 2.1.1 - is-hexadecimal@2.0.1: {} - is-in-ssh@1.0.0: {} is-inside-container@1.0.0: @@ -8255,20 +7693,18 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonfile@5.0.0: + dependencies: + universalify: 0.1.2 + optionalDependencies: + graceful-fs: 4.2.11 + jsonfile@6.2.1: dependencies: universalify: 2.0.1 optionalDependencies: graceful-fs: 4.2.11 - katex@0.16.47: - dependencies: - commander: 8.3.0 - - katex@0.18.2: - dependencies: - commander: 8.3.0 - keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -8419,8 +7855,6 @@ snapshots: loglevel@1.9.2: {} - longest-streak@3.1.0: {} - lowercase-keys@2.0.0: {} lru-cache@5.1.1: @@ -8439,8 +7873,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - markdown-table@3.0.4: {} - matcher@3.0.0: dependencies: escape-string-regexp: 4.0.0 @@ -8448,176 +7880,6 @@ snapshots: math-intrinsics@1.1.0: {} - mdast-util-find-and-replace@3.0.2: - dependencies: - '@types/mdast': 4.0.4 - escape-string-regexp: 5.0.0 - unist-util-is: 6.0.1 - unist-util-visit-parents: 6.0.2 - - mdast-util-from-markdown@2.0.3(supports-color@7.2.0): - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - decode-named-character-reference: 1.3.0 - devlop: 1.1.0 - mdast-util-to-string: 4.0.0 - micromark: 4.0.2(supports-color@7.2.0) - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-decode-string: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - unist-util-stringify-position: 4.0.0 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-autolink-literal@2.0.1: - dependencies: - '@types/mdast': 4.0.4 - ccount: 2.0.1 - devlop: 1.1.0 - mdast-util-find-and-replace: 3.0.2 - micromark-util-character: 2.1.1 - - mdast-util-gfm-footnote@2.1.0(supports-color@7.2.0): - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3(supports-color@7.2.0) - mdast-util-to-markdown: 2.1.2 - micromark-util-normalize-identifier: 2.0.1 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-strikethrough@2.0.0(supports-color@7.2.0): - dependencies: - '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.3(supports-color@7.2.0) - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-table@2.0.0(supports-color@7.2.0): - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - markdown-table: 3.0.4 - mdast-util-from-markdown: 2.0.3(supports-color@7.2.0) - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-task-list-item@2.0.0(supports-color@7.2.0): - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3(supports-color@7.2.0) - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm@3.1.0(supports-color@7.2.0): - dependencies: - mdast-util-from-markdown: 2.0.3(supports-color@7.2.0) - mdast-util-gfm-autolink-literal: 2.0.1 - mdast-util-gfm-footnote: 2.1.0(supports-color@7.2.0) - mdast-util-gfm-strikethrough: 2.0.0(supports-color@7.2.0) - mdast-util-gfm-table: 2.0.0(supports-color@7.2.0) - mdast-util-gfm-task-list-item: 2.0.0(supports-color@7.2.0) - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-math@3.0.0(supports-color@7.2.0): - dependencies: - '@types/hast': 3.0.5 - '@types/mdast': 4.0.4 - devlop: 1.1.0 - longest-streak: 3.1.0 - mdast-util-from-markdown: 2.0.3(supports-color@7.2.0) - mdast-util-to-markdown: 2.1.2 - unist-util-remove-position: 5.0.0 - transitivePeerDependencies: - - supports-color - - mdast-util-mdx-expression@2.0.1(supports-color@7.2.0): - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.5 - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3(supports-color@7.2.0) - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-mdx-jsx@3.2.0(supports-color@7.2.0): - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.5 - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - ccount: 2.0.1 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3(supports-color@7.2.0) - mdast-util-to-markdown: 2.1.2 - parse-entities: 4.0.2 - stringify-entities: 4.0.4 - unist-util-stringify-position: 4.0.0 - vfile-message: 4.0.3 - transitivePeerDependencies: - - supports-color - - mdast-util-mdxjs-esm@2.0.1(supports-color@7.2.0): - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.5 - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3(supports-color@7.2.0) - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-newline-to-break@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-find-and-replace: 3.0.2 - - mdast-util-phrasing@4.1.0: - dependencies: - '@types/mdast': 4.0.4 - unist-util-is: 6.0.1 - - mdast-util-to-hast@13.2.1: - dependencies: - '@types/hast': 3.0.5 - '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.3.3 - devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.1 - trim-lines: 3.0.1 - unist-util-position: 5.0.0 - unist-util-visit: 5.1.0 - vfile: 6.0.3 - - mdast-util-to-markdown@2.1.2: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - longest-streak: 3.1.0 - mdast-util-phrasing: 4.1.0 - mdast-util-to-string: 4.0.0 - micromark-util-classify-character: 2.0.1 - micromark-util-decode-string: 2.0.1 - unist-util-visit: 5.1.0 - zwitch: 2.0.4 - - mdast-util-to-string@4.0.0: - dependencies: - '@types/mdast': 4.0.4 - media-typer@1.1.1: {} merge-descriptors@2.0.0: {} @@ -8626,207 +7888,6 @@ snapshots: merge2@1.4.1: {} - micromark-core-commonmark@2.0.3: - dependencies: - decode-named-character-reference: 1.3.0 - devlop: 1.1.0 - micromark-factory-destination: 2.0.1 - micromark-factory-label: 2.0.1 - micromark-factory-space: 2.0.1 - micromark-factory-title: 2.0.1 - micromark-factory-whitespace: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-chunked: 2.0.1 - micromark-util-classify-character: 2.0.1 - micromark-util-html-tag-name: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-subtokenize: 2.1.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-autolink-literal@2.1.0: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-sanitize-uri: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-footnote@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-core-commonmark: 2.0.3 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-sanitize-uri: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-strikethrough@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.1 - micromark-util-classify-character: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-table@2.1.1: - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-tagfilter@2.0.0: - dependencies: - micromark-util-types: 2.0.2 - - micromark-extension-gfm-task-list-item@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm@3.0.0: - dependencies: - micromark-extension-gfm-autolink-literal: 2.1.0 - micromark-extension-gfm-footnote: 2.1.0 - micromark-extension-gfm-strikethrough: 2.1.0 - micromark-extension-gfm-table: 2.1.1 - micromark-extension-gfm-tagfilter: 2.0.0 - micromark-extension-gfm-task-list-item: 2.1.0 - micromark-util-combine-extensions: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-math@3.1.0: - dependencies: - '@types/katex': 0.16.8 - devlop: 1.1.0 - katex: 0.16.47 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-destination@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-label@2.0.1: - dependencies: - devlop: 1.1.0 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-space@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-types: 2.0.2 - - micromark-factory-title@2.0.1: - dependencies: - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-whitespace@2.0.1: - dependencies: - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-character@2.1.1: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-chunked@2.0.1: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-classify-character@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-combine-extensions@2.0.1: - dependencies: - micromark-util-chunked: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-decode-numeric-character-reference@2.0.2: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-decode-string@2.0.1: - dependencies: - decode-named-character-reference: 1.3.0 - micromark-util-character: 2.1.1 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-symbol: 2.0.1 - - micromark-util-encode@2.0.1: {} - - micromark-util-html-tag-name@2.0.1: {} - - micromark-util-normalize-identifier@2.0.1: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-resolve-all@2.0.1: - dependencies: - micromark-util-types: 2.0.2 - - micromark-util-sanitize-uri@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-encode: 2.0.1 - micromark-util-symbol: 2.0.1 - - micromark-util-subtokenize@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-symbol@2.0.1: {} - - micromark-util-types@2.0.2: {} - - micromark@4.0.2(supports-color@7.2.0): - dependencies: - '@types/debug': 4.1.13 - debug: 4.4.3(supports-color@7.2.0) - decode-named-character-reference: 1.3.0 - devlop: 1.1.0 - micromark-core-commonmark: 2.0.3 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-chunked: 2.0.1 - micromark-util-combine-extensions: 2.0.1 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-encode: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-sanitize-uri: 2.0.1 - micromark-util-subtokenize: 2.1.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - transitivePeerDependencies: - - supports-color - micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -8982,14 +8043,6 @@ snapshots: dependencies: mimic-function: 5.0.1 - oniguruma-parser@0.12.2: {} - - oniguruma-to-es@4.3.6: - dependencies: - oniguruma-parser: 0.12.2 - regex: 6.1.0 - regex-recursion: 6.0.2 - open@11.0.0: dependencies: default-browser: 5.5.0 @@ -9050,16 +8103,6 @@ snapshots: dependencies: callsites: 3.1.0 - parse-entities@4.0.2: - dependencies: - '@types/unist': 2.0.11 - character-entities-legacy: 3.0.0 - character-reference-invalid: 2.0.1 - decode-named-character-reference: 1.3.0 - is-alphanumerical: 2.0.1 - is-decimal: 2.0.1 - is-hexadecimal: 2.0.1 - parse-json@5.2.0: dependencies: '@babel/code-frame': 7.29.7 @@ -9069,10 +8112,6 @@ snapshots: parse-ms@4.0.0: {} - parse5@7.3.0: - dependencies: - entities: 6.0.1 - parseurl@1.3.3: {} path-browserify@1.0.1: {} @@ -9168,8 +8207,6 @@ snapshots: retry: 0.12.0 signal-exit: 3.0.7 - property-information@7.2.0: {} - proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -9213,24 +8250,6 @@ snapshots: react-is@19.2.8: {} - react-markdown@10.1.0(@types/react@19.2.18)(react@19.2.8)(supports-color@7.2.0): - dependencies: - '@types/hast': 3.0.5 - '@types/mdast': 4.0.4 - '@types/react': 19.2.18 - devlop: 1.1.0 - hast-util-to-jsx-runtime: 2.3.6(supports-color@7.2.0) - html-url-attributes: 3.0.1 - mdast-util-to-hast: 13.2.1 - react: 19.2.8 - remark-parse: 11.0.0(supports-color@7.2.0) - remark-rehype: 11.1.2 - unified: 11.0.5 - unist-util-visit: 5.1.0 - vfile: 6.0.3 - transitivePeerDependencies: - - supports-color - react-redux@9.3.0(@types/react@19.2.18)(react@19.2.8)(redux@5.0.1): dependencies: '@types/use-sync-external-store': 0.0.6 @@ -9324,92 +8343,6 @@ snapshots: redux@5.0.1: {} - regex-recursion@6.0.2: - dependencies: - regex-utilities: 2.3.0 - - regex-utilities@2.3.0: {} - - regex@6.1.0: - dependencies: - regex-utilities: 2.3.0 - - rehype-autolink-headings@7.1.0: - dependencies: - '@types/hast': 3.0.5 - '@ungap/structured-clone': 1.3.3 - hast-util-heading-rank: 3.0.0 - hast-util-is-element: 3.0.0 - unified: 11.0.5 - unist-util-visit: 5.1.0 - - rehype-katex@7.0.1: - dependencies: - '@types/hast': 3.0.5 - '@types/katex': 0.16.8 - hast-util-from-html-isomorphic: 2.0.0 - hast-util-to-text: 4.0.2 - katex: 0.16.47 - unist-util-visit-parents: 6.0.2 - vfile: 6.0.3 - - rehype-slug@6.0.0: - dependencies: - '@types/hast': 3.0.5 - github-slugger: 2.0.0 - hast-util-heading-rank: 3.0.0 - hast-util-to-string: 3.0.1 - unist-util-visit: 5.1.0 - - remark-breaks@4.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-newline-to-break: 2.0.0 - unified: 11.0.5 - - remark-gfm@4.0.1(supports-color@7.2.0): - dependencies: - '@types/mdast': 4.0.4 - mdast-util-gfm: 3.1.0(supports-color@7.2.0) - micromark-extension-gfm: 3.0.0 - remark-parse: 11.0.0(supports-color@7.2.0) - remark-stringify: 11.0.0 - unified: 11.0.5 - transitivePeerDependencies: - - supports-color - - remark-math@6.0.0(supports-color@7.2.0): - dependencies: - '@types/mdast': 4.0.4 - mdast-util-math: 3.0.0(supports-color@7.2.0) - micromark-extension-math: 3.1.0 - unified: 11.0.5 - transitivePeerDependencies: - - supports-color - - remark-parse@11.0.0(supports-color@7.2.0): - dependencies: - '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.3(supports-color@7.2.0) - micromark-util-types: 2.0.2 - unified: 11.0.5 - transitivePeerDependencies: - - supports-color - - remark-rehype@11.1.2: - dependencies: - '@types/hast': 3.0.5 - '@types/mdast': 4.0.4 - mdast-util-to-hast: 13.2.1 - unified: 11.0.5 - vfile: 6.0.3 - - remark-stringify@11.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-to-markdown: 2.1.2 - unified: 11.0.5 - require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -9596,23 +8529,52 @@ snapshots: - supports-color - typescript + shadcn@4.16.2(supports-color@7.2.0)(typescript@7.0.2): + dependencies: + '@babel/core': 7.29.7(supports-color@7.2.0) + '@babel/parser': 7.29.8 + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) + '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) + '@dotenvx/dotenvx': 1.75.1 + '@modelcontextprotocol/sdk': 1.30.0(supports-color@7.2.0)(zod@3.25.76) + '@types/validate-npm-package-name': 4.0.2 + browserslist: 4.28.7 + commander: 14.0.3 + cosmiconfig: 9.0.2(typescript@7.0.2) + dedent: 1.7.2 + deepmerge: 4.3.1 + diff: 8.0.4 + execa: 9.6.1 + fast-glob: 3.3.3 + fs-extra: 11.4.0 + fuzzysort: 3.1.0 + kleur: 4.1.5 + open: 11.0.0 + ora: 8.2.0 + postcss: 8.5.26 + postcss-selector-parser: 7.1.5 + prompts: 2.4.2 + recast: 0.23.19 + stringify-object: 5.0.0 + tailwind-merge: 3.6.0 + ts-morph: 26.0.0 + tsconfig-paths: 4.2.0 + undici: 7.29.0 + validate-npm-package-name: 7.0.2 + zod: 3.25.76 + zod-to-json-schema: 3.25.2(zod@3.25.76) + transitivePeerDependencies: + - '@cfworker/json-schema' + - babel-plugin-macros + - supports-color + - typescript + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 shebang-regex@3.0.0: {} - shiki@4.4.2: - dependencies: - '@shikijs/core': 4.4.2 - '@shikijs/engine-javascript': 4.4.2 - '@shikijs/engine-oniguruma': 4.4.2 - '@shikijs/langs': 4.4.2 - '@shikijs/themes': 4.4.2 - '@shikijs/types': 4.4.2 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.5 - side-channel-list@1.0.1: dependencies: es-errors: 1.3.0 @@ -9667,8 +8629,6 @@ snapshots: source-map@0.6.1: {} - space-separated-tokens@2.0.2: {} - sprintf-js@1.1.3: optional: true @@ -9698,11 +8658,6 @@ snapshots: dependencies: safe-buffer: 5.1.2 - stringify-entities@4.0.4: - dependencies: - character-entities-html4: 2.1.0 - character-entities-legacy: 3.0.0 - stringify-object@5.0.0: dependencies: get-own-enumerable-keys: 1.0.0 @@ -9725,14 +8680,6 @@ snapshots: style-mod@4.1.3: {} - style-to-js@1.1.21: - dependencies: - style-to-object: 1.0.14 - - style-to-object@1.0.14: - dependencies: - inline-style-parser: 0.2.7 - sumchecker@3.0.1(supports-color@7.2.0): dependencies: debug: 4.4.3(supports-color@7.2.0) @@ -9802,10 +8749,6 @@ snapshots: toidentifier@1.0.1: {} - trim-lines@3.0.1: {} - - trough@2.2.0: {} - truncate-utf8-bytes@1.0.2: dependencies: utf8-byte-length: 1.0.5 @@ -9893,49 +8836,6 @@ snapshots: unicorn-magic@0.3.0: {} - unified@11.0.5: - dependencies: - '@types/unist': 3.0.3 - bail: 2.0.2 - devlop: 1.1.0 - extend: 3.0.2 - is-plain-obj: 4.1.0 - trough: 2.2.0 - vfile: 6.0.3 - - unist-util-find-after@5.0.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - - unist-util-is@6.0.1: - dependencies: - '@types/unist': 3.0.3 - - unist-util-position@5.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-remove-position@5.0.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-visit: 5.1.0 - - unist-util-stringify-position@4.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-visit-parents@6.0.2: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - - unist-util-visit@5.1.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - unist-util-visit-parents: 6.0.2 - universalify@0.1.2: {} universalify@2.0.1: {} @@ -10001,21 +8901,6 @@ snapshots: - '@types/react' - '@types/react-dom' - vfile-location@5.0.3: - dependencies: - '@types/unist': 3.0.3 - vfile: 6.0.3 - - vfile-message@4.0.3: - dependencies: - '@types/unist': 3.0.3 - unist-util-stringify-position: 4.0.0 - - vfile@6.0.3: - dependencies: - '@types/unist': 3.0.3 - vfile-message: 4.0.3 - victory-vendor@37.3.6: dependencies: '@types/d3-array': 3.2.2 @@ -10076,8 +8961,6 @@ snapshots: w3c-keyname@2.2.8: {} - web-namespaces@2.0.1: {} - webcrypto-core@1.9.2: dependencies: '@peculiar/asn1-schema': 2.8.0 @@ -10176,5 +9059,3 @@ snapshots: immer: 11.1.16 react: 19.2.8 use-sync-external-store: 1.6.0(react@19.2.8) - - zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 2bcade5..87d0c28 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,5 +6,5 @@ allowBuilds: electron-winstaller: true esbuild: true overrides: - "@methanium/ui": "https://git.methanium.net/methanium/ui/releases/download/0.0.28/methanium-ui.tgz" + "@methanium/ui": "https://git.methanium.net/methanium/ui/releases/download/0.0.22/methanium-ui.tgz" mtp: "https://git.methanium.net/methanium/mtp/releases/download/0.2.0-dev-a692bed/mtp-0.2.0.tgz"