diff --git a/package.json b/package.json index 0dae08f282..47c8f2c8a8 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "validate:snippets": "node scripts/validate-snippets.js" }, "dependencies": { + "@base-ui-components/react": "1.0.0-beta.4", "@codemirror/autocomplete": "^6.18.6", "@codemirror/commands": "^6.3.3", "@codemirror/language": "^6.10.0", @@ -37,7 +38,6 @@ "@lezer/highlight": "^1.2.1", "@next/bundle-analyzer": "^15.4.5", "@plaiceholder/next": "^3.0.0", - "@radix-ui/react-radio-group": "^1.2.2", "@sparticuz/chromium": "^138.0.2", "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/nesting": "0.0.0-insiders.565cd3e", @@ -55,7 +55,6 @@ "iframe-resizer-react": "^1.1.1", "leaflet": "^1.9.4", "lucide-react": "^0.469.0", - "markdown-to-jsx": "^7.7.2", "motion": "^12.11.0", "next": "^14.2.32", "next-query-params": "^5.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fd6c5eb678..04e55ba74d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,6 +19,9 @@ importers: .: dependencies: + '@base-ui-components/react': + specifier: 1.0.0-beta.4 + version: 1.0.0-beta.4(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@codemirror/autocomplete': specifier: ^6.18.6 version: 6.18.7 @@ -58,9 +61,6 @@ importers: '@plaiceholder/next': specifier: ^3.0.0 version: 3.0.0(next@14.2.32(@babel/core@7.28.3)(@playwright/test@1.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(plaiceholder@3.0.0(sharp@0.34.4))(sharp@0.34.4) - '@radix-ui/react-radio-group': - specifier: ^1.2.2 - version: 1.3.8(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@sparticuz/chromium': specifier: ^138.0.2 version: 138.0.2 @@ -112,9 +112,6 @@ importers: lucide-react: specifier: ^0.469.0 version: 0.469.0(react@18.3.1) - markdown-to-jsx: - specifier: ^7.7.2 - version: 7.7.15(react@18.3.1) motion: specifier: ^12.11.0 version: 12.23.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -886,6 +883,27 @@ packages: resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} engines: {node: '>=6.9.0'} + '@base-ui-components/react@1.0.0-beta.4': + resolution: {integrity: sha512-sPYKj26gbFHD2ZsrMYqQshXnMuomBodzPn+d0dDxWieTj232XCQ9QGt9fU9l5SDGC9hi8s24lDlg9FXPSI7T8A==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + + '@base-ui-components/utils@0.1.2': + resolution: {integrity: sha512-aEitDGpMsYO2qnSpYOwZNykn9Rzn2ioyEVk2fyDRH7t+TIHVKpp9CeV7SPTq43M9mMSDxQ+7UeZJVkrj2dCVIQ==} + peerDependencies: + '@types/react': ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + '@braintree/sanitize-url@7.1.1': resolution: {integrity: sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==} @@ -1738,173 +1756,6 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@radix-ui/primitive@1.1.3': - resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} - - '@radix-ui/react-collection@1.1.7': - resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-compose-refs@1.1.2': - resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-context@1.1.2': - resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-direction@1.1.1': - resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-id@1.1.1': - resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-presence@1.1.5': - resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-primitive@2.1.3': - resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-radio-group@1.3.8': - resolution: {integrity: sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-roving-focus@1.1.11': - resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-slot@1.2.3': - resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-callback-ref@1.1.1': - resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-controllable-state@1.2.2': - resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-effect-event@0.0.2': - resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-layout-effect@1.1.1': - resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-previous@1.1.1': - resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-size@1.1.1': - resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@react-aria/focus@3.21.1': resolution: {integrity: sha512-hmH1IhHlcQ2lSIxmki1biWzMbGgnhdxJUM0MFfzc71Rv6YAzhlx4kX3GYn4VNcjCeb6cdPv4RZ5vunV4kgMZYQ==} peerDependencies: @@ -4179,15 +4030,6 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - markdown-to-jsx@7.7.15: - resolution: {integrity: sha512-U5dw5oRajrPTE2oJQWAbLK8RgbCDJ264AjW3fGABq+/rZjQ0E/WGVCLKAHvpKHQFUwoWoK8ZZWVPNLR/biYMhg==} - engines: {node: '>= 10'} - peerDependencies: - react: '>= 0.14.0' - peerDependenciesMeta: - react: - optional: true - marked@16.2.0: resolution: {integrity: sha512-LbbTuye+0dWRz2TS9KJ7wsnD4KAtpj0MVkWc90XvBa6AslXsT0hTBVH5k32pcSyHH1fst9XEFJunXHktVy0zlg==} engines: {node: '>= 20'} @@ -5193,6 +5035,9 @@ packages: remove-trailing-separator@1.1.0: resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -6847,6 +6692,31 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@base-ui-components/react@1.0.0-beta.4(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@base-ui-components/utils': 0.1.2(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/react-dom': 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/utils': 0.2.10 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + reselect: 5.1.1 + tabbable: 6.2.0 + use-sync-external-store: 1.5.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.26 + + '@base-ui-components/utils@0.1.2(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@floating-ui/utils': 0.2.10 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + reselect: 5.1.1 + use-sync-external-store: 1.5.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.26 + '@braintree/sanitize-url@7.1.1': {} '@chevrotain/cst-dts-gen@11.0.3': @@ -7718,141 +7588,6 @@ snapshots: '@polka/url@1.0.0-next.29': {} - '@radix-ui/primitive@1.1.3': {} - - '@radix-ui/react-collection@1.1.7(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.26)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.26)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.26 - - '@radix-ui/react-compose-refs@1.1.2(@types/react@18.3.26)(react@18.3.1)': - dependencies: - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.26 - - '@radix-ui/react-context@1.1.2(@types/react@18.3.26)(react@18.3.1)': - dependencies: - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.26 - - '@radix-ui/react-direction@1.1.1(@types/react@18.3.26)(react@18.3.1)': - dependencies: - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.26 - - '@radix-ui/react-id@1.1.1(@types/react@18.3.26)(react@18.3.1)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.26)(react@18.3.1) - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.26 - - '@radix-ui/react-presence@1.1.5(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.26)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.26 - - '@radix-ui/react-primitive@2.1.3(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.26)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.26 - - '@radix-ui/react-radio-group@1.3.8(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.26)(react@18.3.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.26)(react@18.3.1) - '@radix-ui/react-presence': 1.1.5(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-roving-focus': 1.1.11(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.26)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.26)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.26)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.26 - - '@radix-ui/react-roving-focus@1.1.11(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@18.3.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.26)(react@18.3.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.26)(react@18.3.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.26)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.26)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.26)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.26 - - '@radix-ui/react-slot@1.2.3(@types/react@18.3.26)(react@18.3.1)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@18.3.1) - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.26 - - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@18.3.26)(react@18.3.1)': - dependencies: - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.26 - - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@18.3.26)(react@18.3.1)': - dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.3.26)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.26)(react@18.3.1) - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.26 - - '@radix-ui/react-use-effect-event@0.0.2(@types/react@18.3.26)(react@18.3.1)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.26)(react@18.3.1) - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.26 - - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.3.26)(react@18.3.1)': - dependencies: - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.26 - - '@radix-ui/react-use-previous@1.1.1(@types/react@18.3.26)(react@18.3.1)': - dependencies: - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.26 - - '@radix-ui/react-use-size@1.1.1(@types/react@18.3.26)(react@18.3.1)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.26)(react@18.3.1) - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.26 - '@react-aria/focus@3.21.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@react-aria/interactions': 3.25.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -10514,10 +10249,6 @@ snapshots: markdown-table@3.0.4: {} - markdown-to-jsx@7.7.15(react@18.3.1): - optionalDependencies: - react: 18.3.1 - marked@16.2.0: {} math-intrinsics@1.1.0: {} @@ -11934,6 +11665,8 @@ snapshots: remove-trailing-separator@1.1.0: {} + reselect@5.1.1: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} diff --git a/prettier.config.mjs b/prettier.config.mjs index f6439a7795..fe9e149744 100644 --- a/prettier.config.mjs +++ b/prettier.config.mjs @@ -26,7 +26,10 @@ export default { }, }, ], - plugins: ["prettier-plugin-pkg", "prettier-plugin-tailwindcss"], - // We need this to ensure classes format the same across CI and editors. + // We need the absolute paths here to ensure classes format the same across CI and editors. + plugins: [ + import.meta.resolve("prettier-plugin-pkg").replace("file://", ""), + import.meta.resolve("prettier-plugin-tailwindcss").replace("file://", ""), + ], tailwindConfig: resolve(__dirname, "./tailwind.config.ts"), } diff --git a/scripts/get-github-info/get-github-info.ts b/scripts/get-github-info/get-github-info.ts index b7236fb8dd..1dcfeed059 100644 --- a/scripts/get-github-info/get-github-info.ts +++ b/scripts/get-github-info/get-github-info.ts @@ -40,6 +40,7 @@ async function main() { const content = await fs.readFile(filePath, "utf8") const { data } = grayMatter(content) if (data.github) { + // TODO: This needs to be pooled to make the builds faster. const stats = await getGitHubStats(data.github) if (stats) { newState.set(data.github, stats) diff --git a/scripts/get-github-info/github-stats.json b/scripts/get-github-info/github-stats.json index 52a0f1699f..453b431d14 100644 --- a/scripts/get-github-info/github-stats.json +++ b/scripts/get-github-info/github-stats.json @@ -1,18 +1,18 @@ { "altair-graphql/altair": { "hasCommitsInLast3Months": false, - "stars": 5339, + "stars": 5344, "formattedStars": "5k", "license": "MIT License", - "lastRelease": "2025-10-04T13:56:59Z", - "formattedLastRelease": "1 week ago" + "lastRelease": "2025-10-18T09:02:01Z", + "formattedLastRelease": "2 days ago" }, "apache/apisix": { "hasCommitsInLast3Months": false, - "stars": 15719, + "stars": 15738, "formattedStars": "16k", "license": "Apache License 2.0", - "lastRelease": "2025-10-10T05:53:01Z", + "lastRelease": "2025-10-16T07:54:57Z", "formattedLastRelease": "4 days ago" }, "apollographql/apollo-studio-community": { @@ -28,12 +28,12 @@ "stars": 5615, "formattedStars": "6k", "license": "MIT License", - "lastRelease": "2025-10-14T06:34:22Z", - "formattedLastRelease": "15 hours ago" + "lastRelease": "2025-10-20T15:01:39Z", + "formattedLastRelease": "8 hours ago" }, "dgraph-io/dgraph": { "hasCommitsInLast3Months": false, - "stars": 21294, + "stars": 21301, "formattedStars": "21k", "license": "Apache License 2.0", "lastRelease": "2025-10-07T20:50:36Z", @@ -57,31 +57,31 @@ }, "hasura/graphql-engine": { "hasCommitsInLast3Months": false, - "stars": 31743, + "stars": 31754, "formattedStars": "32k", "license": "Apache License 2.0", "lastRelease": "2025-10-14T15:20:38Z", - "formattedLastRelease": "6 hours ago" + "formattedLastRelease": "6 days ago" }, "graphql-hive/platform": { "hasCommitsInLast3Months": false, - "stars": 461, - "formattedStars": "461", + "stars": 463, + "formattedStars": "463", "license": "MIT License", - "lastRelease": "2025-10-13T13:13:34Z", - "formattedLastRelease": "1 day ago" + "lastRelease": "2025-10-16T10:40:33Z", + "formattedLastRelease": "4 days ago" }, "Kong/insomnia": { "hasCommitsInLast3Months": false, - "stars": 37318, + "stars": 37365, "formattedStars": "37k", "license": "Apache License 2.0", "lastRelease": "2025-09-18T07:30:33Z", - "formattedLastRelease": "3 weeks ago" + "formattedLastRelease": "1 month ago" }, "postmanlabs/postman-app-support": { "hasCommitsInLast3Months": false, - "stars": 5957, + "stars": 5959, "formattedStars": "6k", "license": "Unknown", "lastRelease": "", @@ -97,7 +97,7 @@ }, "TykTechnologies/tyk": { "hasCommitsInLast3Months": false, - "stars": 10413, + "stars": 10425, "formattedStars": "10k", "license": "Other", "lastRelease": "2025-08-07T14:37:50Z", @@ -109,15 +109,15 @@ "formattedStars": "114", "license": "Apache License 2.0", "lastRelease": "2023-10-16T07:50:50Z", - "formattedLastRelease": "1 year ago" + "formattedLastRelease": "2 years ago" }, "webiny/webiny-js": { "hasCommitsInLast3Months": false, - "stars": 7862, + "stars": 7864, "formattedStars": "8k", "license": "Other", "lastRelease": "2025-09-16T08:29:00Z", - "formattedLastRelease": "4 weeks ago" + "formattedLastRelease": "1 month ago" }, "ballerina-platform/module-ballerina-graphql": { "hasCommitsInLast3Months": false, @@ -135,6 +135,22 @@ "lastRelease": "2017-10-16T21:47:42Z", "formattedLastRelease": "8 years ago" }, + "burner/graphqld": { + "hasCommitsInLast3Months": false, + "stars": 35, + "formattedStars": "35", + "license": "GNU Lesser General Public License v3.0", + "lastRelease": "2024-05-14T13:42:29Z", + "formattedLastRelease": "1 year ago" + }, + "oliyh/re-graph": { + "hasCommitsInLast3Months": false, + "stars": 466, + "formattedStars": "466", + "license": "Unknown", + "lastRelease": "2022-07-20T09:24:02Z", + "formattedLastRelease": "3 years ago" + }, "alumbra/alumbra": { "hasCommitsInLast3Months": false, "stars": 148, @@ -159,14 +175,6 @@ "lastRelease": "", "formattedLastRelease": "" }, - "oliyh/re-graph": { - "hasCommitsInLast3Months": false, - "stars": 466, - "formattedStars": "466", - "license": "Unknown", - "lastRelease": "2022-07-20T09:24:02Z", - "formattedLastRelease": "3 years ago" - }, "graphql-dotnet/graphql-client": { "hasCommitsInLast3Months": false, "stars": 644, @@ -201,51 +209,11 @@ }, "byme8/ZeroQL": { "hasCommitsInLast3Months": false, - "stars": 311, - "formattedStars": "311", + "stars": 312, + "formattedStars": "312", "license": "MIT License", "lastRelease": "2025-10-14T11:58:44Z", - "formattedLastRelease": "10 hours ago" - }, - "EntityGraphQL/EntityGraphQL": { - "hasCommitsInLast3Months": false, - "stars": 446, - "formattedStars": "446", - "license": "MIT License", - "lastRelease": "2025-09-16T00:35:14Z", - "formattedLastRelease": "4 weeks ago" - }, - "graphql-dotnet/graphql-dotnet": { - "hasCommitsInLast3Months": false, - "stars": 5969, - "formattedStars": "6k", - "license": "MIT License", - "lastRelease": "2025-09-21T17:57:29Z", - "formattedLastRelease": "3 weeks ago" - }, - "chkimes/graphql-net": { - "hasCommitsInLast3Months": false, - "stars": 888, - "formattedStars": "1k", - "license": "MIT License", - "lastRelease": "", - "formattedLastRelease": "" - }, - "rivantsov/ngraphql": { - "hasCommitsInLast3Months": false, - "stars": 45, - "formattedStars": "45", - "license": "MIT License", - "lastRelease": "", - "formattedLastRelease": "" - }, - "burner/graphqld": { - "hasCommitsInLast3Months": false, - "stars": 35, - "formattedStars": "35", - "license": "GNU Lesser General Public License v3.0", - "lastRelease": "2024-05-14T13:42:29Z", - "formattedLastRelease": "1 year ago" + "formattedLastRelease": "6 days ago" }, "annkissam/common_graphql_client": { "hasCommitsInLast3Months": false, @@ -265,7 +233,7 @@ }, "absinthe-graphql/absinthe": { "hasCommitsInLast3Months": false, - "stars": 4365, + "stars": 4367, "formattedStars": "4k", "license": "Other", "lastRelease": "2025-06-09T16:38:08Z", @@ -279,6 +247,38 @@ "lastRelease": "2016-09-09T04:49:46Z", "formattedLastRelease": "9 years ago" }, + "EntityGraphQL/EntityGraphQL": { + "hasCommitsInLast3Months": false, + "stars": 446, + "formattedStars": "446", + "license": "MIT License", + "lastRelease": "2025-09-16T00:35:14Z", + "formattedLastRelease": "1 month ago" + }, + "graphql-dotnet/graphql-dotnet": { + "hasCommitsInLast3Months": false, + "stars": 5969, + "formattedStars": "6k", + "license": "MIT License", + "lastRelease": "2025-09-21T17:57:29Z", + "formattedLastRelease": "4 weeks ago" + }, + "chkimes/graphql-net": { + "hasCommitsInLast3Months": false, + "stars": 888, + "formattedStars": "1k", + "license": "MIT License", + "lastRelease": "", + "formattedLastRelease": "" + }, + "rivantsov/ngraphql": { + "hasCommitsInLast3Months": false, + "stars": 46, + "formattedStars": "46", + "license": "MIT License", + "lastRelease": "", + "formattedLastRelease": "" + }, "dillonkearns/elm-graphql": { "hasCommitsInLast3Months": false, "stars": 782, @@ -297,7 +297,7 @@ }, "gql-dart/ferry": { "hasCommitsInLast3Months": false, - "stars": 627, + "stars": 628, "formattedStars": "1k", "license": "MIT License", "lastRelease": "", @@ -305,7 +305,7 @@ }, "zino-app/graphql-flutter": { "hasCommitsInLast3Months": false, - "stars": 3267, + "stars": 3269, "formattedStars": "3k", "license": "MIT License", "lastRelease": "2025-09-07T10:05:12Z", @@ -313,11 +313,11 @@ }, "Khan/genqlient": { "hasCommitsInLast3Months": false, - "stars": 1253, + "stars": 1254, "formattedStars": "1k", "license": "MIT License", "lastRelease": "2025-05-18T19:09:08Z", - "formattedLastRelease": "4 months ago" + "formattedLastRelease": "5 months ago" }, "hasura/go-graphql-client": { "hasCommitsInLast3Months": false, @@ -325,11 +325,11 @@ "formattedStars": "449", "license": "MIT License", "lastRelease": "2025-09-29T10:13:43Z", - "formattedLastRelease": "2 weeks ago" + "formattedLastRelease": "3 weeks ago" }, "shurcooL/graphql": { "hasCommitsInLast3Months": false, - "stars": 728, + "stars": 727, "formattedStars": "1k", "license": "MIT License", "lastRelease": "", @@ -345,11 +345,11 @@ }, "99designs/gqlgen": { "hasCommitsInLast3Months": false, - "stars": 10531, + "stars": 10546, "formattedStars": "11k", "license": "MIT License", "lastRelease": "2025-09-25T23:00:54Z", - "formattedLastRelease": "2 weeks ago" + "formattedLastRelease": "3 weeks ago" }, "andrewwphillips/eggql": { "hasCommitsInLast3Months": false, @@ -369,7 +369,7 @@ }, "graph-gophers/graphql-go": { "hasCommitsInLast3Months": false, - "stars": 4728, + "stars": 4727, "formattedStars": "5k", "license": "BSD 2-Clause \"Simplified\" License", "lastRelease": "2025-09-09T11:37:07Z", @@ -377,7 +377,7 @@ }, "graphql-go/graphql": { "hasCommitsInLast3Months": false, - "stars": 10115, + "stars": 10117, "formattedStars": "10k", "license": "MIT License", "lastRelease": "2023-04-10T18:20:23Z", @@ -385,15 +385,15 @@ }, "graphql-go/relay": { "hasCommitsInLast3Months": false, - "stars": 425, - "formattedStars": "425", + "stars": 426, + "formattedStars": "426", "license": "MIT License", "lastRelease": "", "formattedLastRelease": "" }, "samsarahq/thunder": { "hasCommitsInLast3Months": false, - "stars": 1580, + "stars": 1579, "formattedStars": "2k", "license": "MIT License", "lastRelease": "", @@ -401,11 +401,11 @@ }, "wundergraph/graphql-go-tools": { "hasCommitsInLast3Months": false, - "stars": 785, + "stars": 787, "formattedStars": "1k", "license": "MIT License", - "lastRelease": "2025-10-09T14:05:39Z", - "formattedLastRelease": "5 days ago" + "lastRelease": "2025-10-20T13:49:14Z", + "formattedLastRelease": "9 hours ago" }, "dosco/graphjin": { "hasCommitsInLast3Months": false, @@ -413,7 +413,7 @@ "formattedStars": "3k", "license": "Apache License 2.0", "lastRelease": "2025-09-18T06:22:50Z", - "formattedLastRelease": "3 weeks ago" + "formattedLastRelease": "1 month ago" }, "grails/gorm-graphql": { "hasCommitsInLast3Months": false, @@ -457,7 +457,7 @@ }, "apollographql/apollo-kotlin": { "hasCommitsInLast3Months": false, - "stars": 3908, + "stars": 3911, "formattedStars": "4k", "license": "MIT License", "lastRelease": "2025-08-21T15:31:08Z", @@ -465,11 +465,11 @@ }, "ExpediaGroup/graphql-kotlin": { "hasCommitsInLast3Months": false, - "stars": 1783, + "stars": 1784, "formattedStars": "2k", "license": "Apache License 2.0", "lastRelease": "2025-06-16T17:02:18Z", - "formattedLastRelease": "3 months ago" + "formattedLastRelease": "4 months ago" }, "americanexpress/nodes": { "hasCommitsInLast3Months": false, @@ -489,7 +489,7 @@ }, "graphql-java-kickstart/graphql-spring-boot": { "hasCommitsInLast3Months": false, - "stars": 1513, + "stars": 1514, "formattedStars": "2k", "license": "MIT License", "lastRelease": "2023-12-07T11:07:47Z", @@ -497,19 +497,19 @@ }, "graphql-java/graphql-java": { "hasCommitsInLast3Months": false, - "stars": 6214, + "stars": 6216, "formattedStars": "6k", "license": "MIT License", "lastRelease": "2025-10-12T21:04:23Z", - "formattedLastRelease": "2 days ago" + "formattedLastRelease": "1 week ago" }, "babyfish-ct/jimmer": { "hasCommitsInLast3Months": false, - "stars": 1505, + "stars": 1514, "formattedStars": "2k", "license": "Apache License 2.0", - "lastRelease": "2025-09-29T11:28:10Z", - "formattedLastRelease": "2 weeks ago" + "lastRelease": "2025-10-18T13:30:49Z", + "formattedLastRelease": "2 days ago" }, "aPureBase/KGraphQL": { "hasCommitsInLast3Months": false, @@ -529,19 +529,19 @@ }, "netflix/dgs-framework": { "hasCommitsInLast3Months": false, - "stars": 3264, + "stars": 3267, "formattedStars": "3k", "license": "Apache License 2.0", - "lastRelease": "2025-09-15T20:09:48Z", - "formattedLastRelease": "4 weeks ago" + "lastRelease": "2025-10-15T19:54:58Z", + "formattedLastRelease": "5 days ago" }, "spring-projects/spring-graphql": { "hasCommitsInLast3Months": false, - "stars": 1572, + "stars": 1573, "formattedStars": "2k", "license": "Apache License 2.0", "lastRelease": "2025-09-16T15:48:43Z", - "formattedLastRelease": "4 weeks ago" + "formattedLastRelease": "1 month ago" }, "graphql-java-generator/graphql-gradle-plugin-project": { "hasCommitsInLast3Months": false, @@ -551,78 +551,6 @@ "lastRelease": "", "formattedLastRelease": "" }, - "apollographql/apollo-server": { - "hasCommitsInLast3Months": false, - "stars": 13917, - "formattedStars": "14k", - "license": "MIT License", - "lastRelease": "2025-07-17T16:58:26Z", - "formattedLastRelease": "2 months ago" - }, - "badbatch/graphql-box": { - "hasCommitsInLast3Months": false, - "stars": 27, - "formattedStars": "27", - "license": "MIT License", - "lastRelease": "", - "formattedLastRelease": "" - }, - "graphql/graphql-http": { - "hasCommitsInLast3Months": false, - "stars": 353, - "formattedStars": "353", - "license": "MIT License", - "lastRelease": "2025-01-17T14:16:52Z", - "formattedLastRelease": "8 months ago" - }, - "graphql/graphql-js": { - "hasCommitsInLast3Months": false, - "stars": 20257, - "formattedStars": "20k", - "license": "MIT License", - "lastRelease": "2025-06-11T16:37:17Z", - "formattedLastRelease": "4 months ago" - }, - "enisdenjo/graphql-sse": { - "hasCommitsInLast3Months": false, - "stars": 431, - "formattedStars": "431", - "license": "MIT License", - "lastRelease": "2025-01-10T11:57:20Z", - "formattedLastRelease": "9 months ago" - }, - "enisdenjo/graphql-ws": { - "hasCommitsInLast3Months": false, - "stars": 1835, - "formattedStars": "2k", - "license": "MIT License", - "lastRelease": "2025-07-14T12:15:37Z", - "formattedLastRelease": "3 months ago" - }, - "dotansimha/graphql-yoga": { - "hasCommitsInLast3Months": false, - "stars": 8442, - "formattedStars": "8k", - "license": "MIT License", - "lastRelease": "2025-09-19T16:33:06Z", - "formattedLastRelease": "3 weeks ago" - }, - "mercurius-js/mercurius": { - "hasCommitsInLast3Months": false, - "stars": 2448, - "formattedStars": "2k", - "license": "MIT License", - "lastRelease": "2025-10-10T09:11:58Z", - "formattedLastRelease": "4 days ago" - }, - "getcronit/pylon": { - "hasCommitsInLast3Months": false, - "stars": 338, - "formattedStars": "338", - "license": "Apache License 2.0", - "lastRelease": "2025-10-01T08:35:15Z", - "formattedLastRelease": "1 week ago" - }, "neomatrixcode/Diana.jl": { "hasCommitsInLast3Months": false, "stars": 117, @@ -639,223 +567,63 @@ "lastRelease": "2022-10-26T16:48:16Z", "formattedLastRelease": "2 years ago" }, - "networkimprov/brangr": { + "andreas/ocaml-graphql-server": { "hasCommitsInLast3Months": false, - "stars": 4, - "formattedStars": "4", - "license": "Mozilla Public License 2.0", - "lastRelease": "2023-06-02T09:20:18Z", - "formattedLastRelease": "2 years ago" + "stars": 622, + "formattedStars": "1k", + "license": "MIT License", + "lastRelease": "2022-07-08T16:26:45Z", + "formattedLastRelease": "3 years ago" }, - "hayes/giraphql": { + "graphql-perl/graphql-perl": { "hasCommitsInLast3Months": false, - "stars": 2537, - "formattedStars": "3k", - "license": "ISC License", - "lastRelease": "2025-10-07T19:06:27Z", - "formattedLastRelease": "1 week ago" + "stars": 73, + "formattedStars": "73", + "license": "Unknown", + "lastRelease": "", + "formattedLastRelease": "" }, - "graphql/graphiql": { + "api-platform/api-platform": { "hasCommitsInLast3Months": false, - "stars": 16657, - "formattedStars": "17k", + "stars": 9029, + "formattedStars": "9k", "license": "MIT License", - "lastRelease": "2025-07-19T17:43:48Z", - "formattedLastRelease": "2 months ago" + "lastRelease": "2025-03-11T16:15:41Z", + "formattedLastRelease": "7 months ago" }, - "Urigo/graphql-cli": { + "GatoGraphQL/GatoGraphQL": { "hasCommitsInLast3Months": false, - "stars": 2017, - "formattedStars": "2k", + "stars": 374, + "formattedStars": "374", + "license": "GNU General Public License v2.0", + "lastRelease": "2025-10-17T15:10:13Z", + "formattedLastRelease": "3 days ago" + }, + "infinityloop-dev/graphpinator": { + "hasCommitsInLast3Months": false, + "stars": 45, + "formattedStars": "45", "license": "MIT License", - "lastRelease": "2020-10-07T12:54:45Z", - "formattedLastRelease": "5 years ago" + "lastRelease": "2025-06-26T12:08:01Z", + "formattedLastRelease": "3 months ago" }, - "dotansimha/graphql-code-generator": { + "jerowork/graphql-attribute-schema": { "hasCommitsInLast3Months": false, - "stars": 11158, - "formattedStars": "11k", + "stars": 16, + "formattedStars": "16", "license": "MIT License", - "lastRelease": "2025-10-06T11:42:38Z", + "lastRelease": "2025-10-11T09:19:14Z", "formattedLastRelease": "1 week ago" }, - "kamilkisiela/graphql-config": { + "webonyx/graphql-php": { "hasCommitsInLast3Months": false, - "stars": 1193, - "formattedStars": "1k", + "stars": 4694, + "formattedStars": "5k", "license": "MIT License", - "lastRelease": "2025-04-28T15:15:29Z", - "formattedLastRelease": "5 months ago" + "lastRelease": "2025-10-08T10:30:00Z", + "formattedLastRelease": "1 week ago" }, - "dimaMachina/graphql-eslint/": { - "hasCommitsInLast3Months": false, - "stars": 831, - "formattedStars": "1k", - "license": "MIT License", - "lastRelease": "2025-03-26T14:11:23Z", - "formattedLastRelease": "6 months ago" - }, - "kamilkisiela/graphql-inspector": { - "hasCommitsInLast3Months": false, - "stars": 1724, - "formattedStars": "2k", - "license": "MIT License", - "lastRelease": "2024-12-09T13:34:14Z", - "formattedLastRelease": "10 months ago" - }, - "graphql/graphql-language-service": { - "hasCommitsInLast3Months": false, - "stars": 418, - "formattedStars": "418", - "license": "Unknown", - "lastRelease": "", - "formattedLastRelease": "" - }, - "n1ru4l/graphql-live-query": { - "hasCommitsInLast3Months": false, - "stars": 438, - "formattedStars": "438", - "license": "MIT License", - "lastRelease": "2022-07-29T09:27:53Z", - "formattedLastRelease": "3 years ago" - }, - "Urigo/graphql-mesh": { - "hasCommitsInLast3Months": false, - "stars": 3438, - "formattedStars": "3k", - "license": "MIT License", - "lastRelease": "2025-10-14T12:56:40Z", - "formattedLastRelease": "9 hours ago" - }, - "maticzav/graphql-middleware": { - "hasCommitsInLast3Months": false, - "stars": 1149, - "formattedStars": "1k", - "license": "MIT License", - "lastRelease": "2023-07-07T16:38:02Z", - "formattedLastRelease": "2 years ago" - }, - "Urigo/graphql-modules": { - "hasCommitsInLast3Months": false, - "stars": 1329, - "formattedStars": "1k", - "license": "MIT License", - "lastRelease": "2025-02-19T10:43:37Z", - "formattedLastRelease": "7 months ago" - }, - "Urigo/graphql-scalars": { - "hasCommitsInLast3Months": false, - "stars": 1920, - "formattedStars": "2k", - "license": "MIT License", - "lastRelease": "2025-03-19T11:10:44Z", - "formattedLastRelease": "6 months ago" - }, - "maticzav/graphql-shield": { - "hasCommitsInLast3Months": false, - "stars": 3568, - "formattedStars": "4k", - "license": "MIT License", - "lastRelease": "2022-11-22T19:08:37Z", - "formattedLastRelease": "2 years ago" - }, - "ardatan/graphql-tools": { - "hasCommitsInLast3Months": false, - "stars": 5413, - "formattedStars": "5k", - "license": "MIT License", - "lastRelease": "2025-09-22T19:23:14Z", - "formattedLastRelease": "3 weeks ago" - }, - "anvilco/graphql-introspection-tools": { - "hasCommitsInLast3Months": false, - "stars": 34, - "formattedStars": "34", - "license": "MIT License", - "lastRelease": "", - "formattedLastRelease": "" - }, - "graphile/postgraphile": { - "hasCommitsInLast3Months": false, - "stars": 12820, - "formattedStars": "13k", - "license": "Other", - "lastRelease": "2023-10-05T16:27:00Z", - "formattedLastRelease": "2 years ago" - }, - "Urigo/SOFA": { - "hasCommitsInLast3Months": false, - "stars": 1108, - "formattedStars": "1k", - "license": "MIT License", - "lastRelease": "2024-12-16T10:06:41Z", - "formattedLastRelease": "9 months ago" - }, - "anvilco/spectaql": { - "hasCommitsInLast3Months": false, - "stars": 1183, - "formattedStars": "1k", - "license": "MIT License", - "lastRelease": "", - "formattedLastRelease": "" - }, - "andreas/ocaml-graphql-server": { - "hasCommitsInLast3Months": false, - "stars": 622, - "formattedStars": "1k", - "license": "MIT License", - "lastRelease": "2022-07-08T16:26:45Z", - "formattedLastRelease": "3 years ago" - }, - "graphql-perl/graphql-perl": { - "hasCommitsInLast3Months": false, - "stars": 73, - "formattedStars": "73", - "license": "Unknown", - "lastRelease": "", - "formattedLastRelease": "" - }, - "api-platform/api-platform": { - "hasCommitsInLast3Months": false, - "stars": 9028, - "formattedStars": "9k", - "license": "MIT License", - "lastRelease": "2025-03-11T16:15:41Z", - "formattedLastRelease": "7 months ago" - }, - "GatoGraphQL/GatoGraphQL": { - "hasCommitsInLast3Months": false, - "stars": 374, - "formattedStars": "374", - "license": "GNU General Public License v2.0", - "lastRelease": "2025-09-23T13:35:04Z", - "formattedLastRelease": "3 weeks ago" - }, - "infinityloop-dev/graphpinator": { - "hasCommitsInLast3Months": false, - "stars": 45, - "formattedStars": "45", - "license": "MIT License", - "lastRelease": "2025-06-26T12:08:01Z", - "formattedLastRelease": "3 months ago" - }, - "jerowork/graphql-attribute-schema": { - "hasCommitsInLast3Months": false, - "stars": 16, - "formattedStars": "16", - "license": "MIT License", - "lastRelease": "2025-10-11T09:19:14Z", - "formattedLastRelease": "3 days ago" - }, - "webonyx/graphql-php": { - "hasCommitsInLast3Months": false, - "stars": 4692, - "formattedStars": "5k", - "license": "MIT License", - "lastRelease": "2025-10-08T10:30:00Z", - "formattedLastRelease": "6 days ago" - }, - "ivome/graphql-relay-php": { + "ivome/graphql-relay-php": { "hasCommitsInLast3Months": false, "stars": 271, "formattedStars": "271", @@ -865,11 +633,11 @@ }, "overblog/GraphQLBundle": { "hasCommitsInLast3Months": false, - "stars": 793, + "stars": 792, "formattedStars": "1k", "license": "MIT License", "lastRelease": "2024-12-19T15:48:59Z", - "formattedLastRelease": "9 months ago" + "formattedLastRelease": "10 months ago" }, "thecodingmachine/graphqlite": { "hasCommitsInLast3Months": false, @@ -881,7 +649,7 @@ }, "nuwave/lighthouse": { "hasCommitsInLast3Months": false, - "stars": 3458, + "stars": 3459, "formattedStars": "3k", "license": "MIT License", "lastRelease": "2025-09-11T08:07:50Z", @@ -913,23 +681,23 @@ }, "wp-graphql/wp-graphql": { "hasCommitsInLast3Months": false, - "stars": 3749, + "stars": 3750, "formattedStars": "4k", "license": "GNU General Public License v3.0", "lastRelease": "2025-10-14T17:40:19Z", - "formattedLastRelease": "4 hours ago" + "formattedLastRelease": "6 days ago" }, "mirumee/ariadne-codegen": { "hasCommitsInLast3Months": false, - "stars": 353, - "formattedStars": "353", + "stars": 354, + "formattedStars": "354", "license": "BSD 3-Clause \"New\" or \"Revised\" License", "lastRelease": "2025-10-13T06:38:02Z", - "formattedLastRelease": "1 day ago" + "formattedLastRelease": "1 week ago" }, "graphql-python/gql": { "hasCommitsInLast3Months": false, - "stars": 1638, + "stars": 1639, "formattedStars": "2k", "license": "MIT License", "lastRelease": "2025-09-05T14:22:54Z", @@ -975,21 +743,13 @@ "lastRelease": "", "formattedLastRelease": "" }, - "ropensci/ghql": { - "hasCommitsInLast3Months": false, - "stars": 148, - "formattedStars": "148", - "license": "Other", - "lastRelease": "2025-09-08T08:41:00Z", - "formattedLastRelease": "1 month ago" - }, "mirumee/ariadne": { "hasCommitsInLast3Months": false, "stars": 2298, "formattedStars": "2k", "license": "BSD 3-Clause \"New\" or \"Revised\" License", "lastRelease": "2025-04-18T08:27:47Z", - "formattedLastRelease": "5 months ago" + "formattedLastRelease": "6 months ago" }, "yefeza/django-graphbox": { "hasCommitsInLast3Months": false, @@ -1009,7 +769,7 @@ }, "graphql-python/graphene": { "hasCommitsInLast3Months": false, - "stars": 8221, + "stars": 8226, "formattedStars": "8k", "license": "MIT License", "lastRelease": "2024-11-09T20:43:58Z", @@ -1017,11 +777,11 @@ }, "strawberry-graphql/strawberry": { "hasCommitsInLast3Months": false, - "stars": 4434, + "stars": 4445, "formattedStars": "4k", "license": "MIT License", - "lastRelease": "2025-10-10T20:03:01Z", - "formattedLastRelease": "4 days ago" + "lastRelease": "2025-10-18T20:31:24Z", + "formattedLastRelease": "2 days ago" }, "tartiflette/tartiflette": { "hasCommitsInLast3Months": false, @@ -1031,21 +791,53 @@ "lastRelease": "2021-11-15T11:05:03Z", "formattedLastRelease": "3 years ago" }, + "ropensci/ghql": { + "hasCommitsInLast3Months": false, + "stars": 148, + "formattedStars": "148", + "license": "Other", + "lastRelease": "2025-09-08T08:41:00Z", + "formattedLastRelease": "1 month ago" + }, + "ohler55/agoo": { + "hasCommitsInLast3Months": false, + "stars": 922, + "formattedStars": "1k", + "license": "MIT License", + "lastRelease": "2025-09-24T22:20:23Z", + "formattedLastRelease": "3 weeks ago" + }, + "rmosolgo/graphql-ruby": { + "hasCommitsInLast3Months": false, + "stars": 5424, + "formattedStars": "5k", + "license": "MIT License", + "lastRelease": "2025-07-19T17:15:49Z", + "formattedLastRelease": "3 months ago" + }, + "virtualshield/rails-graphql": { + "hasCommitsInLast3Months": false, + "stars": 187, + "formattedStars": "187", + "license": "MIT License", + "lastRelease": "2025-08-25T17:53:38Z", + "formattedLastRelease": "1 month ago" + }, "apollographql/apollo-client": { "hasCommitsInLast3Months": false, - "stars": 19655, + "stars": 19658, "formattedStars": "20k", "license": "MIT License", "lastRelease": "2025-10-10T15:51:43Z", - "formattedLastRelease": "4 days ago" + "formattedLastRelease": "1 week ago" }, "aws-amplify/amplify-js": { "hasCommitsInLast3Months": false, - "stars": 9559, + "stars": 9560, "formattedStars": "10k", "license": "Apache License 2.0", "lastRelease": "2025-09-29T15:51:39Z", - "formattedLastRelease": "2 weeks ago" + "formattedLastRelease": "3 weeks ago" }, "Houfeng/gq-loader": { "hasCommitsInLast3Months": false, @@ -1061,7 +853,7 @@ "formattedStars": "1k", "license": "MIT License", "lastRelease": "2025-10-09T07:29:27Z", - "formattedLastRelease": "5 days ago" + "formattedLastRelease": "1 week ago" }, "grafoojs/grafoo": { "hasCommitsInLast3Months": false, @@ -1071,6 +863,14 @@ "lastRelease": "2018-06-20T15:21:00Z", "formattedLastRelease": "7 years ago" }, + "badbatch/graphql-box": { + "hasCommitsInLast3Months": false, + "stars": 28, + "formattedStars": "28", + "license": "MIT License", + "lastRelease": "", + "formattedLastRelease": "" + }, "nearform/graphql-hooks": { "hasCommitsInLast3Months": false, "stars": 1890, @@ -1079,13 +879,29 @@ "lastRelease": "2025-01-08T18:45:52Z", "formattedLastRelease": "9 months ago" }, + "graphql/graphql-http": { + "hasCommitsInLast3Months": false, + "stars": 354, + "formattedStars": "354", + "license": "MIT License", + "lastRelease": "2025-01-17T14:16:52Z", + "formattedLastRelease": "9 months ago" + }, "jasonkuhrt/graphql-request": { "hasCommitsInLast3Months": false, - "stars": 6047, + "stars": 6058, "formattedStars": "6k", "license": "MIT License", - "lastRelease": "2020-05-29T13:00:56Z", - "formattedLastRelease": "5 years ago" + "lastRelease": "2025-10-18T20:40:58Z", + "formattedLastRelease": "2 days ago" + }, + "enisdenjo/graphql-sse": { + "hasCommitsInLast3Months": false, + "stars": 432, + "formattedStars": "432", + "license": "MIT License", + "lastRelease": "2025-01-10T11:57:20Z", + "formattedLastRelease": "9 months ago" }, "babyfish-ct/graphql-ts-client": { "hasCommitsInLast3Months": false, @@ -1095,6 +911,14 @@ "lastRelease": "2023-12-14T03:06:21Z", "formattedLastRelease": "1 year ago" }, + "enisdenjo/graphql-ws": { + "hasCommitsInLast3Months": false, + "stars": 1837, + "formattedStars": "2k", + "license": "MIT License", + "lastRelease": "2025-07-14T12:15:37Z", + "formattedLastRelease": "3 months ago" + }, "hasura/graphqurl": { "hasCommitsInLast3Months": false, "stars": 3374, @@ -1121,7 +945,7 @@ }, "facebook/relay": { "hasCommitsInLast3Months": false, - "stars": 18863, + "stars": 18870, "formattedStars": "19k", "license": "MIT License", "lastRelease": "2025-08-06T23:45:00Z", @@ -1129,43 +953,59 @@ }, "FormidableLabs/urql": { "hasCommitsInLast3Months": false, - "stars": 8873, + "stars": 8876, "formattedStars": "9k", "license": "MIT License", "lastRelease": "2025-08-29T08:06:41Z", "formattedLastRelease": "1 month ago" }, - "ohler55/agoo": { + "apollographql/apollo-server": { "hasCommitsInLast3Months": false, - "stars": 922, - "formattedStars": "1k", + "stars": 13916, + "formattedStars": "14k", "license": "MIT License", - "lastRelease": "2025-09-24T22:20:23Z", - "formattedLastRelease": "2 weeks ago" + "lastRelease": "2025-07-17T16:58:26Z", + "formattedLastRelease": "3 months ago" }, - "rmosolgo/graphql-ruby": { + "graphql/graphql-js": { "hasCommitsInLast3Months": false, - "stars": 5423, - "formattedStars": "5k", + "stars": 20261, + "formattedStars": "20k", "license": "MIT License", - "lastRelease": "2025-07-19T17:15:49Z", - "formattedLastRelease": "2 months ago" + "lastRelease": "2025-06-11T16:37:17Z", + "formattedLastRelease": "4 months ago" }, - "virtualshield/rails-graphql": { + "dotansimha/graphql-yoga": { "hasCommitsInLast3Months": false, - "stars": 187, - "formattedStars": "187", + "stars": 8442, + "formattedStars": "8k", "license": "MIT License", - "lastRelease": "2025-08-25T17:53:38Z", + "lastRelease": "2025-09-19T16:33:06Z", "formattedLastRelease": "1 month ago" }, + "mercurius-js/mercurius": { + "hasCommitsInLast3Months": false, + "stars": 2446, + "formattedStars": "2k", + "license": "MIT License", + "lastRelease": "2025-10-10T09:11:58Z", + "formattedLastRelease": "1 week ago" + }, + "getcronit/pylon": { + "hasCommitsInLast3Months": false, + "stars": 341, + "formattedStars": "341", + "license": "Apache License 2.0", + "lastRelease": "2025-10-01T08:35:15Z", + "formattedLastRelease": "2 weeks ago" + }, "obmarg/cynic": { "hasCommitsInLast3Months": false, "stars": 435, "formattedStars": "435", "license": "Mozilla Public License 2.0", "lastRelease": "2025-08-19T19:37:22Z", - "formattedLastRelease": "1 month ago" + "formattedLastRelease": "2 months ago" }, "arthurkhlghatyan/gql-client-rs": { "hasCommitsInLast3Months": false, @@ -1175,9 +1015,169 @@ "lastRelease": "2025-06-07T14:31:10Z", "formattedLastRelease": "4 months ago" }, + "networkimprov/brangr": { + "hasCommitsInLast3Months": false, + "stars": 4, + "formattedStars": "4", + "license": "Mozilla Public License 2.0", + "lastRelease": "2023-06-02T09:20:18Z", + "formattedLastRelease": "2 years ago" + }, + "hayes/giraphql": { + "hasCommitsInLast3Months": false, + "stars": 2539, + "formattedStars": "3k", + "license": "ISC License", + "lastRelease": "2025-10-20T17:43:11Z", + "formattedLastRelease": "5 hours ago" + }, + "graphql/graphiql": { + "hasCommitsInLast3Months": false, + "stars": 16666, + "formattedStars": "17k", + "license": "MIT License", + "lastRelease": "2025-07-19T17:43:48Z", + "formattedLastRelease": "3 months ago" + }, + "Urigo/graphql-cli": { + "hasCommitsInLast3Months": false, + "stars": 2018, + "formattedStars": "2k", + "license": "MIT License", + "lastRelease": "2020-10-07T12:54:45Z", + "formattedLastRelease": "5 years ago" + }, + "dotansimha/graphql-code-generator": { + "hasCommitsInLast3Months": false, + "stars": 11161, + "formattedStars": "11k", + "license": "MIT License", + "lastRelease": "2025-10-06T11:42:38Z", + "formattedLastRelease": "2 weeks ago" + }, + "kamilkisiela/graphql-config": { + "hasCommitsInLast3Months": false, + "stars": 1193, + "formattedStars": "1k", + "license": "MIT License", + "lastRelease": "2025-04-28T15:15:29Z", + "formattedLastRelease": "5 months ago" + }, + "dimaMachina/graphql-eslint/": { + "hasCommitsInLast3Months": false, + "stars": 831, + "formattedStars": "1k", + "license": "MIT License", + "lastRelease": "2025-03-26T14:11:23Z", + "formattedLastRelease": "6 months ago" + }, + "kamilkisiela/graphql-inspector": { + "hasCommitsInLast3Months": false, + "stars": 1725, + "formattedStars": "2k", + "license": "MIT License", + "lastRelease": "2024-12-09T13:34:14Z", + "formattedLastRelease": "10 months ago" + }, + "graphql/graphql-language-service": { + "hasCommitsInLast3Months": false, + "stars": 418, + "formattedStars": "418", + "license": "Unknown", + "lastRelease": "", + "formattedLastRelease": "" + }, + "n1ru4l/graphql-live-query": { + "hasCommitsInLast3Months": false, + "stars": 438, + "formattedStars": "438", + "license": "MIT License", + "lastRelease": "2022-07-29T09:27:53Z", + "formattedLastRelease": "3 years ago" + }, + "Urigo/graphql-mesh": { + "hasCommitsInLast3Months": false, + "stars": 3443, + "formattedStars": "3k", + "license": "MIT License", + "lastRelease": "2025-10-14T12:56:40Z", + "formattedLastRelease": "6 days ago" + }, + "maticzav/graphql-middleware": { + "hasCommitsInLast3Months": false, + "stars": 1149, + "formattedStars": "1k", + "license": "MIT License", + "lastRelease": "2023-07-07T16:38:02Z", + "formattedLastRelease": "2 years ago" + }, + "Urigo/graphql-modules": { + "hasCommitsInLast3Months": false, + "stars": 1330, + "formattedStars": "1k", + "license": "MIT License", + "lastRelease": "2025-02-19T10:43:37Z", + "formattedLastRelease": "8 months ago" + }, + "Urigo/graphql-scalars": { + "hasCommitsInLast3Months": false, + "stars": 1922, + "formattedStars": "2k", + "license": "MIT License", + "lastRelease": "2025-10-14T23:00:24Z", + "formattedLastRelease": "6 days ago" + }, + "maticzav/graphql-shield": { + "hasCommitsInLast3Months": false, + "stars": 3566, + "formattedStars": "4k", + "license": "MIT License", + "lastRelease": "2022-11-22T19:08:37Z", + "formattedLastRelease": "2 years ago" + }, + "ardatan/graphql-tools": { + "hasCommitsInLast3Months": false, + "stars": 5413, + "formattedStars": "5k", + "license": "MIT License", + "lastRelease": "2025-09-22T19:23:14Z", + "formattedLastRelease": "4 weeks ago" + }, + "anvilco/graphql-introspection-tools": { + "hasCommitsInLast3Months": false, + "stars": 35, + "formattedStars": "35", + "license": "MIT License", + "lastRelease": "", + "formattedLastRelease": "" + }, + "graphile/postgraphile": { + "hasCommitsInLast3Months": false, + "stars": 12826, + "formattedStars": "13k", + "license": "Other", + "lastRelease": "2023-10-05T16:27:00Z", + "formattedLastRelease": "2 years ago" + }, + "Urigo/SOFA": { + "hasCommitsInLast3Months": false, + "stars": 1108, + "formattedStars": "1k", + "license": "MIT License", + "lastRelease": "2024-12-16T10:06:41Z", + "formattedLastRelease": "10 months ago" + }, + "anvilco/spectaql": { + "hasCommitsInLast3Months": false, + "stars": 1185, + "formattedStars": "1k", + "license": "MIT License", + "lastRelease": "", + "formattedLastRelease": "" + }, "async-graphql/async-graphql": { "hasCommitsInLast3Months": false, - "stars": 3580, + "stars": 3582, "formattedStars": "4k", "license": "Apache License 2.0", "lastRelease": "", @@ -1185,7 +1185,7 @@ }, "graphql-rust/juniper": { "hasCommitsInLast3Months": false, - "stars": 5889, + "stars": 5887, "formattedStars": "6k", "license": "Other", "lastRelease": "2025-09-08T23:23:40Z", @@ -1193,7 +1193,7 @@ }, "ghostdogpr/caliban": { "hasCommitsInLast3Months": false, - "stars": 976, + "stars": 977, "formattedStars": "1k", "license": "Apache License 2.0", "lastRelease": "2025-07-14T00:24:20Z", @@ -1201,19 +1201,19 @@ }, "sangria-graphql/sangria": { "hasCommitsInLast3Months": false, - "stars": 1959, + "stars": 1958, "formattedStars": "2k", "license": "Apache License 2.0", - "lastRelease": "2025-10-07T13:32:28Z", - "formattedLastRelease": "1 week ago" + "lastRelease": "2025-10-20T11:40:30Z", + "formattedLastRelease": "11 hours ago" }, "apollographql/apollo-ios": { "hasCommitsInLast3Months": false, - "stars": 3993, + "stars": 3996, "formattedStars": "4k", "license": "MIT License", "lastRelease": "2025-10-14T16:43:14Z", - "formattedLastRelease": "5 hours ago" + "formattedLastRelease": "6 days ago" }, "nerdsupremacist/Graphaello": { "hasCommitsInLast3Months": false, @@ -1257,11 +1257,11 @@ }, "apollographql/router": { "hasCommitsInLast3Months": false, - "stars": 933, + "stars": 934, "formattedStars": "1k", "license": "Other", "lastRelease": "2025-10-01T17:36:59Z", - "formattedLastRelease": "1 week ago" + "formattedLastRelease": "2 weeks ago" }, "eerimoq/gqt": { "hasCommitsInLast3Months": false, @@ -1273,7 +1273,7 @@ }, "Escape-Technologies/graphql-armor": { "hasCommitsInLast3Months": false, - "stars": 548, + "stars": 550, "formattedStars": "1k", "license": "MIT License", "lastRelease": "2025-08-22T13:32:40Z", @@ -1281,31 +1281,31 @@ }, "ldebruijn/graphql-protect": { "hasCommitsInLast3Months": false, - "stars": 35, - "formattedStars": "35", + "stars": 34, + "formattedStars": "34", "license": "MIT License", "lastRelease": "2025-09-09T20:03:39Z", "formattedLastRelease": "1 month ago" }, "graphql-hive/gateway": { "hasCommitsInLast3Months": false, - "stars": 60, - "formattedStars": "60", + "stars": 62, + "formattedStars": "62", "license": "MIT License", "lastRelease": "2025-10-13T09:50:34Z", - "formattedLastRelease": "1 day ago" + "formattedLastRelease": "1 week ago" }, "microcks/microcks": { "hasCommitsInLast3Months": false, - "stars": 1726, + "stars": 1725, "formattedStars": "2k", "license": "Apache License 2.0", "lastRelease": "2025-07-21T12:44:11Z", - "formattedLastRelease": "2 months ago" + "formattedLastRelease": "3 months ago" }, "glideapps/quicktype": { "hasCommitsInLast3Months": false, - "stars": 13363, + "stars": 13375, "formattedStars": "13k", "license": "Apache License 2.0", "lastRelease": "", @@ -1313,18 +1313,18 @@ }, "schemathesis/schemathesis": { "hasCommitsInLast3Months": false, - "stars": 2774, + "stars": 2786, "formattedStars": "3k", "license": "MIT License", - "lastRelease": "2025-10-14T15:53:33Z", - "formattedLastRelease": "6 hours ago" + "lastRelease": "2025-10-20T22:30:18Z", + "formattedLastRelease": "54 minutes ago" }, "wundergraph/cosmo": { "hasCommitsInLast3Months": false, - "stars": 1091, + "stars": 1097, "formattedStars": "1k", "license": "Apache License 2.0", - "lastRelease": "2025-10-14T08:05:52Z", - "formattedLastRelease": "14 hours ago" + "lastRelease": "2025-10-20T15:08:25Z", + "formattedLastRelease": "8 hours ago" } } \ No newline at end of file diff --git a/scripts/get-github-info/last-success.isodate b/scripts/get-github-info/last-success.isodate index 7ae35a51ea..51a2963d36 100644 --- a/scripts/get-github-info/last-success.isodate +++ b/scripts/get-github-info/last-success.isodate @@ -1 +1 @@ -2025-10-14T22:15:27.030Z \ No newline at end of file +2025-10-20T23:24:23.459Z \ No newline at end of file diff --git a/src/_design-system/breadcrumbs.tsx b/src/_design-system/breadcrumbs.tsx index 776caff29f..4b42a7554a 100644 --- a/src/_design-system/breadcrumbs.tsx +++ b/src/_design-system/breadcrumbs.tsx @@ -1,7 +1,6 @@ import { Fragment } from "react" import { clsx } from "clsx" import NextLink from "next/link" -import { ArrowRightIcon } from "nextra/icons" import type { Item } from "nextra/normalize-pages" import CaretDown from "@/app/conf/_design-system/pixelarticons/caret-down.svg?svgr" @@ -18,26 +17,18 @@ export const Breadcrumbs = ({ return (
- {activePath.map((item, index, arr) => { - const nextItem = arr[index + 1] - const href = nextItem - ? "frontMatter" in item - ? item.route - : (item as { children: { route: string }[] }).children[0]?.route === - nextItem.route - ? "" - : (item as { children: { route: string }[] }).children[0]?.route - : "" + {activePath.map((item, index) => { + const href = getHref(item) const title = extractStringsFromReactNode(item.title) const className = clsx( - "truncate text-neu-700 dark:text-neu-400 min-w-6 last:text-neu-800 dark:last:text-neu-800 leading-none", + "text-neu-700 dark:text-neu-400 min-w-6 last:text-neu-800 dark:last:text-neu-800 leading-none", href && - "focus-visible:gql-focus-visible ring-inset hover:text-neu-900 hover:underline", + "gql-focus-visible ring-inset hover:text-neu-900 hover:underline underline-offset-2", ) return ( @@ -46,7 +37,12 @@ export const Breadcrumbs = ({ )} {href ? ( - + {item.title} ) : ( @@ -60,3 +56,10 @@ export const Breadcrumbs = ({
) } + +function getHref(item: Item): string { + if ("frontMatter" in item) return item.route + + const firstChild = item.children?.[0] + return firstChild ? getHref(firstChild) : "" +} diff --git a/src/app/conf/2025/schedule/_components/filters.tsx b/src/app/conf/2025/schedule/_components/filters.tsx index daa2b7bf3b..dac1105c09 100644 --- a/src/app/conf/2025/schedule/_components/filters.tsx +++ b/src/app/conf/2025/schedule/_components/filters.tsx @@ -11,6 +11,7 @@ import { import { Tag } from "@/app/conf/_design-system/tag" import { Button } from "@/app/conf/_design-system/button" +import { CheckboxIcon } from "@/app/conf/_design-system/pixelarticons/checkbox-icon" import CloseIcon from "@/app/conf/_design-system/pixelarticons/close.svg?svgr" import CaretDownIcon from "@/app/conf/_design-system/pixelarticons/caret-down.svg?svgr" @@ -217,41 +218,6 @@ function FiltersCombobox({ ) } -interface CheckboxIconProps extends React.SVGProps { - checked: boolean -} -function CheckboxIcon({ checked, ...rest }: CheckboxIconProps) { - return ( - - {!checked ? ( - <> - - - ) : ( - - - - - - - - - )} - - ) -} - function FilterComboboxOption({ active, selected, diff --git a/src/app/conf/_design-system/button.tsx b/src/app/conf/_design-system/button.tsx index c3b1e4a074..84be43414d 100644 --- a/src/app/conf/_design-system/button.tsx +++ b/src/app/conf/_design-system/button.tsx @@ -46,7 +46,7 @@ export declare namespace ButtonProps { extends BaseProps, React.DetailedHTMLProps, HTMLElement> { href?: never - as: "span" | "div" + as: "span" | "div" | "label" className?: string } } diff --git a/src/app/conf/_design-system/pixelarticons/arrow-bar-left.svg b/src/app/conf/_design-system/pixelarticons/arrow-bar-left.svg new file mode 100644 index 0000000000..a7770c4b5b --- /dev/null +++ b/src/app/conf/_design-system/pixelarticons/arrow-bar-left.svg @@ -0,0 +1,5 @@ + + + diff --git a/src/app/conf/_design-system/pixelarticons/checkbox-icon.tsx b/src/app/conf/_design-system/pixelarticons/checkbox-icon.tsx new file mode 100644 index 0000000000..7be08f9281 --- /dev/null +++ b/src/app/conf/_design-system/pixelarticons/checkbox-icon.tsx @@ -0,0 +1,34 @@ +export interface CheckboxIconProps extends React.SVGProps { + checked: boolean +} +export function CheckboxIcon({ checked, ...rest }: CheckboxIconProps) { + return ( + + {!checked ? ( + <> + + + ) : ( + + + + + + + + + )} + + ) +} diff --git a/src/app/conf/_design-system/pixelarticons/moon.svg b/src/app/conf/_design-system/pixelarticons/moon.svg new file mode 100644 index 0000000000..c1529d535a --- /dev/null +++ b/src/app/conf/_design-system/pixelarticons/moon.svg @@ -0,0 +1,7 @@ + + + diff --git a/src/app/conf/_design-system/pixelarticons/notes.svg b/src/app/conf/_design-system/pixelarticons/notes.svg new file mode 100644 index 0000000000..0aba221fb1 --- /dev/null +++ b/src/app/conf/_design-system/pixelarticons/notes.svg @@ -0,0 +1,5 @@ + + + diff --git a/src/app/conf/_design-system/pixelarticons/radio-icon.tsx b/src/app/conf/_design-system/pixelarticons/radio-icon.tsx new file mode 100644 index 0000000000..2c1c354581 --- /dev/null +++ b/src/app/conf/_design-system/pixelarticons/radio-icon.tsx @@ -0,0 +1,20 @@ +export interface RadioIconProps extends React.SVGProps { + checked?: boolean +} + +export function RadioIcon({ checked, ...rest }: RadioIconProps) { + return ( + + + + + ) +} diff --git a/src/app/conf/_design-system/pixelarticons/sun.svg b/src/app/conf/_design-system/pixelarticons/sun.svg new file mode 100644 index 0000000000..c2eb3aae94 --- /dev/null +++ b/src/app/conf/_design-system/pixelarticons/sun.svg @@ -0,0 +1,7 @@ + + + diff --git a/src/app/conf/_design-system/pixelarticons/system.svg b/src/app/conf/_design-system/pixelarticons/system.svg new file mode 100644 index 0000000000..3121ba395c --- /dev/null +++ b/src/app/conf/_design-system/pixelarticons/system.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 0f30f4ddc5..8a10151189 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,6 +1,8 @@ import { ReactElement, ReactNode } from "react" import { Metadata } from "next" import GoogleAnalytics from "@/app/ga" + +import "../conf.css" import "../globals.css" export const metadata: Metadata = { diff --git a/src/components/checkbox-tree/checkbox-tree.tsx b/src/components/checkbox-tree/checkbox-tree.tsx new file mode 100644 index 0000000000..43bc82f237 --- /dev/null +++ b/src/components/checkbox-tree/checkbox-tree.tsx @@ -0,0 +1,123 @@ +import type { ReactNode } from "react" +import { clsx } from "clsx" +import { CheckboxIcon } from "@/app/conf/_design-system/pixelarticons/checkbox-icon" + +export interface CheckboxTreeItem { + id: string + label: string + value?: string + count?: number + disabled?: boolean + children?: CheckboxTreeItem[] +} + +interface CheckboxTreeProps { + items: CheckboxTreeItem[] + selectedValues: string[] + onSelectionChange: (next: string[]) => void + depth?: number +} + +export function CheckboxTree({ + items, + selectedValues, + onSelectionChange, + depth = 0, +}: CheckboxTreeProps) { + return ( +
+ {items.map(item => { + const isSelectable = Boolean(item.value) + const isDisabled = item.disabled || item.count === 0 + const isChecked = isSelectable + ? selectedValues.includes(item.value!) + : false + const checkboxId = `checkbox-tree-${item.id}` + + const toggleValue = (value: string) => { + const next = selectedValues.includes(value) + ? selectedValues.filter(tag => tag !== value) + : [...selectedValues, value] + onSelectionChange(next) + } + + return ( +
+
+ {isSelectable ? ( + + ) : ( +
+ {item.label} +
+ )} +
+ + {item.children && item.children.length > 0 ? ( + + ) : null} +
+ ) + })} +
+ ) +} diff --git a/src/components/checkbox-tree/index.ts b/src/components/checkbox-tree/index.ts new file mode 100644 index 0000000000..9d0602a173 --- /dev/null +++ b/src/components/checkbox-tree/index.ts @@ -0,0 +1,2 @@ +export { CheckboxTree } from "./checkbox-tree" +export type { CheckboxTreeItem } from "./checkbox-tree" diff --git a/src/components/checkbox-tree/use-search-params-state.tsx b/src/components/checkbox-tree/use-search-params-state.tsx new file mode 100644 index 0000000000..178fe5f622 --- /dev/null +++ b/src/components/checkbox-tree/use-search-params-state.tsx @@ -0,0 +1,147 @@ +import { use, useCallback, useMemo, useSyncExternalStore } from "react" + +type SetSearchParamsAction = + | string + | URLSearchParams + | ((prev: URLSearchParams) => URLSearchParams | void) + +interface SetSearchParamsOptions { + replace?: boolean +} + +const listeners = new Set<() => void>() +let restoreHistory: (() => void) | null = null + +const notifyListeners = () => { + listeners.forEach(listener => listener()) +} + +const subscribe = (listener: () => void) => { + if (typeof window === "undefined") { + return () => {} + } + + if (listeners.size === 0) { + window.addEventListener("popstate", notifyListeners) + patchHistory() + } + + listeners.add(listener) + + return () => { + listeners.delete(listener) + + if (listeners.size === 0) { + window.removeEventListener("popstate", notifyListeners) + restorePatchedHistory() + } + } +} + +const patchHistory = () => { + if (restoreHistory || typeof window === "undefined") return + + const { history } = window + const originalPushState = history.pushState + const originalReplaceState = history.replaceState + + const patchedPushState: History["pushState"] = (...args) => { + const result = originalPushState.apply(history, args) + notifyListeners() + return result + } + + const patchedReplaceState: History["replaceState"] = (...args) => { + const result = originalReplaceState.apply(history, args) + notifyListeners() + return result + } + + history.pushState = patchedPushState + history.replaceState = patchedReplaceState + + restoreHistory = () => { + history.pushState = originalPushState + history.replaceState = originalReplaceState + restoreHistory = null + } +} + +const restorePatchedHistory = () => { + restoreHistory?.() +} + +const getClientSnapshot = () => + typeof window === "undefined" ? "" : window.location.search + +const getServerSnapshot = () => "" + +const formatSearchFromAction = ( + action: SetSearchParamsAction, + current: URLSearchParams, +) => { + if (typeof action === "function") { + const draft = new URLSearchParams(current) + const result = action(draft) + return result ?? draft + } + + if (typeof action === "string") { + const normalized = action.startsWith("?") ? action.slice(1) : action + return new URLSearchParams(normalized) + } + + return new URLSearchParams(action) +} + +/** + * Next.js Pages Router doesn't have `useSearchParams`, and we need one for Tools and Libraries checkbox tree. + */ +export function useSearchParamsState() { + const search = useSyncExternalStore( + subscribe, + getClientSnapshot, + getServerSnapshot, + ) + + const searchParams = useMemo(() => { + const normalized = search.startsWith("?") ? search.slice(1) : search + return new URLSearchParams(normalized) + }, [search]) + + const setSearchParams = useCallback( + (action: SetSearchParamsAction, options?: SetSearchParamsOptions) => { + if (typeof window === "undefined") return + + const current = new URLSearchParams(window.location.search) + const nextParams = formatSearchFromAction(action, current) + const nextQuery = nextParams.toString() + const nextSearch = nextQuery ? `?${nextQuery}` : "" + + if (nextSearch === window.location.search) return + + const url = new URL(window.location.href) + + url.search = nextQuery + + const method = options?.replace ? "replaceState" : "pushState" + + window.history[method]( + window.history.state, + "", + `${url.pathname}${url.search}${url.hash}`, + ) + }, + [], + ) + + return [searchParams, setSearchParams] as const +} + +// eslint-disable-next-line @typescript-eslint/no-namespace +export declare namespace useSearchParamsState { + type SetSearchParams = ( + action: SetSearchParamsAction, + options?: SetSearchParamsOptions, + ) => void +} diff --git a/src/components/footer/index.tsx b/src/components/footer/index.tsx index d372f9e5b5..effa176e5a 100644 --- a/src/components/footer/index.tsx +++ b/src/components/footer/index.tsx @@ -1,15 +1,16 @@ import NextLink from "next/link" -import { useConfig, useThemeConfig } from "nextra-theme-docs" +import { useThemeConfig } from "nextra-theme-docs" +import type { ReactNode } from "react" import { GraphQLWordmarkLogo } from "@/icons" import { SocialIcons } from "@/app/conf/_components/social-icons" import { StripesDecoration } from "@/app/conf/_design-system/stripes-decoration" import blurBean from "@/app/conf/2025/components/footer/blur-bean.webp" +import { Anchor } from "@/app/conf/_design-system/anchor" import { renderComponent } from "../utils/render-component" -import { Anchor } from "@/app/conf/_design-system/anchor" -import type { ReactNode } from "react" import { ConferenceFooterBox } from "./conference-footer-box" +import { ThemeSwitch } from "../theme-switch" interface FooterLink { title: ReactNode @@ -122,7 +123,7 @@ export function Footer() {
- {renderComponent(themeConfig.themeSwitch.component)} +
@@ -175,9 +176,8 @@ export function Footer() {
{themeConfig.darkMode && ( - // todo: new theme switch component
- {renderComponent(themeConfig.themeSwitch.component)} +
)}

diff --git a/src/components/micro-markdown.tsx b/src/components/micro-markdown.tsx new file mode 100644 index 0000000000..63ed9e6c77 --- /dev/null +++ b/src/components/micro-markdown.tsx @@ -0,0 +1,136 @@ +import NextLink from "next/link" +import { memo, type ReactNode } from "react" + +type Token = + | { type: "text"; value: string } + | { type: "link"; label: string; href: string } + | { type: "code"; value: string } + | { type: "bold"; value: string } + | { type: "italic"; value: string } + +const TOKEN_REGEX = + /\[([^\]]+)\]\(([^)]+)\)|`([^`]+)`|\*\*([^*]+)\*\*|_([^_]+)_|(https?:\/\/[^\s)]+|www\.[^\s)]+)/g + +const TRAILING_PUNCTUATION = /[),.?!]+$/ + +const formatUrlLabel = (url: string) => + url.replace(/^https?:\/\//, "").replace(/^www\./, "") + +const splitMarkdown = (value: string): Token[] => { + const tokens: Token[] = [] + let lastIndex = 0 + let match: RegExpExecArray | null + TOKEN_REGEX.lastIndex = 0 + + while ((match = TOKEN_REGEX.exec(value)) !== null) { + const matchIndex = match.index + if (matchIndex > lastIndex) { + tokens.push({ type: "text", value: value.slice(lastIndex, matchIndex) }) + } + + if (match[1] && match[2]) { + const href = match[2].trim() + tokens.push({ type: "link", label: match[1], href }) + } else if (match[3]) { + tokens.push({ type: "code", value: match[3] }) + } else if (match[4]) { + tokens.push({ type: "bold", value: match[4] }) + } else if (match[5]) { + tokens.push({ type: "italic", value: match[5] }) + } else if (match[6]) { + const rawUrl = match[6] + const trailing = TRAILING_PUNCTUATION.exec(rawUrl) + const trailingCharacters = trailing?.[0] ?? "" + const href = trailingCharacters + ? rawUrl.slice(0, rawUrl.length - trailingCharacters.length) + : rawUrl + tokens.push({ + type: "link", + label: formatUrlLabel(href), + href: href.startsWith("http") ? href : `https://${href}`, + }) + if (trailingCharacters) { + tokens.push({ type: "text", value: trailingCharacters }) + } + } + + lastIndex = TOKEN_REGEX.lastIndex + } + + if (lastIndex < value.length) { + tokens.push({ type: "text", value: value.slice(lastIndex) }) + } + + return tokens +} + +const renderTokens = (tokens: Token[], keyPrefix = ""): ReactNode[] => + tokens.map((token, index) => { + const key = `${keyPrefix}${index}` + if (token.type === "text") { + return {token.value} + } + + if (token.type === "bold") { + return ( + + {renderTokens(splitMarkdown(token.value), `${key}-`)} + + ) + } + + if (token.type === "italic") { + return ( + {renderTokens(splitMarkdown(token.value), `${key}-`)} + ) + } + + if (token.type === "code") { + return ( + + {token.value} + + ) + } + + if (token.type === "link") { + const { href, label } = token + const isExternal = /^https?:\/\//.test(href) + if (!isExternal) { + return ( + + {label} + + ) + } + + return ( + + {label} + + ) + } + + return null + }) + +type MicroMarkdownProps = { + text: string + className?: string +} + +/** + * Transforms a subset of Markdown for Tools and Libraries frontmatter descriptions. + */ +export const MicroMarkdown = memo(({ text, className }: MicroMarkdownProps) => { + const tokens = splitMarkdown(text) + return

{renderTokens(tokens)}

+}) + +MicroMarkdown.displayName = "μMd" diff --git a/src/components/navbar/navbar.tsx b/src/components/navbar/navbar.tsx index 76eea928a0..fd0c58cd1d 100644 --- a/src/components/navbar/navbar.tsx +++ b/src/components/navbar/navbar.tsx @@ -1,11 +1,17 @@ -import { MenuItem, Menu, MenuButton, MenuItems } from "@headlessui/react" +import { NavigationMenu } from "@base-ui-components/react/navigation-menu" + import clsx from "clsx" // eslint-disable-next-line no-restricted-imports -- since we don't need newWindow prop import NextLink from "next/link" import { Button } from "nextra/components" import { useFSRoute } from "nextra/hooks" import type * as normalizePages from "nextra/normalize-pages" -import { Fragment, useState, type ReactElement, type ReactNode } from "react" +import React, { + useCallback, + useEffect, + type ReactElement, + type ReactNode, +} from "react" import { useMenu, useThemeConfig } from "nextra-theme-docs" import { Anchor } from "@/app/conf/_design-system/anchor" import { renderComponent } from "@/components/utils/render-component" @@ -18,77 +24,56 @@ export interface NavBarProps { items: Item[] } -const classes = { - link: "typography-menu flex items-center text-neu-900 px-3 py-1 nextra-focus [text-box:trim-both_cap_alphabetic] leading-none hover:underline underline-offset-2", -} +const linkClasses = + "typography-menu flex items-center text-neu-900 px-3 py-1 nextra-focus [text-box:trim-both_cap_alphabetic] leading-none hover:underline underline-offset-2" function NavbarMenu({ menu, children, - onSubmenuOpen, }: { menu: normalizePages.MenuItem children: ReactNode - onSubmenuOpen: (open: boolean) => void }): ReactElement { const routes = Object.fromEntries( (menu.children || []).map(route => [route.name, route]), ) return ( - - - {({ focus, open }) => { - // I'm sorry, I know this is so cursed. - // I need to migrate out of HeadlessUI to something with change handlers. - onSubmenuOpen(open) - - return ( - - ) - }} - - - // eslint-disable-next-line tailwindcss/no-custom-classname - clsx( - "gql-navbar-menu-items", - "motion-reduce:transition-none", - "focus-visible:outline-none", - open ? "opacity-100" : "opacity-0", - "nextra-scrollbar overflow-visible transition-opacity", - "z-20 rounded-md py-1 text-sm", - // headlessui adds max-height as style, use !important to override - "!max-h-[min(calc(100vh-5rem),256px)]", - ) - } - anchor={{ to: "top start", gap: 21, padding: 16, offset: -8 }} + + + {children} + + {Object.entries(menu.items || {}).map(([key, item]) => ( - - - - {item.title} - - - + , + state: NavigationMenu.Link.State, + ) => ( + + + {item.title} + + + )} + /> ))} - - + + ) } @@ -97,7 +82,13 @@ export function Navbar({ items }: NavBarProps): ReactElement { const activeRoute = useFSRoute() const { menu, setMenu } = useMenu() - const [submenuOpen, setSubmenuOpen] = useState(false) + + useEffect( + () => () => { + document.body.style.overflow = "auto" + }, + [], + ) return (
)}
-
- {items.map(pageOrMenu => { - if (pageOrMenu.display === "hidden") return null + { + document.body.style.overflow = value != null ? "hidden" : "auto" + }} + className="-mx-2 flex overflow-x-auto px-2 py-1.5 xl:absolute xl:left-1/2 xl:-translate-x-1/2" + > + + {items.map(pageOrMenu => { + if (pageOrMenu.display === "hidden") return null - if (pageOrMenu.type === "menu") { - const menu = pageOrMenu as normalizePages.MenuItem - return ( - { - if (typeof window !== "undefined") { - if (open) { - document.body.style.overflow = "hidden" - } else { - document.body.style.overflow = "auto" - } - } - setSubmenuOpen(open) - }} - > - {menu.title} - - ) - } - const page = pageOrMenu as normalizePages.PageItem - let href = page.href || page.route || "#" + if (pageOrMenu.type === "menu") { + const menu = pageOrMenu as normalizePages.MenuItem + return ( + + {menu.title} + + ) + } + const page = pageOrMenu as normalizePages.PageItem + let href = page.href || page.route || "#" - // If it's a directory - if (page.children) { - href = - (page.withIndexPage ? page.route : page.firstChildRoute) || href - } + // If it's a directory + if (page.children) { + href = + (page.withIndexPage ? page.route : page.firstChildRoute) || + href + } - const isActive = - page.route === activeRoute || - activeRoute.startsWith(page.route + "/") + const isActive = + page.route === activeRoute || + activeRoute.startsWith(page.route + "/") - return ( - - {page.title} - - ) - })} -
+ return ( + + + {page.title} + + + ) + })} + + + + + + + + + + {process.env.NEXTRA_SEARCH && renderComponent(themeConfig.search.component, { @@ -218,7 +218,6 @@ export function Navbar({ items }: NavBarProps): ReactElement { )} -
) } @@ -266,14 +265,3 @@ export function NavbarPlaceholder({ /> ) } - -function SubmenuBackdrop({ className }: { className: string }) { - return ( -
- ) -} diff --git a/src/components/radio.tsx b/src/components/radio.tsx index 5ed5af9e53..a4617f9c8c 100644 --- a/src/components/radio.tsx +++ b/src/components/radio.tsx @@ -1,29 +1,31 @@ -import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" +import { Radio as BaseRadio } from "@base-ui-components/react/radio" import { clsx as cn } from "clsx" import { ComponentPropsWithoutRef, ElementRef, forwardRef } from "react" +import { RadioIcon } from "../app/conf/_design-system/pixelarticons/radio-icon" -const RadioGroup = RadioGroupPrimitive.Root +export { RadioGroup } from "@base-ui-components/react/radio-group" -const RadioGroupItem = forwardRef< - ElementRef, - ComponentPropsWithoutRef +export const Radio = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, ...props }, ref) => { return ( - - -
- - + + ) }) -RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName -export { RadioGroup, RadioGroupItem } +Radio.displayName = "Radio" diff --git a/src/components/sidebar.tsx b/src/components/sidebar.tsx index 75716ae395..439ff2e477 100644 --- a/src/components/sidebar.tsx +++ b/src/components/sidebar.tsx @@ -2,11 +2,11 @@ * @file sidebar module extracted from Nextra 3.3.1 */ -import cn from "clsx" +import cn, { clsx } from "clsx" import type { Heading } from "nextra" import { Button } from "nextra/components" import { useFSRoute, useMounted } from "nextra/hooks" -import { ArrowRightIcon, ExpandIcon } from "nextra/icons" +import { ArrowRightIcon } from "nextra/icons" import type { Item, MenuItem, PageItem } from "nextra/normalize-pages" import type { FocusEventHandler, ReactElement } from "react" import { @@ -20,7 +20,6 @@ import { useState, } from "react" import scrollIntoView from "scroll-into-view-if-needed" -import { renderComponent } from "./utils/render-component" import { useActiveAnchor, useMenu, @@ -29,7 +28,11 @@ import { LocaleSwitch, } from "nextra-theme-docs" -import { Anchor } from "../app/conf/_design-system/anchor" +import ArrowBarLeft from "@/app/conf/_design-system/pixelarticons/arrow-bar-left.svg?svgr" +import { Anchor } from "@/app/conf/_design-system/anchor" + +import { renderComponent } from "./utils/render-component" +import { ThemeSwitch } from "./theme-switch" const TreeState: Record = Object.create(null) @@ -470,54 +473,76 @@ export function Sidebar({ {hasMenu && ( -
- - {themeConfig.darkMode && ( -
- {renderComponent(themeConfig.themeSwitch.component, { - lite: !showSidebar || hasI18n, - })} -
- )} - {themeConfig.sidebar.toggleButton && ( - - )} -
+ )} ) } + +export function SidebarFooter({ + showSidebar, + setSidebar, + showToggleAnimation = false, + hasI18n = false, + setToggleAnimation, + className, + hiddenOnMobile = true, +}: { + showSidebar: boolean + setSidebar: (show: boolean) => void + showToggleAnimation?: boolean + hasI18n?: boolean + setToggleAnimation?: (show: boolean) => void + className?: string + hiddenOnMobile?: boolean +}) { + const themeConfig = useThemeConfig() + + return ( +
+ +
+ +
+ {themeConfig.sidebar.toggleButton && ( + + )} +
+ ) +} diff --git a/src/components/theme-switch.tsx b/src/components/theme-switch.tsx new file mode 100644 index 0000000000..cddb11be3f --- /dev/null +++ b/src/components/theme-switch.tsx @@ -0,0 +1,96 @@ +"use client" + +import { clsx } from "clsx" +// @ts-expect-error we use a transitive-dependency and this one is vulnerable to context clash +import { useTheme } from "next-themes" +import { Select } from "@base-ui-components/react/select" +import { useMounted } from "nextra/hooks" + +import MoonIcon from "@/app/conf/_design-system/pixelarticons/moon.svg?svgr" +import SunIcon from "@/app/conf/_design-system/pixelarticons/sun.svg?svgr" +import SystemIcon from "@/app/conf/_design-system/pixelarticons/system.svg?svgr" + +const THEME_OPTIONS = ["light", "dark", "system"] as const +export type ThemeOption = (typeof THEME_OPTIONS)[number] + +const OPTION_ICONS = { + light: SunIcon, + dark: MoonIcon, + system: SystemIcon, +} as const + +const isThemeOption = (value: unknown): value is ThemeOption => + value === "light" || value === "dark" || value === "system" + +export interface ThemeSwitchProps { + lite?: boolean + className?: string +} + +export function ThemeSwitch({ lite, className }: ThemeSwitchProps) { + const { setTheme, resolvedTheme, theme } = useTheme() + const mounted = useMounted() + + const selectedTheme = isThemeOption(theme) ? theme : "light" + const currentTheme = mounted ? selectedTheme : "light" + const resolved = mounted && resolvedTheme === "dark" ? "dark" : "light" + const ResolvedThemeIcon = OPTION_ICONS[resolved] + + return ( + { + if (isThemeOption(value)) { + setTheme(value) + } + }} + > + + + + + + + + + {THEME_OPTIONS.map(option => { + const Icon = OPTION_ICONS[option] + const isSystem = Icon === SystemIcon + return ( + + + + {option} + + + ) + })} + + + + + + ) +} diff --git a/src/components/tools-and-libraries.tsx b/src/components/tools-and-libraries.tsx index 33425021c8..0a92d52d78 100644 --- a/src/components/tools-and-libraries.tsx +++ b/src/components/tools-and-libraries.tsx @@ -1,37 +1,28 @@ -import { - GitHubIcon, - GlobeIcon, - NPMIcon, - StarIcon, - MagnifyingGlassIcon, - RubyGemsIcon, - ChevronLeftIcon, -} from "@/icons" -import { Card } from "@/components" -import NextLink from "next/link" +import { clsx } from "clsx" import NextHead from "next/head" -import { useMounted } from "nextra/hooks" -import Markdown from "markdown-to-jsx" +import NextLink from "next/link" +import { Collapse, getComponents, useConfig } from "nextra-theme-docs" import { evaluate } from "nextra/components" +import { useMounted } from "nextra/hooks" +import { memo, useCallback, useEffect, useMemo, useState } from "react" + +import { Card } from "@/components" +import { CheckboxTree, type CheckboxTreeItem } from "@/components/checkbox-tree" +import { useSearchParamsState } from "@/components/checkbox-tree/use-search-params-state" +import { GitHubIcon, GlobeIcon, NPMIcon, RubyGemsIcon } from "@/icons" +import { MicroMarkdown } from "@/components/micro-markdown" +import { Radio, RadioGroup } from "@/components/radio" -import { - DelimitedArrayParam, - useQueryParam, - withDefault, -} from "use-query-params" -import { - useCallback, - useState, - MouseEvent, - useMemo, - KeyboardEventHandler, - memo, -} from "react" -import { clsx } from "clsx" -import { getComponents } from "nextra-theme-docs" -import { RadioGroup, RadioGroupItem } from "@/components/radio" import { Button } from "@/app/conf/_design-system/button" +import CaretDown from "@/app/conf/_design-system/pixelarticons/caret-down.svg?svgr" +import SearchIcon from "@/app/conf/_design-system/pixelarticons/search.svg?svgr" +import NotesIcon from "@/app/conf/_design-system/pixelarticons/notes.svg?svgr" import { Tag } from "@/app/conf/_design-system/tag" +import ArrowBarLeft from "@/app/conf/_design-system/pixelarticons/arrow-bar-left.svg?svgr" + +import { Breadcrumbs } from "../_design-system/breadcrumbs" + +import { SidebarFooter } from "./sidebar" type PackageInfo = { name: string @@ -59,126 +50,43 @@ type CodePageProps = { }[] } -const TagParam = withDefault(DelimitedArrayParam, []) +const TAG_PARAM_KEY = "tags" + +type Sort = "popularity" | "alphabetical" export function CodePage({ allTags, data }: CodePageProps) { const allTagsMap = useMemo( () => new Map(allTags.map(({ tag, count, name }) => [tag, { count, name }])), + // eslint-disable-next-line react-hooks/exhaustive-deps [], ) - const [search, setSearch] = useState("") - const [queryParamsTags, setTags] = useQueryParam("tags", TagParam) - - const handleQuery = useCallback( - (e: MouseEvent) => { - e.preventDefault() - const tag = e.currentTarget.dataset.tag! - - setTags(prevTags => { - if (prevTags!.includes(tag)) { - return prevTags!.filter(t => t !== tag) - } - return [...prevTags!, tag] - }) - }, - [setTags], - ) - - const mounted = useMounted() - const [isBackspacePressed, setIsBackspacePressed] = useState(false) - - const inputTags = - mounted && - queryParamsTags - .map(tag => [tag, allTagsMap.get(tag as string)?.name]) - .filter(([, name]) => name) - .map(([tag, name]) => ( - - )) - - const handleKeyDown: KeyboardEventHandler = useCallback( - e => { - if (e.key === "Backspace" && !search) { - if (isBackspacePressed) { - setIsBackspacePressed(false) - setTags(prevTags => prevTags!.slice(0, -1)) - } else { - setIsBackspacePressed(true) - } - } - }, - [isBackspacePressed, search], - ) - - const { newData, queryTags } = useMemo(() => { - const newData = mounted - ? data.filter(({ tags }) => { - const matchQueryParamsTags = - !queryParamsTags.length || - (queryParamsTags as string[]).every(tag => tags.includes(tag)) - - return matchQueryParamsTags - }) - : data - - const queryTags = newData - .flatMap(({ tags }) => tags) - .reduce>((acc, tag) => { - acc[tag] ??= 0 - acc[tag] += 1 - return acc - }, {}) - - return { - newData, - queryTags: Object.entries(queryTags) - .filter( - ([tag]) => !mounted || !(queryParamsTags as string[]).includes(tag), - ) - .map(([tag, count]) => ({ - tag, - count, - name: allTagsMap.get(tag)?.name || "", - })), - } - }, [mounted, data, queryParamsTags]) - - const selectedTagsAsString = useMemo(() => { - const tags = queryParamsTags - .slice() - .map(tag => allTagsMap.get(tag as string)?.name ?? tag) - .filter((tag): tag is string => typeof tag === "string") - - if (tags.length === 0) { - return "" - } - - if (tags.length === 1) { - return tags[0] - } + const [sort, setSort] = useState("popularity") - return `${tags.slice(0, -1).join(", ")} and ${tags.slice(-1)[0]}` - }, [queryParamsTags, allTagsMap]) - - const [sort, setSort] = useState("popularity") + const sidebarState = useToolsAndLibrariesSidebarState({ + allTagsMap, + data, + }) let description = `A collection of tools and libraries for GraphQL` let title = "Tools and Libraries | GraphQL" - if (selectedTagsAsString) { - description += ` related to ${selectedTagsAsString}` - title = `${selectedTagsAsString} | ${title}` + if (sidebarState.selectedTagsAsString) { + description += ` related to ${sidebarState.selectedTagsAsString}` + title = `${sidebarState.selectedTagsAsString} | ${title}` } + useEffect(() => { + document.addEventListener("keydown", e => { + if (e.key === "Escape") { + const toggle = document.getElementById("sidebar-toggle") + if (toggle && (toggle as HTMLInputElement).checked) { + toggle.click() + } + } + }) + }, []) + return ( <> @@ -191,171 +99,146 @@ export function CodePage({ allTags, data }: CodePageProps) { key="meta-og-description" /> -
-

Code Using GraphQL

-
-
- {inputTags} -
- setSearch(e.target.value)} - onKeyDown={handleKeyDown} - placeholder="Search..." - className={clsx( - "block grow bg-transparent", - "focus-visible:ring-0 focus-visible:ring-offset-0", - "focus-visible:border-b-primary", - )} +
+ + +
+ - -
-
- {queryTags.map(({ tag, count, name }) => { - const isTagMatchSearch = - !search || name.toLowerCase().includes(search.toLowerCase()) - if (!isTagMatchSearch) return - - return ( - - - {name} ({count}) - - - ) - })} -
-
- -
Sort by:
-
- - -
-
- - -
-
- - {/* todo: add md:*:h-full when the readme opens in a modal */} -
- {(sort === "alphabetical" - ? [...newData].sort((a, b) => - a.frontMatter.name.localeCompare(b.frontMatter.name), - ) - : newData - ).map( - ({ - frontMatter, - tags, - formattedStars, - lastRelease, - license, - compiledSource, - }) => { - const { name, description } = frontMatter - const hasMetadata = formattedStars || lastRelease || license - return ( - -
-
- {name} - -
-
- {tags.map(tag => ( - - - {allTagsMap.get(tag)!.name} - - - ))} -
- - {description} - -
- {hasMetadata && ( -
+ + +
+ {(sort === "alphabetical" + ? [...sidebarState.filteredData].sort((a, b) => + a.frontMatter.name.localeCompare(b.frontMatter.name), + ) + : sidebarState.filteredData + ).map( + ({ + frontMatter, + tags, + formattedStars, + lastRelease, + license, + compiledSource, + }) => { + const { name, description } = frontMatter + const hasMetadata = formattedStars || lastRelease || license + return ( + :not(:last-child)]:border-r [&>:not(:last-child)]:border-neu-500 [&>:not(:last-child)]:pr-5", + "flex h-full flex-col !p-0", + "min-w-0", // hack to avoid overflow when opening details )} > - {lastRelease && Last release {lastRelease}} - {formattedStars && ( - - - {formattedStars} - - )} - {license && {license}} -
- )} -
- - {compiledSource && ( -
- &]:shadow-[-5px_10px_30px_20px_#1b1b1b4d]", - "[[open]>&]:bg-neu-200", - "cursor-pointer", +
+
+ + {name} + + +
+
+ {tags.map(tag => ( + + + {allTagsMap.get(tag)!.name} + + + ))} +
+ +
+ {hasMetadata && ( +
*:not(:first-of-type):before]:[content:'/__']", + )} + > + {formattedStars && ( + + ★{formattedStars} + + )} + {license && ( + + + {license.split(" ").map((word, index) => { + const isLicense = + word.toLowerCase() === "license" + return ( + + + {word} + {" "} + + ) + })} + + )} + {lastRelease && ( + + Last{" "} + + release + {" "} + {lastRelease} + + )} +
+ )} +
+ + {compiledSource && ( +
+ &]:shadow-[-5px_10px_30px_20px_#1b1b1b4d]", + "border-t border-neu-100 dark:border-neu-50 [[open]>&]:bg-neu-200 dark:[[open]>&]:bg-neu-50/25", + "gql-focus-visible cursor-pointer", + )} + > + README + + +
+ +
+
)} - > - README - -
-
- -
-
- )} -
- ) - }, - )} + + ) + }, + )} +
+
+
) @@ -422,3 +305,365 @@ function PackageLinks({ data }: { data: PackageInfo }) { ) } + +interface ToolsAndLibrariesSidebarProps { + treeItems: CheckboxTreeItem[] + searchInputValue: string + setSearchInputValue: (value: string) => void + selectedTags: string[] + updateTags: (tags: string[]) => void +} +function ToolsAndLibrariesSidebar({ + treeItems, + searchInputValue, + setSearchInputValue, + selectedTags, + updateTags, +}: ToolsAndLibrariesSidebarProps) { + const [sidebarShown, setSidebarShown] = useState(true) + return ( +
+ +
+
+ +
+ +
+
+ +
+ ) +} + +function useToolsAndLibrariesSidebarState({ + allTagsMap, + data, +}: { + allTagsMap: Map + data: CodePageProps["data"] +}) { + const mounted = useMounted() + const [searchParams, setSearchParams] = useSearchParamsState() + const selectedTags = searchParams.getAll(TAG_PARAM_KEY) + + const selectedTagsAsString = useMemo(() => { + const tags = selectedTags + .slice() + .map(tag => allTagsMap.get(tag)?.name ?? tag) + .filter((tag): tag is string => typeof tag === "string") + + if (tags.length === 0) { + return "" + } + + if (tags.length === 1) { + return tags[0] + } + + return `${tags.slice(0, -1).join(", ")} and ${tags.slice(-1)[0]}` + }, [allTagsMap, selectedTags]) + + const [searchInputValue, setSearchInputValue] = useState("") + const normalizedSearch = useMemo( + () => searchInputValue.trim().toLowerCase(), + [searchInputValue], + ) + + const searchTokens = useMemo( + () => + normalizedSearch ? normalizedSearch.split(/\s+/).filter(Boolean) : [], + [normalizedSearch], + ) + + const updateTags = useCallback( + (next: string[]) => { + setSearchParams(prev => { + prev.delete(TAG_PARAM_KEY) + next.forEach(tag => { + prev.append(TAG_PARAM_KEY, tag) + }) + }) + }, + [setSearchParams], + ) + + const { filteredData, tagCounts } = useMemo(() => { + const fuzzyMatch = (haystack: string, needle: string) => { + if (!needle) return true + let matchIndex = 0 + for ( + let i = 0; + i < haystack.length && matchIndex < needle.length; + i += 1 + ) { + if (haystack[i] === needle[matchIndex]) { + matchIndex += 1 + } + } + return matchIndex === needle.length + } + + const filteredData = mounted + ? data.filter(item => { + if ( + selectedTags.length && + !selectedTags.every(tag => item.tags.includes(tag)) + ) { + return false + } + + if (!searchTokens.length) { + return true + } + + const tagNames = item.tags + .map(tag => allTagsMap.get(tag)?.name ?? tag) + .join(" ") + + const searchableText = [ + item.frontMatter.name, + item.frontMatter.description, + tagNames, + item.license, + item.lastRelease, + item.formattedStars, + item.frontMatter.github, + item.frontMatter.npm, + item.frontMatter.url, + item.frontMatter.gem, + item.compiledSource, + ] + .filter(Boolean) + .join(" ") + .toLowerCase() + + if (!searchableText) { + return false + } + + return searchTokens.every(token => fuzzyMatch(searchableText, token)) + }) + : data + + const tagCounts = filteredData.reduce>( + (acc, { tags }) => { + tags.forEach(tag => { + acc.set(tag, (acc.get(tag) ?? 0) + 1) + }) + return acc + }, + new Map(), + ) + + return { + filteredData, + tagCounts, + } + }, [mounted, data, allTagsMap, searchTokens, selectedTags]) + + const treeItems = useMemo(() => { + const matchesSearch = (label: string) => + normalizedSearch ? label.toLowerCase().includes(normalizedSearch) : true + + const getName = (tag: string) => allTagsMap.get(tag)?.name ?? tag + const getCount = (tag: string) => tagCounts.get(tag) ?? 0 + + const nonLanguageTags = new Set([ + "client", + "server", + "services", + "tools", + "general", + "gateways-supergraphs", + ]) + + const languages = Array.from(allTagsMap.keys()) + .filter(tag => !nonLanguageTags.has(tag)) + .map(tag => ({ + id: `language-${tag}`, + label: getName(tag), + value: tag, + count: getCount(tag), + })) + .sort((a, b) => a.label.localeCompare(b.label)) + + const baseItems: CheckboxTreeItem[] = [ + { + id: "category", + label: "Category", + children: [ + { + id: "usage-server", + label: getName("server"), + value: "server", + count: getCount("server"), + }, + { + id: "usage-client", + label: getName("client"), + value: "client", + count: getCount("client"), + }, + { + id: "category-tools", + label: getName("tools"), + value: "tools", + count: getCount("tools"), + children: [ + { + id: "category-tools-gateways-supergraphs", + label: getName("gateways-supergraphs"), + value: "gateways-supergraphs", + count: getCount("gateways-supergraphs"), + }, + { + id: "category-tools-general", + label: getName("general"), + value: "general", + count: getCount("general"), + }, + ], + }, + { + id: "category-services", + label: getName("services"), + value: "services", + count: getCount("services"), + }, + ], + }, + ] + + if (languages.length > 0) { + baseItems.push({ + id: "language-support", + label: "Language Support", + children: languages, + }) + } + + const applySearch = (item: CheckboxTreeItem): CheckboxTreeItem => { + const children = item.children?.map(applySearch) ?? [] + const enabledChildren = children.filter(child => !child.disabled) + const disabledChildren = children.filter(child => child.disabled) + const hasEnabledChildren = enabledChildren.length > 0 + const isMatch = matchesSearch(item.label) + const isDisabled = + normalizedSearch && !isMatch && !hasEnabledChildren ? true : false + + const orderedChildren = + children.length > 0 + ? [...enabledChildren, ...disabledChildren] + : undefined + + return { + ...item, + disabled: isDisabled, + ...(orderedChildren + ? { children: orderedChildren } + : { children: undefined }), + } + } + + const processed = baseItems.map(applySearch) + const enabledRoots = processed.filter(item => !item.disabled) + const disabledRoots = processed.filter(item => item.disabled) + + return [...enabledRoots, ...disabledRoots] + }, [allTagsMap, normalizedSearch, tagCounts]) + + return { + filteredData, + searchInputValue, + setSearchInputValue, + selectedTags, + selectedTagsAsString, + treeItems, + updateTags, + } +} + +function MobileSidebarToggle(props: React.HTMLAttributes) { + return ( + + ) +} + +function ToolsAndLibrariesHeader({ + sort, + setSort, +}: { + sort: Sort + setSort: (value: Sort) => void +}) { + const { activePath } = useConfig().normalizePagesResult + + return ( +
+
+ + setSort(value as Sort)} + className="typography-menu flex flex-wrap gap-2 text-sm text-neu-800 dark:text-neu-600 md:flex-nowrap" + > +
Sort by:
+ + +
+
+ +
+ ) +} + +function SidebarOverlay() { + return ( +