feat: sentry bug reports and integration (glitchtip)
This commit is contained in:
		
							
								
								
									
										62
									
								
								App.tsx
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								App.tsx
									
									
									
									
									
								
							@ -1,6 +1,7 @@
 | 
				
			|||||||
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
 | 
					import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
 | 
				
			||||||
import { Dialog } from "@headlessui/react";
 | 
					import { Dialog } from "@headlessui/react";
 | 
				
			||||||
import { Cog6ToothIcon } from "@heroicons/react/24/outline";
 | 
					import { Cog6ToothIcon, ChatBubbleLeftRightIcon } from "@heroicons/react/24/outline";
 | 
				
			||||||
 | 
					import * as Sentry from "@sentry/react";
 | 
				
			||||||
import { Exercise, CheckResult, ExercisePartType } from './types';
 | 
					import { Exercise, CheckResult, ExercisePartType } from './types';
 | 
				
			||||||
import { swaExercises1 } from './data/swa_exercises.1';
 | 
					import { swaExercises1 } from './data/swa_exercises.1';
 | 
				
			||||||
import { swaExercises2 } from './data/swa_exercises.2';
 | 
					import { swaExercises2 } from './data/swa_exercises.2';
 | 
				
			||||||
@ -539,6 +540,16 @@ const allSwaExercises: Exercise[] = [
 | 
				
			|||||||
export default function App() {
 | 
					export default function App() {
 | 
				
			||||||
    const [exercises, setExercises] = useState<Exercise[]>([]);
 | 
					    const [exercises, setExercises] = useState<Exercise[]>([]);
 | 
				
			||||||
    const [currentIndex, setCurrentIndex] = useState(0);
 | 
					    const [currentIndex, setCurrentIndex] = useState(0);
 | 
				
			||||||
 | 
					    const [showFeedback, setShowFeedback] = useState(false);
 | 
				
			||||||
 | 
					    const [feedbackMessage, setFeedbackMessage] = useState('');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const handleFeedbackSubmit = () => {
 | 
				
			||||||
 | 
					        Sentry.captureFeedback({
 | 
				
			||||||
 | 
					            message: feedbackMessage,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        setFeedbackMessage('');
 | 
				
			||||||
 | 
					        setShowFeedback(false);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // --- API KEY STATE ---
 | 
					    // --- API KEY STATE ---
 | 
				
			||||||
    const [apiKey, setApiKey] = useState<string>('');
 | 
					    const [apiKey, setApiKey] = useState<string>('');
 | 
				
			||||||
@ -592,6 +603,14 @@ export default function App() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <div className="min-h-screen flex flex-col p-4 md:p-8">
 | 
					        <div className="min-h-screen flex flex-col p-4 md:p-8">
 | 
				
			||||||
 | 
					            {/* Feedback Button */}
 | 
				
			||||||
 | 
					            <button
 | 
				
			||||||
 | 
					                className="fixed top-4 right-16 z-50 bg-white rounded-full p-2 shadow hover:bg-slate-100 transition"
 | 
				
			||||||
 | 
					                aria-label="Feedback"
 | 
				
			||||||
 | 
					                onClick={() => setShowFeedback(true)}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <ChatBubbleLeftRightIcon className="w-7 h-7 text-slate-600" />
 | 
				
			||||||
 | 
					            </button>
 | 
				
			||||||
            {/* Settings Button */}
 | 
					            {/* Settings Button */}
 | 
				
			||||||
            <button
 | 
					            <button
 | 
				
			||||||
                className="fixed top-4 right-4 z-50 bg-white rounded-full p-2 shadow hover:bg-slate-100 transition"
 | 
					                className="fixed top-4 right-4 z-50 bg-white rounded-full p-2 shadow hover:bg-slate-100 transition"
 | 
				
			||||||
@ -642,6 +661,45 @@ export default function App() {
 | 
				
			|||||||
                    </Dialog.Panel>
 | 
					                    </Dialog.Panel>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </Dialog>
 | 
					            </Dialog>
 | 
				
			||||||
 | 
					            {/* Feedback Modal */}
 | 
				
			||||||
 | 
					            <Dialog open={showFeedback} onClose={() => setShowFeedback(false)} className="relative z-50">
 | 
				
			||||||
 | 
					                <div className="fixed inset-0 bg-black/30" aria-hidden="true" />
 | 
				
			||||||
 | 
					                <div className="fixed inset-0 flex items-center justify-center p-4">
 | 
				
			||||||
 | 
					                    <Dialog.Panel className="bg-white rounded-lg shadow-xl p-8 max-w-md w-full">
 | 
				
			||||||
 | 
					                        <Dialog.Title className="font-bold text-lg mb-4 flex items-center gap-2">
 | 
				
			||||||
 | 
					                            <ChatBubbleLeftRightIcon className="w-6 h-6 text-blue-600" />
 | 
				
			||||||
 | 
								Feedback?
 | 
				
			||||||
 | 
					                        </Dialog.Title>
 | 
				
			||||||
 | 
					                        <div className="mb-4">
 | 
				
			||||||
 | 
								<p>
 | 
				
			||||||
 | 
					                            Hi :)<br/>Hast du Feedback, Aufgabenideen, oder gab es einen Bug? Sag mir gern bescheid, und ich schau, was ich tun kann - yandrik
 | 
				
			||||||
 | 
								    </p>
 | 
				
			||||||
 | 
					                            <textarea
 | 
				
			||||||
 | 
					                                className="w-full border rounded px-3 py-2 text-slate-800"
 | 
				
			||||||
 | 
					                                value={feedbackMessage}
 | 
				
			||||||
 | 
					                                onChange={e => setFeedbackMessage(e.target.value)}
 | 
				
			||||||
 | 
					                                placeholder="Ich finde, dass..."
 | 
				
			||||||
 | 
					                                rows={5}
 | 
				
			||||||
 | 
					                            />
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        <div className="flex justify-end gap-2">
 | 
				
			||||||
 | 
					                            <button
 | 
				
			||||||
 | 
					                                className="px-4 py-2 rounded bg-slate-200 text-slate-700 hover:bg-slate-300"
 | 
				
			||||||
 | 
					                                onClick={() => setShowFeedback(false)}
 | 
				
			||||||
 | 
					                            >
 | 
				
			||||||
 | 
					                                Abbrechen
 | 
				
			||||||
 | 
					                            </button>
 | 
				
			||||||
 | 
					                            <button
 | 
				
			||||||
 | 
					                                className="px-4 py-2 rounded bg-blue-600 text-white font-bold hover:bg-blue-700"
 | 
				
			||||||
 | 
					                                onClick={handleFeedbackSubmit}
 | 
				
			||||||
 | 
					                                disabled={!feedbackMessage.trim()}
 | 
				
			||||||
 | 
					                            >
 | 
				
			||||||
 | 
					                                Senden
 | 
				
			||||||
 | 
					                            </button>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </Dialog.Panel>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </Dialog>
 | 
				
			||||||
            <header className="mb-6 flex-shrink-0">
 | 
					            <header className="mb-6 flex-shrink-0">
 | 
				
			||||||
                <h1 className="text-3xl font-serif font-bold text-slate-800">SWA Trainer</h1>
 | 
					                <h1 className="text-3xl font-serif font-bold text-slate-800">SWA Trainer</h1>
 | 
				
			||||||
                 <p className="text-slate-500">Interaktive Übungen zur Vorbereitung auf die SWA-Klausur</p>
 | 
					                 <p className="text-slate-500">Interaktive Übungen zur Vorbereitung auf die SWA-Klausur</p>
 | 
				
			||||||
@ -673,4 +731,4 @@ export default function App() {
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										11
									
								
								index.tsx
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								index.tsx
									
									
									
									
									
								
							@ -2,6 +2,17 @@
 | 
				
			|||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import ReactDOM from 'react-dom/client';
 | 
					import ReactDOM from 'react-dom/client';
 | 
				
			||||||
import App from './App';
 | 
					import App from './App';
 | 
				
			||||||
 | 
					import * as Sentry from "@sentry/react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Sentry.init({
 | 
				
			||||||
 | 
					  dsn: "https://2851a11b9f1b4715b389979628da322f@glitchtip.yandrik.dev/3",
 | 
				
			||||||
 | 
					  integrations: [
 | 
				
			||||||
 | 
					    Sentry.feedbackIntegration({
 | 
				
			||||||
 | 
					      // Additional SDK configuration goes in here, for example:
 | 
				
			||||||
 | 
					      colorScheme: "system",
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const rootElement = document.getElementById('root');
 | 
					const rootElement = document.getElementById('root');
 | 
				
			||||||
if (!rootElement) {
 | 
					if (!rootElement) {
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@
 | 
				
			|||||||
    "@google/genai": "latest",
 | 
					    "@google/genai": "latest",
 | 
				
			||||||
    "@headlessui/react": "^2.2.4",
 | 
					    "@headlessui/react": "^2.2.4",
 | 
				
			||||||
    "@heroicons/react": "^2.2.0",
 | 
					    "@heroicons/react": "^2.2.0",
 | 
				
			||||||
 | 
					    "@sentry/react": "^9.36.0",
 | 
				
			||||||
    "diff": "5.2.0",
 | 
					    "diff": "5.2.0",
 | 
				
			||||||
    "mermaid": "^11.8.1",
 | 
					    "mermaid": "^11.8.1",
 | 
				
			||||||
    "prismjs": "1.29.0",
 | 
					    "prismjs": "1.29.0",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										80
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										80
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							@ -17,6 +17,9 @@ importers:
 | 
				
			|||||||
      '@heroicons/react':
 | 
					      '@heroicons/react':
 | 
				
			||||||
        specifier: ^2.2.0
 | 
					        specifier: ^2.2.0
 | 
				
			||||||
        version: 2.2.0(react@19.1.0)
 | 
					        version: 2.2.0(react@19.1.0)
 | 
				
			||||||
 | 
					      '@sentry/react':
 | 
				
			||||||
 | 
					        specifier: ^9.36.0
 | 
				
			||||||
 | 
					        version: 9.36.0(react@19.1.0)
 | 
				
			||||||
      diff:
 | 
					      diff:
 | 
				
			||||||
        specifier: 5.2.0
 | 
					        specifier: 5.2.0
 | 
				
			||||||
        version: 5.2.0
 | 
					        version: 5.2.0
 | 
				
			||||||
@ -434,6 +437,36 @@ packages:
 | 
				
			|||||||
    cpu: [x64]
 | 
					    cpu: [x64]
 | 
				
			||||||
    os: [win32]
 | 
					    os: [win32]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@sentry-internal/browser-utils@9.36.0':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-4cGT7c4kr4DMRlcnErgWiL/uLwHvD8A52w6ffJkT/Bj7olSUjFCo8RKIEsDiJqk8No3yfoS6dRbudMZUIFuDTA==}
 | 
				
			||||||
 | 
					    engines: {node: '>=18'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@sentry-internal/feedback@9.36.0':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-0FNKlP2/CWbitAqtLKVPuSqMs8h7jMJm812wSA0gPpCBVMoX+mWEKQh3pYHYekHVfI0muieX9CWYlup0McXaJA==}
 | 
				
			||||||
 | 
					    engines: {node: '>=18'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@sentry-internal/replay-canvas@9.36.0':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-/RrYKvQ80gzjEJoHWMFXqYPlQS8ovEUWG2KeSJQdFtvL+/M/864jfxTA21hgHJC/5GbnOq4V541C1P5YVXwg6w==}
 | 
				
			||||||
 | 
					    engines: {node: '>=18'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@sentry-internal/replay@9.36.0':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-BKxDz4r7bC23d9+zx3a0qkjWLa4zgE/8tXSfcSVYwhczEzTNXYOPxIodYm2IzgVgN9NwUE1m9Cf+/xlKKQLyFQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>=18'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@sentry/browser@9.36.0':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-DCn6VoJCWMjZm5sOfD2+2EgEVdr+H/8Hqs080ruWZo1MZzIUmF1+qnG7AtYeIlm93OydU0PLjvYeWo0zttgGvg==}
 | 
				
			||||||
 | 
					    engines: {node: '>=18'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@sentry/core@9.36.0':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-LU6EmsXPxi8QFkrx0fCqhXicsJA6uUWCD0VrxePZzs+Xs0SgVNDxOgRELVrZa4LPomQJBR5wmm3Duozp9JkHcQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>=18'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@sentry/react@9.36.0':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-SCl3jxZ5fEw+r+7Eo95NgI4cNs7r8Z2ppv0/iyK6qQE/CCTJfBDgnJf6Af/NupO2V/tkfdEJcsb+QNLAu91qvA==}
 | 
				
			||||||
 | 
					    engines: {node: '>=18'}
 | 
				
			||||||
 | 
					    peerDependencies:
 | 
				
			||||||
 | 
					      react: ^16.14.0 || 17.x || 18.x || 19.x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@swc/helpers@0.5.17':
 | 
					  '@swc/helpers@0.5.17':
 | 
				
			||||||
    resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==}
 | 
					    resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -919,6 +952,9 @@ packages:
 | 
				
			|||||||
  hast-util-whitespace@3.0.0:
 | 
					  hast-util-whitespace@3.0.0:
 | 
				
			||||||
    resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
 | 
					    resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  hoist-non-react-statics@3.3.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  html-url-attributes@3.0.1:
 | 
					  html-url-attributes@3.0.1:
 | 
				
			||||||
    resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
 | 
					    resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1209,6 +1245,9 @@ packages:
 | 
				
			|||||||
    peerDependencies:
 | 
					    peerDependencies:
 | 
				
			||||||
      react: ^19.1.0
 | 
					      react: ^19.1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  react-is@16.13.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  react-markdown@9.1.0:
 | 
					  react-markdown@9.1.0:
 | 
				
			||||||
    resolution: {integrity: sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==}
 | 
					    resolution: {integrity: sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==}
 | 
				
			||||||
    peerDependencies:
 | 
					    peerDependencies:
 | 
				
			||||||
@ -1727,6 +1766,41 @@ snapshots:
 | 
				
			|||||||
  '@rollup/rollup-win32-x64-msvc@4.44.2':
 | 
					  '@rollup/rollup-win32-x64-msvc@4.44.2':
 | 
				
			||||||
    optional: true
 | 
					    optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@sentry-internal/browser-utils@9.36.0':
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@sentry/core': 9.36.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@sentry-internal/feedback@9.36.0':
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@sentry/core': 9.36.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@sentry-internal/replay-canvas@9.36.0':
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@sentry-internal/replay': 9.36.0
 | 
				
			||||||
 | 
					      '@sentry/core': 9.36.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@sentry-internal/replay@9.36.0':
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@sentry-internal/browser-utils': 9.36.0
 | 
				
			||||||
 | 
					      '@sentry/core': 9.36.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@sentry/browser@9.36.0':
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@sentry-internal/browser-utils': 9.36.0
 | 
				
			||||||
 | 
					      '@sentry-internal/feedback': 9.36.0
 | 
				
			||||||
 | 
					      '@sentry-internal/replay': 9.36.0
 | 
				
			||||||
 | 
					      '@sentry-internal/replay-canvas': 9.36.0
 | 
				
			||||||
 | 
					      '@sentry/core': 9.36.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@sentry/core@9.36.0': {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@sentry/react@9.36.0(react@19.1.0)':
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@sentry/browser': 9.36.0
 | 
				
			||||||
 | 
					      '@sentry/core': 9.36.0
 | 
				
			||||||
 | 
					      hoist-non-react-statics: 3.3.2
 | 
				
			||||||
 | 
					      react: 19.1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@swc/helpers@0.5.17':
 | 
					  '@swc/helpers@0.5.17':
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      tslib: 2.8.1
 | 
					      tslib: 2.8.1
 | 
				
			||||||
@ -2291,6 +2365,10 @@ snapshots:
 | 
				
			|||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      '@types/hast': 3.0.4
 | 
					      '@types/hast': 3.0.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  hoist-non-react-statics@3.3.2:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      react-is: 16.13.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  html-url-attributes@3.0.1: {}
 | 
					  html-url-attributes@3.0.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  https-proxy-agent@7.0.6:
 | 
					  https-proxy-agent@7.0.6:
 | 
				
			||||||
@ -2814,6 +2892,8 @@ snapshots:
 | 
				
			|||||||
      react: 19.1.0
 | 
					      react: 19.1.0
 | 
				
			||||||
      scheduler: 0.26.0
 | 
					      scheduler: 0.26.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  react-is@16.13.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  react-markdown@9.1.0(@types/react@19.1.8)(react@19.1.0):
 | 
					  react-markdown@9.1.0(@types/react@19.1.8)(react@19.1.0):
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      '@types/hast': 3.0.4
 | 
					      '@types/hast': 3.0.4
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user