mirror of
				https://github.com/NginxProxyManager/nginx-proxy-manager.git
				synced 2025-10-31 15:53:33 +00:00 
			
		
		
		
	Tidy up
- Add help docs for most sections - Add translations documentation - Fix up todos - Remove german translation
This commit is contained in:
		| @@ -8,14 +8,11 @@ | |||||||
|  |  | ||||||
| const allLocales = [ | const allLocales = [ | ||||||
| 	["en", "en-US"], | 	["en", "en-US"], | ||||||
| 	["de", "de-DE"], |  | ||||||
| 	["fa", "fa-IR"], | 	["fa", "fa-IR"], | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
| const ignoreUnused = [ | const ignoreUnused = [ | ||||||
| 	/^capability\..*$/, | 	/^.*$/, | ||||||
| 	/^status\..*$/, |  | ||||||
| 	/^type\..*$/, |  | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
| const { spawnSync } = require("child_process"); | const { spawnSync } = require("child_process"); | ||||||
| @@ -119,19 +116,9 @@ const compareLocale = (locale) => { | |||||||
| const checkForMissing = (locale) => { | const checkForMissing = (locale) => { | ||||||
| 	allKeys.forEach((key) => { | 	allKeys.forEach((key) => { | ||||||
| 		if (typeof locale.data[key] === "undefined") { | 		if (typeof locale.data[key] === "undefined") { | ||||||
| 			let ignored = false; | 			allWarnings.push( | ||||||
| 			ignoreMissing.map((regex) => { | 				"WARN: `" + locale[0] + "` does not contain item: `" + key + "`", | ||||||
| 				if (key.match(regex)) { | 			); | ||||||
| 					ignored = true; |  | ||||||
| 				} |  | ||||||
| 				return null; |  | ||||||
| 			}); |  | ||||||
|  |  | ||||||
| 			if (!ignored) { |  | ||||||
| 				allWarnings.push( |  | ||||||
| 					"WARN: `" + locale[0] + "` does not contain item: `" + key + "`", |  | ||||||
| 				); |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 		return null; | 		return null; | ||||||
| 	}); | 	}); | ||||||
|   | |||||||
| @@ -33,6 +33,7 @@ | |||||||
| 		"react-bootstrap": "^2.10.10", | 		"react-bootstrap": "^2.10.10", | ||||||
| 		"react-dom": "^19.2.0", | 		"react-dom": "^19.2.0", | ||||||
| 		"react-intl": "^7.1.14", | 		"react-intl": "^7.1.14", | ||||||
|  | 		"react-markdown": "^10.1.0", | ||||||
| 		"react-router-dom": "^7.9.4", | 		"react-router-dom": "^7.9.4", | ||||||
| 		"react-select": "^5.10.2", | 		"react-select": "^5.10.2", | ||||||
| 		"react-toastify": "^11.0.5", | 		"react-toastify": "^11.0.5", | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ import type { AccessList } from "./models"; | |||||||
| export async function createAccessList(item: AccessList): Promise<AccessList> { | export async function createAccessList(item: AccessList): Promise<AccessList> { | ||||||
| 	return await api.post({ | 	return await api.post({ | ||||||
| 		url: "/nginx/access-lists", | 		url: "/nginx/access-lists", | ||||||
| 		// todo: only use whitelist of fields for this data |  | ||||||
| 		data: item, | 		data: item, | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ import type { DeadHost } from "./models"; | |||||||
| export async function createDeadHost(item: DeadHost): Promise<DeadHost> { | export async function createDeadHost(item: DeadHost): Promise<DeadHost> { | ||||||
| 	return await api.post({ | 	return await api.post({ | ||||||
| 		url: "/nginx/dead-hosts", | 		url: "/nginx/dead-hosts", | ||||||
| 		// todo: only use whitelist of fields for this data |  | ||||||
| 		data: item, | 		data: item, | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ import type { ProxyHost } from "./models"; | |||||||
| export async function createProxyHost(item: ProxyHost): Promise<ProxyHost> { | export async function createProxyHost(item: ProxyHost): Promise<ProxyHost> { | ||||||
| 	return await api.post({ | 	return await api.post({ | ||||||
| 		url: "/nginx/proxy-hosts", | 		url: "/nginx/proxy-hosts", | ||||||
| 		// todo: only use whitelist of fields for this data |  | ||||||
| 		data: item, | 		data: item, | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ import type { RedirectionHost } from "./models"; | |||||||
| export async function createRedirectionHost(item: RedirectionHost): Promise<RedirectionHost> { | export async function createRedirectionHost(item: RedirectionHost): Promise<RedirectionHost> { | ||||||
| 	return await api.post({ | 	return await api.post({ | ||||||
| 		url: "/nginx/redirection-hosts", | 		url: "/nginx/redirection-hosts", | ||||||
| 		// todo: only use whitelist of fields for this data |  | ||||||
| 		data: item, | 		data: item, | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ import type { Stream } from "./models"; | |||||||
| export async function createStream(item: Stream): Promise<Stream> { | export async function createStream(item: Stream): Promise<Stream> { | ||||||
| 	return await api.post({ | 	return await api.post({ | ||||||
| 		url: "/nginx/streams", | 		url: "/nginx/streams", | ||||||
| 		// todo: only use whitelist of fields for this data |  | ||||||
| 		data: item, | 		data: item, | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -18,7 +18,6 @@ export interface NewUser { | |||||||
| export async function createUser(item: NewUser, noAuth?: boolean): Promise<User> { | export async function createUser(item: NewUser, noAuth?: boolean): Promise<User> { | ||||||
| 	return await api.post({ | 	return await api.post({ | ||||||
| 		url: "/users", | 		url: "/users", | ||||||
| 		// todo: only use whitelist of fields for this data |  | ||||||
| 		data: item, | 		data: item, | ||||||
| 		noAuth, | 		noAuth, | ||||||
| 	}); | 	}); | ||||||
|   | |||||||
| @@ -1,5 +1,4 @@ | |||||||
| import { createIntl, createIntlCache } from "react-intl"; | import { createIntl, createIntlCache } from "react-intl"; | ||||||
| import langDe from "./lang/de.json"; |  | ||||||
| import langEn from "./lang/en.json"; | import langEn from "./lang/en.json"; | ||||||
| import langFa from "./lang/fa.json"; | import langFa from "./lang/fa.json"; | ||||||
| import langList from "./lang/lang-list.json"; | import langList from "./lang/lang-list.json"; | ||||||
| @@ -9,15 +8,12 @@ import langList from "./lang/lang-list.json"; | |||||||
| // Remember when adding to this list, also update check-locales.js script | // Remember when adding to this list, also update check-locales.js script | ||||||
| const localeOptions = [ | const localeOptions = [ | ||||||
| 	["en", "en-US"], | 	["en", "en-US"], | ||||||
| 	["de", "de-DE"], |  | ||||||
| 	["fa", "fa-IR"], | 	["fa", "fa-IR"], | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
| const loadMessages = (locale?: string): typeof langList & typeof langEn => { | const loadMessages = (locale?: string): typeof langList & typeof langEn => { | ||||||
| 	const thisLocale = locale || "en"; | 	const thisLocale = locale || "en"; | ||||||
| 	switch (thisLocale.slice(0, 2)) { | 	switch (thisLocale.slice(0, 2)) { | ||||||
| 		case "de": |  | ||||||
| 			return Object.assign({}, langList, langEn, langDe); |  | ||||||
| 		case "fa": | 		case "fa": | ||||||
| 			return Object.assign({}, langList, langEn, langFa); | 			return Object.assign({}, langList, langEn, langFa); | ||||||
| 		default: | 		default: | ||||||
| @@ -27,9 +23,6 @@ const loadMessages = (locale?: string): typeof langList & typeof langEn => { | |||||||
|  |  | ||||||
| const getFlagCodeForLocale = (locale?: string) => { | const getFlagCodeForLocale = (locale?: string) => { | ||||||
| 	switch (locale) { | 	switch (locale) { | ||||||
| 		case "de-DE": |  | ||||||
| 		case "de": |  | ||||||
| 			return "DE"; |  | ||||||
| 		case "fa-IR": | 		case "fa-IR": | ||||||
| 		case "fa": | 		case "fa": | ||||||
| 			return "IR"; | 			return "IR"; | ||||||
|   | |||||||
| @@ -1,23 +1,48 @@ | |||||||
| # Internationalisation support | # Internationalisation support | ||||||
|  |  | ||||||
|  | ## Before you start | ||||||
|  |  | ||||||
|  | It's highly recommended that you spin up a development instance of this project | ||||||
|  | on your docker capable server. It's pretty easy: | ||||||
|  |  | ||||||
|  | ```bash | ||||||
|  | git clone https://github.com/NginxProxyManager/nginx-proxy-manager.git | ||||||
|  | cd nginx-proxy-manager | ||||||
|  | ./scripts/start-dev -f | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Then after a while, you can access http://yourserverip:3081 | ||||||
|  |  | ||||||
|  | This stack will watch the file system for changes, especially to language files, | ||||||
|  | and reload the site you have open in the browser. | ||||||
|  |  | ||||||
|  |  | ||||||
| ## Adding new translations | ## Adding new translations | ||||||
|  |  | ||||||
| Modify the files in the `src` folder. Follow the conventions already there. | Modify the files in the `src` folder. Follow the conventions already there. | ||||||
|  |  | ||||||
|  | When the development stack is running, it will sort the locale lang files | ||||||
|  | for you when you save. | ||||||
|  |  | ||||||
|  |  | ||||||
| ## After making changes | ## After making changes | ||||||
|  |  | ||||||
| You will need to run `yarn locale-compile` in this frontend folder for | If you're NOT running the development stack, you will need to run | ||||||
|  | `yarn locale-compile` in the `frontend` folder for | ||||||
| the new translations to be compiled into the `lang` folder. | the new translations to be compiled into the `lang` folder. | ||||||
|  |  | ||||||
| When running in dev mode, this should automatically happen within Vite. |  | ||||||
|  | ## Adding a whole new language | ||||||
|  |  | ||||||
|  | There's a fair bit you'll need to touch. Here's a list that may | ||||||
|  | not be complete by the time you're reading this: | ||||||
|  |  | ||||||
|  | - frontend/src/locale/src/[yourlang].json | ||||||
|  | - frontend/src/locale/src/lang-list.json | ||||||
|  | - frontend/src/locale/src/HelpDoc/* | ||||||
|  | - frontend/src/locale/IntlProvider.tsx | ||||||
|  |  | ||||||
|  |  | ||||||
| ## Checking for missing translations in other languages | ## Checking for missing translations in languages | ||||||
|  |  | ||||||
| Run `node check-locales.cjs` in this frontend folder. | Run `node check-locales.cjs` in this frontend folder. | ||||||
|  |  | ||||||
|  |  | ||||||
| ## Adding new languages |  | ||||||
|  |  | ||||||
| todo |  | ||||||
|   | |||||||
| @@ -1,3 +0,0 @@ | |||||||
| { |  | ||||||
|   "dashboard": "Armaturenbrett" |  | ||||||
| } |  | ||||||
| @@ -100,7 +100,11 @@ | |||||||
|   "error.invalid-auth": "Invalid email or password", |   "error.invalid-auth": "Invalid email or password", | ||||||
|   "error.invalid-domain": "Invalid domain: {domain}", |   "error.invalid-domain": "Invalid domain: {domain}", | ||||||
|   "error.invalid-email": "Invalid email address", |   "error.invalid-email": "Invalid email address", | ||||||
|  |   "error.max-character-length": "Maximum length is {max} character{max, plural, one {} other {s}}", | ||||||
|   "error.max-domains": "Too many domains, max is {max}", |   "error.max-domains": "Too many domains, max is {max}", | ||||||
|  |   "error.maximum": "Maximum is {max}", | ||||||
|  |   "error.min-character-length": "Minimum length is {min} character{min, plural, one {} other {s}}", | ||||||
|  |   "error.minimum": "Minimum is {min}", | ||||||
|   "error.passwords-must-match": "Passwords must match", |   "error.passwords-must-match": "Passwords must match", | ||||||
|   "error.required": "This is required", |   "error.required": "This is required", | ||||||
|   "expires.on": "Expires: {date}", |   "expires.on": "Expires: {date}", | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								frontend/src/locale/src/HelpDoc/en/AccessLists.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								frontend/src/locale/src/HelpDoc/en/AccessLists.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | ## What is an Access List? | ||||||
|  |  | ||||||
|  | Access Lists provide a blacklist or whitelist of specific client IP addresses along with authentication for the Proxy Hosts via Basic HTTP Authentication. | ||||||
|  |  | ||||||
|  | You can configure multiple client rules, usernames and passwords for a single Access List and then apply that to one or more _Proxy Hosts_. | ||||||
|  |  | ||||||
|  | This is most useful for forwarded web services that do not have authentication mechanisms built in or when you want to protect from unknown clients. | ||||||
							
								
								
									
										32
									
								
								frontend/src/locale/src/HelpDoc/en/Certificates.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								frontend/src/locale/src/HelpDoc/en/Certificates.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | ## Certificates Help | ||||||
|  |  | ||||||
|  | ### HTTP Certificate | ||||||
|  |  | ||||||
|  | A HTTP validated certificate means Let's Encrypt servers will | ||||||
|  | attempt to reach your domains over HTTP (not HTTPS!) and if successful, they | ||||||
|  | will issue your certificate. | ||||||
|  |  | ||||||
|  | For this method, you will have to have a _Proxy Host_ created for your domains(s) that | ||||||
|  | is accessible with HTTP and pointing to this Nginx installation. After a certificate | ||||||
|  | has been given, you can modify the _Proxy Host_ to also use this certificate for HTTPS | ||||||
|  | connections. However, the _Proxy Host_ will still need to be configured for HTTP access | ||||||
|  | in order for the certificate to renew. | ||||||
|  |  | ||||||
|  | This process _does not_ support wildcard domains. | ||||||
|  |  | ||||||
|  | ### DNS Certificate | ||||||
|  |  | ||||||
|  | A DNS validated certificate requires you to use a DNS Provider plugin. This DNS | ||||||
|  | Provider will be used to create temporary records on your domain and then Let's | ||||||
|  | Encrypt will query those records to be sure you're the owner and if successful, they | ||||||
|  | will issue your certificate. | ||||||
|  |  | ||||||
|  | You do not need a _Proxy Host_ to be created prior to requesting this type of | ||||||
|  | certificate. Nor do you need to have your _Proxy Host_ configured for HTTP access. | ||||||
|  |  | ||||||
|  | This process _does_ support wildcard domains. | ||||||
|  |  | ||||||
|  | ### Custom Certificate | ||||||
|  |  | ||||||
|  | Use this option to upload your own SSL Certificate, as provided by your own | ||||||
|  | Certificate Authority. | ||||||
							
								
								
									
										10
									
								
								frontend/src/locale/src/HelpDoc/en/DeadHosts.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								frontend/src/locale/src/HelpDoc/en/DeadHosts.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | ## What is a 404 Host? | ||||||
|  |  | ||||||
|  | A 404 Host is simply a host setup that shows a 404 page. | ||||||
|  |  | ||||||
|  | This can be useful when your domain is listed in search engines and you want | ||||||
|  | to provide a nicer error page or specifically to tell the search indexers that | ||||||
|  | the domain pages no longer exist. | ||||||
|  |  | ||||||
|  | Another benefit of having this host is to track the logs for hits to it and | ||||||
|  | view the referrers. | ||||||
							
								
								
									
										7
									
								
								frontend/src/locale/src/HelpDoc/en/ProxyHosts.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								frontend/src/locale/src/HelpDoc/en/ProxyHosts.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | ## What is a Proxy Host? | ||||||
|  |  | ||||||
|  | A Proxy Host is the incoming endpoint for a web service that you want to forward. | ||||||
|  |  | ||||||
|  | It provides optional SSL termination for your service that might not have SSL support built in. | ||||||
|  |  | ||||||
|  | Proxy Hosts are the most common use for the Nginx Proxy Manager. | ||||||
							
								
								
									
										7
									
								
								frontend/src/locale/src/HelpDoc/en/RedirectionHosts.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								frontend/src/locale/src/HelpDoc/en/RedirectionHosts.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | ## What is a Redirection Host? | ||||||
|  |  | ||||||
|  | A Redirection Host will redirect requests from the incoming domain and push the | ||||||
|  | viewer to another domain. | ||||||
|  |  | ||||||
|  | The most common reason to use this type of host is when your website changes | ||||||
|  | domains but you still have search engine or referrer links pointing to the old domain. | ||||||
							
								
								
									
										6
									
								
								frontend/src/locale/src/HelpDoc/en/Streams.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								frontend/src/locale/src/HelpDoc/en/Streams.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | ## What is a Stream? | ||||||
|  |  | ||||||
|  | A relatively new feature for Nginx, a Stream will serve to forward TCP/UDP | ||||||
|  | traffic directly to another computer on the network. | ||||||
|  |  | ||||||
|  | If you're running game servers, FTP or SSH servers this can come in handy. | ||||||
							
								
								
									
										6
									
								
								frontend/src/locale/src/HelpDoc/en/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								frontend/src/locale/src/HelpDoc/en/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | export * as AccessLists from "./AccessLists.md"; | ||||||
|  | export * as Certificates from "./Certificates.md"; | ||||||
|  | export * as DeadHosts from "./DeadHosts.md"; | ||||||
|  | export * as ProxyHosts from "./ProxyHosts.md"; | ||||||
|  | export * as RedirectionHosts from "./RedirectionHosts.md"; | ||||||
|  | export * as Streams from "./Streams.md"; | ||||||
							
								
								
									
										20
									
								
								frontend/src/locale/src/HelpDoc/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								frontend/src/locale/src/HelpDoc/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | // import * as de from "./de/index"; | ||||||
|  | // import * as fa from "./fa/index"; | ||||||
|  | import * as en from "./en/index"; | ||||||
|  |  | ||||||
|  | const items: any = { en }; | ||||||
|  |  | ||||||
|  | const fallbackLang = "en"; | ||||||
|  |  | ||||||
|  | export const getHelpFile = (lang: string, section: string): string => { | ||||||
|  | 	if (typeof items[lang] !== "undefined" && typeof items[lang][section] !== "undefined") { | ||||||
|  | 		return items[lang][section].default; | ||||||
|  | 	} | ||||||
|  | 	// Fallback to English | ||||||
|  | 	if (typeof items[fallbackLang] !== "undefined" && typeof items[fallbackLang][section] !== "undefined") { | ||||||
|  | 		return items[fallbackLang][section].default; | ||||||
|  | 	} | ||||||
|  | 	throw new Error(`Cannot load help doc for ${lang}-${section}`); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default items; | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| { |  | ||||||
| 	"dashboard": { |  | ||||||
| 		"defaultMessage": "Armaturenbrett" |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -302,9 +302,21 @@ | |||||||
| 	"error.invalid-email": { | 	"error.invalid-email": { | ||||||
| 		"defaultMessage": "Invalid email address" | 		"defaultMessage": "Invalid email address" | ||||||
| 	}, | 	}, | ||||||
|  | 	"error.max-character-length": { | ||||||
|  | 		"defaultMessage": "Maximum length is {max} character{max, plural, one {} other {s}}" | ||||||
|  | 	}, | ||||||
| 	"error.max-domains": { | 	"error.max-domains": { | ||||||
| 		"defaultMessage": "Too many domains, max is {max}" | 		"defaultMessage": "Too many domains, max is {max}" | ||||||
| 	}, | 	}, | ||||||
|  | 	"error.maximum": { | ||||||
|  | 		"defaultMessage": "Maximum is {max}" | ||||||
|  | 	}, | ||||||
|  | 	"error.min-character-length": { | ||||||
|  | 		"defaultMessage": "Minimum length is {min} character{min, plural, one {} other {s}}" | ||||||
|  | 	}, | ||||||
|  | 	"error.minimum": { | ||||||
|  | 		"defaultMessage": "Minimum is {min}" | ||||||
|  | 	}, | ||||||
| 	"error.passwords-must-match": { | 	"error.passwords-must-match": { | ||||||
| 		"defaultMessage": "Passwords must match" | 		"defaultMessage": "Passwords must match" | ||||||
| 	}, | 	}, | ||||||
|   | |||||||
| @@ -52,7 +52,27 @@ const DeleteConfirmModal = EasyModal.create( | |||||||
| 					<Alert variant="danger" show={!!error} onClose={() => setError(null)} dismissible> | 					<Alert variant="danger" show={!!error} onClose={() => setError(null)} dismissible> | ||||||
| 						{error} | 						{error} | ||||||
| 					</Alert> | 					</Alert> | ||||||
| 					{children} | 					<div className="text-center mb-3"> | ||||||
|  | 						<svg | ||||||
|  | 							role="img" | ||||||
|  | 							aria-label="warning icon" | ||||||
|  | 							xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 							className="icon mb-2 text-danger icon-lg" | ||||||
|  | 							width="24" | ||||||
|  | 							height="24" | ||||||
|  | 							viewBox="0 0 24 24" | ||||||
|  | 							stroke-width="2" | ||||||
|  | 							stroke="currentColor" | ||||||
|  | 							fill="none" | ||||||
|  | 							stroke-linecap="round" | ||||||
|  | 							stroke-linejoin="round" | ||||||
|  | 						> | ||||||
|  | 							<path stroke="none" d="M0 0h24v24H0z" fill="none" /> | ||||||
|  | 							<path d="M12 9v2m0 4v.01" /> | ||||||
|  | 							<path d="M5 19h14a2 2 0 0 0 1.84 -2.75l-7.1 -12.25a2 2 0 0 0 -3.5 0l-7.1 12.25a2 2 0 0 0 1.75 2.75" /> | ||||||
|  | 						</svg> | ||||||
|  | 					</div> | ||||||
|  | 					<div className="text-center mb-3">{children}</div> | ||||||
| 				</Modal.Body> | 				</Modal.Body> | ||||||
| 				<Modal.Footer> | 				<Modal.Footer> | ||||||
| 					<Button data-bs-dismiss="modal" onClick={remove} disabled={isSubmitting}> | 					<Button data-bs-dismiss="modal" onClick={remove} disabled={isSubmitting}> | ||||||
|   | |||||||
							
								
								
									
										54
									
								
								frontend/src/modals/HelpModal.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								frontend/src/modals/HelpModal.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | import cn from "classnames"; | ||||||
|  | import EasyModal, { type InnerModalProps } from "ez-modal-react"; | ||||||
|  | import { useEffect, useState } from "react"; | ||||||
|  | import Modal from "react-bootstrap/Modal"; | ||||||
|  | import ReactMarkdown from "react-markdown"; | ||||||
|  | import { Button } from "src/components"; | ||||||
|  | import { getLocale, T } from "src/locale"; | ||||||
|  | import { getHelpFile } from "src/locale/src/HelpDoc"; | ||||||
|  |  | ||||||
|  | interface Props extends InnerModalProps { | ||||||
|  | 	section: string; | ||||||
|  | 	color?: string; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const showHelpModal = (section: string, color?: string) => { | ||||||
|  | 	EasyModal.show(HelpModal, { section, color }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const HelpModal = EasyModal.create(({ section, color, visible, remove }: Props) => { | ||||||
|  | 	const [markdownText, setMarkdownText] = useState(""); | ||||||
|  | 	const lang = getLocale(true); | ||||||
|  |  | ||||||
|  | 	useEffect(() => { | ||||||
|  | 		try { | ||||||
|  | 			const docFile = getHelpFile(lang, section) as any; | ||||||
|  | 			fetch(docFile) | ||||||
|  | 				.then((response) => response.text()) | ||||||
|  | 				.then(setMarkdownText); | ||||||
|  | 		} catch (ex: any) { | ||||||
|  | 			setMarkdownText(`**ERROR:** ${ex.message}`); | ||||||
|  | 		} | ||||||
|  | 	}, [lang, section]); | ||||||
|  |  | ||||||
|  | 	return ( | ||||||
|  | 		<Modal show={visible} onHide={remove}> | ||||||
|  | 			<Modal.Body> | ||||||
|  | 				<ReactMarkdown>{markdownText}</ReactMarkdown> | ||||||
|  | 			</Modal.Body> | ||||||
|  | 			<Modal.Footer> | ||||||
|  | 				<Button | ||||||
|  | 					type="button" | ||||||
|  | 					actionType="primary" | ||||||
|  | 					className={cn("ms-auto", color ? `btn-${color}` : null)} | ||||||
|  | 					data-bs-dismiss="modal" | ||||||
|  | 					onClick={remove} | ||||||
|  | 				> | ||||||
|  | 					<T id="action.close" /> | ||||||
|  | 				</Button> | ||||||
|  | 			</Modal.Footer> | ||||||
|  | 		</Modal> | ||||||
|  | 	); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | export { showHelpModal }; | ||||||
| @@ -5,6 +5,7 @@ export * from "./DeadHostModal"; | |||||||
| export * from "./DeleteConfirmModal"; | export * from "./DeleteConfirmModal"; | ||||||
| export * from "./DNSCertificateModal"; | export * from "./DNSCertificateModal"; | ||||||
| export * from "./EventDetailsModal"; | export * from "./EventDetailsModal"; | ||||||
|  | export * from "./HelpModal"; | ||||||
| export * from "./HTTPCertificateModal"; | export * from "./HTTPCertificateModal"; | ||||||
| export * from "./PermissionsModal"; | export * from "./PermissionsModal"; | ||||||
| export * from "./ProxyHostModal"; | export * from "./ProxyHostModal"; | ||||||
|   | |||||||
| @@ -11,12 +11,10 @@ const validateString = (minLength = 0, maxLength = 0) => { | |||||||
| 			return intl.formatMessage({ id: "error.required" }); | 			return intl.formatMessage({ id: "error.required" }); | ||||||
| 		} | 		} | ||||||
| 		if (minLength && value.length < minLength) { | 		if (minLength && value.length < minLength) { | ||||||
| 			// TODO: i18n | 			return intl.formatMessage({ id: "error.min-character-length" }, { min: minLength }); | ||||||
| 			return `Minimum length is ${minLength} character${minLength === 1 ? "" : "s"}`; |  | ||||||
| 		} | 		} | ||||||
| 		if (maxLength && (typeof value === "undefined" || value.length > maxLength)) { | 		if (maxLength && (typeof value === "undefined" || value.length > maxLength)) { | ||||||
| 			// TODO: i18n | 			return intl.formatMessage({ id: "error.max-character-length" }, { max: maxLength }); | ||||||
| 			return `Maximum length is ${maxLength} character${maxLength === 1 ? "" : "s"}`; |  | ||||||
| 		} | 		} | ||||||
| 	}; | 	}; | ||||||
| }; | }; | ||||||
| @@ -33,12 +31,10 @@ const validateNumber = (min = -1, max = -1) => { | |||||||
| 			return intl.formatMessage({ id: "error.required" }); | 			return intl.formatMessage({ id: "error.required" }); | ||||||
| 		} | 		} | ||||||
| 		if (min > -1 && int < min) { | 		if (min > -1 && int < min) { | ||||||
| 			// TODO: i18n | 			return intl.formatMessage({ id: "error.minimum" }, { min }); | ||||||
| 			return `Minimum is ${min}`; |  | ||||||
| 		} | 		} | ||||||
| 		if (max > -1 && int > max) { | 		if (max > -1 && int > max) { | ||||||
| 			// TODO: i18n | 			return intl.formatMessage({ id: "error.maximum" }, { max }); | ||||||
| 			return `Maximum is ${max}`; |  | ||||||
| 		} | 		} | ||||||
| 	}; | 	}; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -53,7 +53,7 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete, | |||||||
| 				cell: (info: any) => <T id="proxy-hosts.count" data={{ count: info.getValue() }} />, | 				cell: (info: any) => <T id="proxy-hosts.count" data={{ count: info.getValue() }} />, | ||||||
| 			}), | 			}), | ||||||
| 			columnHelper.display({ | 			columnHelper.display({ | ||||||
| 				id: "id", // todo: not needed for a display? | 				id: "id", | ||||||
| 				cell: (info: any) => { | 				cell: (info: any) => { | ||||||
| 					return ( | 					return ( | ||||||
| 						<span className="dropdown"> | 						<span className="dropdown"> | ||||||
|   | |||||||
| @@ -1,11 +1,11 @@ | |||||||
| import { IconSearch } from "@tabler/icons-react"; | import { IconHelp, IconSearch } from "@tabler/icons-react"; | ||||||
| import { useState } from "react"; | import { useState } from "react"; | ||||||
| import Alert from "react-bootstrap/Alert"; | import Alert from "react-bootstrap/Alert"; | ||||||
| import { deleteAccessList } from "src/api/backend"; | import { deleteAccessList } from "src/api/backend"; | ||||||
| import { Button, LoadingPage } from "src/components"; | import { Button, LoadingPage } from "src/components"; | ||||||
| import { useAccessLists } from "src/hooks"; | import { useAccessLists } from "src/hooks"; | ||||||
| import { T } from "src/locale"; | import { T } from "src/locale"; | ||||||
| import { showAccessListModal, showDeleteConfirmModal } from "src/modals"; | import { showAccessListModal, showDeleteConfirmModal, showHelpModal } from "src/modals"; | ||||||
| import { showObjectSuccess } from "src/notifications"; | import { showObjectSuccess } from "src/notifications"; | ||||||
| import Table from "./Table"; | import Table from "./Table"; | ||||||
|  |  | ||||||
| @@ -47,9 +47,10 @@ export default function TableWrapper() { | |||||||
| 								<T id="access-lists" /> | 								<T id="access-lists" /> | ||||||
| 							</h2> | 							</h2> | ||||||
| 						</div> | 						</div> | ||||||
| 						{data?.length ? ( |  | ||||||
| 							<div className="col-md-auto col-sm-12"> | 						<div className="col-md-auto col-sm-12"> | ||||||
| 								<div className="ms-auto d-flex flex-wrap btn-list"> | 							<div className="ms-auto d-flex flex-wrap btn-list"> | ||||||
|  | 								{data?.length ? ( | ||||||
| 									<div className="input-group input-group-flat w-auto"> | 									<div className="input-group input-group-flat w-auto"> | ||||||
| 										<span className="input-group-text input-group-text-sm"> | 										<span className="input-group-text input-group-text-sm"> | ||||||
| 											<IconSearch size={16} /> | 											<IconSearch size={16} /> | ||||||
| @@ -62,12 +63,17 @@ export default function TableWrapper() { | |||||||
| 											onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} | 											onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} | ||||||
| 										/> | 										/> | ||||||
| 									</div> | 									</div> | ||||||
|  | 								) : null} | ||||||
|  | 								<Button size="sm" onClick={() => showHelpModal("AccessLists", "cyan")}> | ||||||
|  | 									<IconHelp size={20} /> | ||||||
|  | 								</Button> | ||||||
|  | 								{data?.length ? ( | ||||||
| 									<Button size="sm" className="btn-cyan" onClick={() => showAccessListModal("new")}> | 									<Button size="sm" className="btn-cyan" onClick={() => showAccessListModal("new")}> | ||||||
| 										<T id="object.add" tData={{ object: "access-list" }} /> | 										<T id="object.add" tData={{ object: "access-list" }} /> | ||||||
| 									</Button> | 									</Button> | ||||||
| 								</div> | 								) : null} | ||||||
| 							</div> | 							</div> | ||||||
| 						) : null} | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
| 				</div> | 				</div> | ||||||
| 				<Table | 				<Table | ||||||
| @@ -77,7 +83,7 @@ export default function TableWrapper() { | |||||||
| 					onEdit={(id: number) => showAccessListModal(id)} | 					onEdit={(id: number) => showAccessListModal(id)} | ||||||
| 					onDelete={(id: number) => | 					onDelete={(id: number) => | ||||||
| 						showDeleteConfirmModal({ | 						showDeleteConfirmModal({ | ||||||
| 							title: "access.delete.title", | 							title: <T id="object.delete" tData={{ object: "access-list" }} />, | ||||||
| 							onConfirm: () => handleDelete(id), | 							onConfirm: () => handleDelete(id), | ||||||
| 							invalidations: [["access-lists"], ["access-list", id]], | 							invalidations: [["access-lists"], ["access-list", id]], | ||||||
| 							children: <T id="object.delete.content" tData={{ object: "access-list" }} />, | 							children: <T id="object.delete.content" tData={{ object: "access-list" }} />, | ||||||
|   | |||||||
| @@ -1,14 +1,15 @@ | |||||||
| import { IconSearch } from "@tabler/icons-react"; | import { IconHelp, IconSearch } from "@tabler/icons-react"; | ||||||
| import { useState } from "react"; | import { useState } from "react"; | ||||||
| import Alert from "react-bootstrap/Alert"; | import Alert from "react-bootstrap/Alert"; | ||||||
| import { deleteCertificate, downloadCertificate } from "src/api/backend"; | import { deleteCertificate, downloadCertificate } from "src/api/backend"; | ||||||
| import { LoadingPage } from "src/components"; | import { Button, LoadingPage } from "src/components"; | ||||||
| import { useCertificates } from "src/hooks"; | import { useCertificates } from "src/hooks"; | ||||||
| import { T } from "src/locale"; | import { T } from "src/locale"; | ||||||
| import { | import { | ||||||
| 	showCustomCertificateModal, | 	showCustomCertificateModal, | ||||||
| 	showDeleteConfirmModal, | 	showDeleteConfirmModal, | ||||||
| 	showDNSCertificateModal, | 	showDNSCertificateModal, | ||||||
|  | 	showHelpModal, | ||||||
| 	showHTTPCertificateModal, | 	showHTTPCertificateModal, | ||||||
| 	showRenewCertificateModal, | 	showRenewCertificateModal, | ||||||
| } from "src/modals"; | } from "src/modals"; | ||||||
| @@ -69,9 +70,10 @@ export default function TableWrapper() { | |||||||
| 								<T id="certificates" /> | 								<T id="certificates" /> | ||||||
| 							</h2> | 							</h2> | ||||||
| 						</div> | 						</div> | ||||||
| 						{data?.length ? ( |  | ||||||
| 							<div className="col-md-auto col-sm-12"> | 						<div className="col-md-auto col-sm-12"> | ||||||
| 								<div className="ms-auto d-flex flex-wrap btn-list"> | 							<div className="ms-auto d-flex flex-wrap btn-list"> | ||||||
|  | 								{data?.length ? ( | ||||||
| 									<div className="input-group input-group-flat w-auto"> | 									<div className="input-group input-group-flat w-auto"> | ||||||
| 										<span className="input-group-text input-group-text-sm"> | 										<span className="input-group-text input-group-text-sm"> | ||||||
| 											<IconSearch size={16} /> | 											<IconSearch size={16} /> | ||||||
| @@ -84,6 +86,11 @@ export default function TableWrapper() { | |||||||
| 											onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} | 											onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} | ||||||
| 										/> | 										/> | ||||||
| 									</div> | 									</div> | ||||||
|  | 								) : null} | ||||||
|  | 								<Button size="sm" onClick={() => showHelpModal("Certificates", "pink")}> | ||||||
|  | 									<IconHelp size={20} /> | ||||||
|  | 								</Button> | ||||||
|  | 								{data?.length ? ( | ||||||
| 									<div className="dropdown"> | 									<div className="dropdown"> | ||||||
| 										<button | 										<button | ||||||
| 											type="button" | 											type="button" | ||||||
| @@ -126,9 +133,9 @@ export default function TableWrapper() { | |||||||
| 											</a> | 											</a> | ||||||
| 										</div> | 										</div> | ||||||
| 									</div> | 									</div> | ||||||
| 								</div> | 								) : null} | ||||||
| 							</div> | 							</div> | ||||||
| 						) : null} | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
| 				</div> | 				</div> | ||||||
| 				<Table | 				<Table | ||||||
|   | |||||||
| @@ -116,12 +116,7 @@ const Dashboard = () => { | |||||||
| 				<code>{`Todo: | 				<code>{`Todo: | ||||||
|  |  | ||||||
| - check mobile | - check mobile | ||||||
| - use statuses for table formatters where applicable: https://docs.tabler.io/ui/components/statuses |  | ||||||
| - add help docs for host types |  | ||||||
| - REDO SCREENSHOTS in docs folder | - REDO SCREENSHOTS in docs folder | ||||||
| - search codebase for "TODO" |  | ||||||
| - update documentation to add development notes for translations |  | ||||||
| - double check output of access field selection on proxy host dialog, after access lists are completed |  | ||||||
| - check permissions in all places | - check permissions in all places | ||||||
|  |  | ||||||
| More for api, then implement here: | More for api, then implement here: | ||||||
|   | |||||||
| @@ -58,7 +58,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog | |||||||
| 				}, | 				}, | ||||||
| 			}), | 			}), | ||||||
| 			columnHelper.display({ | 			columnHelper.display({ | ||||||
| 				id: "id", // todo: not needed for a display? | 				id: "id", | ||||||
| 				cell: (info: any) => { | 				cell: (info: any) => { | ||||||
| 					return ( | 					return ( | ||||||
| 						<span className="dropdown"> | 						<span className="dropdown"> | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import { IconSearch } from "@tabler/icons-react"; | import { IconHelp, IconSearch } from "@tabler/icons-react"; | ||||||
| import { useQueryClient } from "@tanstack/react-query"; | import { useQueryClient } from "@tanstack/react-query"; | ||||||
| import { useState } from "react"; | import { useState } from "react"; | ||||||
| import Alert from "react-bootstrap/Alert"; | import Alert from "react-bootstrap/Alert"; | ||||||
| @@ -6,7 +6,7 @@ import { deleteDeadHost, toggleDeadHost } from "src/api/backend"; | |||||||
| import { Button, LoadingPage } from "src/components"; | import { Button, LoadingPage } from "src/components"; | ||||||
| import { useDeadHosts } from "src/hooks"; | import { useDeadHosts } from "src/hooks"; | ||||||
| import { T } from "src/locale"; | import { T } from "src/locale"; | ||||||
| import { showDeadHostModal, showDeleteConfirmModal } from "src/modals"; | import { showDeadHostModal, showDeleteConfirmModal, showHelpModal } from "src/modals"; | ||||||
| import { showObjectSuccess } from "src/notifications"; | import { showObjectSuccess } from "src/notifications"; | ||||||
| import Table from "./Table"; | import Table from "./Table"; | ||||||
|  |  | ||||||
| @@ -56,9 +56,10 @@ export default function TableWrapper() { | |||||||
| 								<T id="dead-hosts" /> | 								<T id="dead-hosts" /> | ||||||
| 							</h2> | 							</h2> | ||||||
| 						</div> | 						</div> | ||||||
| 						{data?.length ? ( |  | ||||||
| 							<div className="col-md-auto col-sm-12"> | 						<div className="col-md-auto col-sm-12"> | ||||||
| 								<div className="ms-auto d-flex flex-wrap btn-list"> | 							<div className="ms-auto d-flex flex-wrap btn-list"> | ||||||
|  | 								{data?.length ? ( | ||||||
| 									<div className="input-group input-group-flat w-auto"> | 									<div className="input-group input-group-flat w-auto"> | ||||||
| 										<span className="input-group-text input-group-text-sm"> | 										<span className="input-group-text input-group-text-sm"> | ||||||
| 											<IconSearch size={16} /> | 											<IconSearch size={16} /> | ||||||
| @@ -71,12 +72,17 @@ export default function TableWrapper() { | |||||||
| 											onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} | 											onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} | ||||||
| 										/> | 										/> | ||||||
| 									</div> | 									</div> | ||||||
|  | 								) : null} | ||||||
|  | 								<Button size="sm" onClick={() => showHelpModal("DeadHosts", "red")}> | ||||||
|  | 									<IconHelp size={20} /> | ||||||
|  | 								</Button> | ||||||
|  | 								{data?.length ? ( | ||||||
| 									<Button size="sm" className="btn-red" onClick={() => showDeadHostModal("new")}> | 									<Button size="sm" className="btn-red" onClick={() => showDeadHostModal("new")}> | ||||||
| 										<T id="object.add" tData={{ object: "dead-host" }} /> | 										<T id="object.add" tData={{ object: "dead-host" }} /> | ||||||
| 									</Button> | 									</Button> | ||||||
| 								</div> | 								) : null} | ||||||
| 							</div> | 							</div> | ||||||
| 						) : null} | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
| 				</div> | 				</div> | ||||||
| 				<Table | 				<Table | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import { IconSearch } from "@tabler/icons-react"; | import { IconHelp, IconSearch } from "@tabler/icons-react"; | ||||||
| import { useQueryClient } from "@tanstack/react-query"; | import { useQueryClient } from "@tanstack/react-query"; | ||||||
| import { useState } from "react"; | import { useState } from "react"; | ||||||
| import Alert from "react-bootstrap/Alert"; | import Alert from "react-bootstrap/Alert"; | ||||||
| @@ -6,7 +6,7 @@ import { deleteProxyHost, toggleProxyHost } from "src/api/backend"; | |||||||
| import { Button, LoadingPage } from "src/components"; | import { Button, LoadingPage } from "src/components"; | ||||||
| import { useProxyHosts } from "src/hooks"; | import { useProxyHosts } from "src/hooks"; | ||||||
| import { T } from "src/locale"; | import { T } from "src/locale"; | ||||||
| import { showDeleteConfirmModal, showProxyHostModal } from "src/modals"; | import { showDeleteConfirmModal, showHelpModal, showProxyHostModal } from "src/modals"; | ||||||
| import { showObjectSuccess } from "src/notifications"; | import { showObjectSuccess } from "src/notifications"; | ||||||
| import Table from "./Table"; | import Table from "./Table"; | ||||||
|  |  | ||||||
| @@ -59,9 +59,10 @@ export default function TableWrapper() { | |||||||
| 								<T id="proxy-hosts" /> | 								<T id="proxy-hosts" /> | ||||||
| 							</h2> | 							</h2> | ||||||
| 						</div> | 						</div> | ||||||
| 						{data?.length ? ( |  | ||||||
| 							<div className="col-md-auto col-sm-12"> | 						<div className="col-md-auto col-sm-12"> | ||||||
| 								<div className="ms-auto d-flex flex-wrap btn-list"> | 							<div className="ms-auto d-flex flex-wrap btn-list"> | ||||||
|  | 								{data?.length ? ( | ||||||
| 									<div className="input-group input-group-flat w-auto"> | 									<div className="input-group input-group-flat w-auto"> | ||||||
| 										<span className="input-group-text input-group-text-sm"> | 										<span className="input-group-text input-group-text-sm"> | ||||||
| 											<IconSearch size={16} /> | 											<IconSearch size={16} /> | ||||||
| @@ -74,12 +75,17 @@ export default function TableWrapper() { | |||||||
| 											onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} | 											onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} | ||||||
| 										/> | 										/> | ||||||
| 									</div> | 									</div> | ||||||
|  | 								) : null} | ||||||
|  | 								<Button size="sm" onClick={() => showHelpModal("ProxyHosts", "lime")}> | ||||||
|  | 									<IconHelp size={20} /> | ||||||
|  | 								</Button> | ||||||
|  | 								{data?.length ? ( | ||||||
| 									<Button size="sm" className="btn-lime" onClick={() => showProxyHostModal("new")}> | 									<Button size="sm" className="btn-lime" onClick={() => showProxyHostModal("new")}> | ||||||
| 										<T id="object.add" tData={{ object: "proxy-host" }} /> | 										<T id="object.add" tData={{ object: "proxy-host" }} /> | ||||||
| 									</Button> | 									</Button> | ||||||
| 								</div> | 								) : null} | ||||||
| 							</div> | 							</div> | ||||||
| 						) : null} | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
| 				</div> | 				</div> | ||||||
| 				<Table | 				<Table | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import { IconSearch } from "@tabler/icons-react"; | import { IconHelp, IconSearch } from "@tabler/icons-react"; | ||||||
| import { useQueryClient } from "@tanstack/react-query"; | import { useQueryClient } from "@tanstack/react-query"; | ||||||
| import { useState } from "react"; | import { useState } from "react"; | ||||||
| import Alert from "react-bootstrap/Alert"; | import Alert from "react-bootstrap/Alert"; | ||||||
| @@ -6,7 +6,7 @@ import { deleteRedirectionHost, toggleRedirectionHost } from "src/api/backend"; | |||||||
| import { Button, LoadingPage } from "src/components"; | import { Button, LoadingPage } from "src/components"; | ||||||
| import { useRedirectionHosts } from "src/hooks"; | import { useRedirectionHosts } from "src/hooks"; | ||||||
| import { T } from "src/locale"; | import { T } from "src/locale"; | ||||||
| import { showDeleteConfirmModal, showRedirectionHostModal } from "src/modals"; | import { showDeleteConfirmModal, showHelpModal, showRedirectionHostModal } from "src/modals"; | ||||||
| import { showObjectSuccess } from "src/notifications"; | import { showObjectSuccess } from "src/notifications"; | ||||||
| import Table from "./Table"; | import Table from "./Table"; | ||||||
|  |  | ||||||
| @@ -59,9 +59,10 @@ export default function TableWrapper() { | |||||||
| 								<T id="redirection-hosts" /> | 								<T id="redirection-hosts" /> | ||||||
| 							</h2> | 							</h2> | ||||||
| 						</div> | 						</div> | ||||||
| 						{data?.length ? ( |  | ||||||
| 							<div className="col-md-auto col-sm-12"> | 						<div className="col-md-auto col-sm-12"> | ||||||
| 								<div className="ms-auto d-flex flex-wrap btn-list"> | 							<div className="ms-auto d-flex flex-wrap btn-list"> | ||||||
|  | 								{data?.length ? ( | ||||||
| 									<div className="input-group input-group-flat w-auto"> | 									<div className="input-group input-group-flat w-auto"> | ||||||
| 										<span className="input-group-text input-group-text-sm"> | 										<span className="input-group-text input-group-text-sm"> | ||||||
| 											<IconSearch size={16} /> | 											<IconSearch size={16} /> | ||||||
| @@ -74,6 +75,11 @@ export default function TableWrapper() { | |||||||
| 											onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} | 											onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} | ||||||
| 										/> | 										/> | ||||||
| 									</div> | 									</div> | ||||||
|  | 								) : null} | ||||||
|  | 								<Button size="sm" onClick={() => showHelpModal("RedirectionHosts", "yellow")}> | ||||||
|  | 									<IconHelp size={20} /> | ||||||
|  | 								</Button> | ||||||
|  | 								{data?.length ? ( | ||||||
| 									<Button | 									<Button | ||||||
| 										size="sm" | 										size="sm" | ||||||
| 										className="btn-yellow" | 										className="btn-yellow" | ||||||
| @@ -81,9 +87,9 @@ export default function TableWrapper() { | |||||||
| 									> | 									> | ||||||
| 										<T id="object.add" tData={{ object: "redirection-host" }} /> | 										<T id="object.add" tData={{ object: "redirection-host" }} /> | ||||||
| 									</Button> | 									</Button> | ||||||
| 								</div> | 								) : null} | ||||||
| 							</div> | 							</div> | ||||||
| 						) : null} | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
| 				</div> | 				</div> | ||||||
| 				<Table | 				<Table | ||||||
|   | |||||||
| @@ -87,7 +87,7 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete, | |||||||
| 				}, | 				}, | ||||||
| 			}), | 			}), | ||||||
| 			columnHelper.display({ | 			columnHelper.display({ | ||||||
| 				id: "id", // todo: not needed for a display? | 				id: "id", | ||||||
| 				cell: (info: any) => { | 				cell: (info: any) => { | ||||||
| 					return ( | 					return ( | ||||||
| 						<span className="dropdown"> | 						<span className="dropdown"> | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import { IconSearch } from "@tabler/icons-react"; | import { IconHelp, IconSearch } from "@tabler/icons-react"; | ||||||
| import { useQueryClient } from "@tanstack/react-query"; | import { useQueryClient } from "@tanstack/react-query"; | ||||||
| import { useState } from "react"; | import { useState } from "react"; | ||||||
| import Alert from "react-bootstrap/Alert"; | import Alert from "react-bootstrap/Alert"; | ||||||
| @@ -6,7 +6,7 @@ import { deleteStream, toggleStream } from "src/api/backend"; | |||||||
| import { Button, LoadingPage } from "src/components"; | import { Button, LoadingPage } from "src/components"; | ||||||
| import { useStreams } from "src/hooks"; | import { useStreams } from "src/hooks"; | ||||||
| import { T } from "src/locale"; | import { T } from "src/locale"; | ||||||
| import { showDeleteConfirmModal, showStreamModal } from "src/modals"; | import { showDeleteConfirmModal, showHelpModal, showStreamModal } from "src/modals"; | ||||||
| import { showObjectSuccess } from "src/notifications"; | import { showObjectSuccess } from "src/notifications"; | ||||||
| import Table from "./Table"; | import Table from "./Table"; | ||||||
|  |  | ||||||
| @@ -61,9 +61,10 @@ export default function TableWrapper() { | |||||||
| 								<T id="streams" /> | 								<T id="streams" /> | ||||||
| 							</h2> | 							</h2> | ||||||
| 						</div> | 						</div> | ||||||
| 						{data?.length ? ( |  | ||||||
| 							<div className="col-md-auto col-sm-12"> | 						<div className="col-md-auto col-sm-12"> | ||||||
| 								<div className="ms-auto d-flex flex-wrap btn-list"> | 							<div className="ms-auto d-flex flex-wrap btn-list"> | ||||||
|  | 								{data?.length ? ( | ||||||
| 									<div className="input-group input-group-flat w-auto"> | 									<div className="input-group input-group-flat w-auto"> | ||||||
| 										<span className="input-group-text input-group-text-sm"> | 										<span className="input-group-text input-group-text-sm"> | ||||||
| 											<IconSearch size={16} /> | 											<IconSearch size={16} /> | ||||||
| @@ -76,12 +77,17 @@ export default function TableWrapper() { | |||||||
| 											onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} | 											onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} | ||||||
| 										/> | 										/> | ||||||
| 									</div> | 									</div> | ||||||
|  | 								) : null} | ||||||
|  | 								<Button size="sm" onClick={() => showHelpModal("Streams", "blue")}> | ||||||
|  | 									<IconHelp size={20} /> | ||||||
|  | 								</Button> | ||||||
|  | 								{data?.length ? ( | ||||||
| 									<Button size="sm" className="btn-blue" onClick={() => showStreamModal("new")}> | 									<Button size="sm" className="btn-blue" onClick={() => showStreamModal("new")}> | ||||||
| 										<T id="object.add" tData={{ object: "stream" }} /> | 										<T id="object.add" tData={{ object: "stream" }} /> | ||||||
| 									</Button> | 									</Button> | ||||||
| 								</div> | 								) : null} | ||||||
| 							</div> | 							</div> | ||||||
| 						) : null} | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
| 				</div> | 				</div> | ||||||
| 				<Table | 				<Table | ||||||
|   | |||||||
| @@ -47,4 +47,5 @@ export default defineConfig({ | |||||||
| 		environment: "happy-dom", | 		environment: "happy-dom", | ||||||
| 		setupFiles: ["./vitest-setup.js"], | 		setupFiles: ["./vitest-setup.js"], | ||||||
| 	}, | 	}, | ||||||
|  | 	assetsInclude: ["**/*.md", "**/*.png", "**/*.svg"], | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -977,12 +977,26 @@ | |||||||
|   resolved "https://registry.yarnpkg.com/@types/country-flag-icons/-/country-flag-icons-1.2.2.tgz#8f51089cab857f0f700feabd38b3960d006d64f2" |   resolved "https://registry.yarnpkg.com/@types/country-flag-icons/-/country-flag-icons-1.2.2.tgz#8f51089cab857f0f700feabd38b3960d006d64f2" | ||||||
|   integrity sha512-CefEn/J336TBDp7NX8JqzlDtCBOsm8M3r1Li0gEOt0HOMHF1XemNyrx9lSHjsafcb1yYWybU0N8ZAXuyCaND0w== |   integrity sha512-CefEn/J336TBDp7NX8JqzlDtCBOsm8M3r1Li0gEOt0HOMHF1XemNyrx9lSHjsafcb1yYWybU0N8ZAXuyCaND0w== | ||||||
|  |  | ||||||
|  | "@types/debug@^4.0.0": | ||||||
|  |   version "4.1.12" | ||||||
|  |   resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" | ||||||
|  |   integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== | ||||||
|  |   dependencies: | ||||||
|  |     "@types/ms" "*" | ||||||
|  |  | ||||||
| "@types/deep-eql@*": | "@types/deep-eql@*": | ||||||
|   version "4.0.2" |   version "4.0.2" | ||||||
|   resolved "https://registry.yarnpkg.com/@types/deep-eql/-/deep-eql-4.0.2.tgz#334311971d3a07121e7eb91b684a605e7eea9cbd" |   resolved "https://registry.yarnpkg.com/@types/deep-eql/-/deep-eql-4.0.2.tgz#334311971d3a07121e7eb91b684a605e7eea9cbd" | ||||||
|   integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== |   integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== | ||||||
|  |  | ||||||
| "@types/estree@1.0.8", "@types/estree@^1.0.0": | "@types/estree-jsx@^1.0.0": | ||||||
|  |   version "1.0.5" | ||||||
|  |   resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-1.0.5.tgz#858a88ea20f34fe65111f005a689fa1ebf70dc18" | ||||||
|  |   integrity sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg== | ||||||
|  |   dependencies: | ||||||
|  |     "@types/estree" "*" | ||||||
|  |  | ||||||
|  | "@types/estree@*", "@types/estree@1.0.8", "@types/estree@^1.0.0": | ||||||
|   version "1.0.8" |   version "1.0.8" | ||||||
|   resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" |   resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" | ||||||
|   integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== |   integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== | ||||||
| @@ -1020,6 +1034,11 @@ | |||||||
|   dependencies: |   dependencies: | ||||||
|     "@types/unist" "*" |     "@types/unist" "*" | ||||||
|  |  | ||||||
|  | "@types/ms@*": | ||||||
|  |   version "2.1.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/@types/ms/-/ms-2.1.0.tgz#052aa67a48eccc4309d7f0191b7e41434b90bb78" | ||||||
|  |   integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== | ||||||
|  |  | ||||||
| "@types/node@^20.0.0": | "@types/node@^20.0.0": | ||||||
|   version "20.19.11" |   version "20.19.11" | ||||||
|   resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.11.tgz#728cab53092bd5f143beed7fbba7ba99de3c16c4" |   resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.11.tgz#728cab53092bd5f143beed7fbba7ba99de3c16c4" | ||||||
| @@ -1356,6 +1375,13 @@ date-fns@^4.1.0: | |||||||
|   resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14" |   resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14" | ||||||
|   integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg== |   integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg== | ||||||
|  |  | ||||||
|  | debug@^4.0.0, debug@^4.4.3: | ||||||
|  |   version "4.4.3" | ||||||
|  |   resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" | ||||||
|  |   integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== | ||||||
|  |   dependencies: | ||||||
|  |     ms "^2.1.3" | ||||||
|  |  | ||||||
| debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: | debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: | ||||||
|   version "4.4.1" |   version "4.4.1" | ||||||
|   resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" |   resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" | ||||||
| @@ -1363,13 +1389,6 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: | |||||||
|   dependencies: |   dependencies: | ||||||
|     ms "^2.1.3" |     ms "^2.1.3" | ||||||
|  |  | ||||||
| debug@^4.4.3: |  | ||||||
|   version "4.4.3" |  | ||||||
|   resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" |  | ||||||
|   integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== |  | ||||||
|   dependencies: |  | ||||||
|     ms "^2.1.3" |  | ||||||
|  |  | ||||||
| decimal.js@^10.4.3: | decimal.js@^10.4.3: | ||||||
|   version "10.6.0" |   version "10.6.0" | ||||||
|   resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.6.0.tgz#e649a43e3ab953a72192ff5983865e509f37ed9a" |   resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.6.0.tgz#e649a43e3ab953a72192ff5983865e509f37ed9a" | ||||||
| @@ -1491,6 +1510,11 @@ escape-string-regexp@^4.0.0: | |||||||
|   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" |   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" | ||||||
|   integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== |   integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== | ||||||
|  |  | ||||||
|  | estree-util-is-identifier-name@^3.0.0: | ||||||
|  |   version "3.0.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz#0b5ef4c4ff13508b34dcd01ecfa945f61fce5dbd" | ||||||
|  |   integrity sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg== | ||||||
|  |  | ||||||
| estree-walker@^3.0.3: | estree-walker@^3.0.3: | ||||||
|   version "3.0.3" |   version "3.0.3" | ||||||
|   resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" |   resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" | ||||||
| @@ -1655,6 +1679,27 @@ hast-util-to-html@^9.0.0: | |||||||
|     stringify-entities "^4.0.0" |     stringify-entities "^4.0.0" | ||||||
|     zwitch "^2.0.4" |     zwitch "^2.0.4" | ||||||
|  |  | ||||||
|  | hast-util-to-jsx-runtime@^2.0.0: | ||||||
|  |   version "2.3.6" | ||||||
|  |   resolved "https://registry.yarnpkg.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz#ff31897aae59f62232e21594eac7ef6b63333e98" | ||||||
|  |   integrity sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg== | ||||||
|  |   dependencies: | ||||||
|  |     "@types/estree" "^1.0.0" | ||||||
|  |     "@types/hast" "^3.0.0" | ||||||
|  |     "@types/unist" "^3.0.0" | ||||||
|  |     comma-separated-tokens "^2.0.0" | ||||||
|  |     devlop "^1.0.0" | ||||||
|  |     estree-util-is-identifier-name "^3.0.0" | ||||||
|  |     hast-util-whitespace "^3.0.0" | ||||||
|  |     mdast-util-mdx-expression "^2.0.0" | ||||||
|  |     mdast-util-mdx-jsx "^3.0.0" | ||||||
|  |     mdast-util-mdxjs-esm "^2.0.0" | ||||||
|  |     property-information "^7.0.0" | ||||||
|  |     space-separated-tokens "^2.0.0" | ||||||
|  |     style-to-js "^1.0.0" | ||||||
|  |     unist-util-position "^5.0.0" | ||||||
|  |     vfile-message "^4.0.0" | ||||||
|  |  | ||||||
| hast-util-to-string@^3.0.0: | hast-util-to-string@^3.0.0: | ||||||
|   version "3.0.1" |   version "3.0.1" | ||||||
|   resolved "https://registry.yarnpkg.com/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz#a4f15e682849326dd211c97129c94b0c3e76527c" |   resolved "https://registry.yarnpkg.com/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz#a4f15e682849326dd211c97129c94b0c3e76527c" | ||||||
| @@ -1698,6 +1743,11 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react- | |||||||
|   dependencies: |   dependencies: | ||||||
|     react-is "^16.7.0" |     react-is "^16.7.0" | ||||||
|  |  | ||||||
|  | html-url-attributes@^3.0.0: | ||||||
|  |   version "3.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/html-url-attributes/-/html-url-attributes-3.0.1.tgz#83b052cd5e437071b756cd74ae70f708870c2d87" | ||||||
|  |   integrity sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ== | ||||||
|  |  | ||||||
| html-void-elements@^3.0.0: | html-void-elements@^3.0.0: | ||||||
|   version "3.0.0" |   version "3.0.0" | ||||||
|   resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7" |   resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7" | ||||||
| @@ -1731,6 +1781,11 @@ indent-string@^4.0.0: | |||||||
|   resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" |   resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" | ||||||
|   integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== |   integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== | ||||||
|  |  | ||||||
|  | inline-style-parser@0.2.4: | ||||||
|  |   version "0.2.4" | ||||||
|  |   resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.4.tgz#f4af5fe72e612839fcd453d989a586566d695f22" | ||||||
|  |   integrity sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== | ||||||
|  |  | ||||||
| intl-messageformat@10.7.18: | intl-messageformat@10.7.18: | ||||||
|   version "10.7.18" |   version "10.7.18" | ||||||
|   resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.7.18.tgz#51a6f387afbca9b0f881b2ec081566db8c540b0d" |   resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.7.18.tgz#51a6f387afbca9b0f881b2ec081566db8c540b0d" | ||||||
| @@ -1845,6 +1900,11 @@ lodash@^4.17.21: | |||||||
|   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" |   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" | ||||||
|   integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== |   integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== | ||||||
|  |  | ||||||
|  | longest-streak@^3.0.0: | ||||||
|  |   version "3.1.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" | ||||||
|  |   integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== | ||||||
|  |  | ||||||
| loose-envify@^1.0.0, loose-envify@^1.4.0: | loose-envify@^1.0.0, loose-envify@^1.4.0: | ||||||
|   version "1.4.0" |   version "1.4.0" | ||||||
|   resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" |   resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" | ||||||
| @@ -1871,6 +1931,74 @@ magic-string@^0.30.19: | |||||||
|   dependencies: |   dependencies: | ||||||
|     "@jridgewell/sourcemap-codec" "^1.5.5" |     "@jridgewell/sourcemap-codec" "^1.5.5" | ||||||
|  |  | ||||||
|  | mdast-util-from-markdown@^2.0.0: | ||||||
|  |   version "2.0.2" | ||||||
|  |   resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz#4850390ca7cf17413a9b9a0fbefcd1bc0eb4160a" | ||||||
|  |   integrity sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA== | ||||||
|  |   dependencies: | ||||||
|  |     "@types/mdast" "^4.0.0" | ||||||
|  |     "@types/unist" "^3.0.0" | ||||||
|  |     decode-named-character-reference "^1.0.0" | ||||||
|  |     devlop "^1.0.0" | ||||||
|  |     mdast-util-to-string "^4.0.0" | ||||||
|  |     micromark "^4.0.0" | ||||||
|  |     micromark-util-decode-numeric-character-reference "^2.0.0" | ||||||
|  |     micromark-util-decode-string "^2.0.0" | ||||||
|  |     micromark-util-normalize-identifier "^2.0.0" | ||||||
|  |     micromark-util-symbol "^2.0.0" | ||||||
|  |     micromark-util-types "^2.0.0" | ||||||
|  |     unist-util-stringify-position "^4.0.0" | ||||||
|  |  | ||||||
|  | mdast-util-mdx-expression@^2.0.0: | ||||||
|  |   version "2.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz#43f0abac9adc756e2086f63822a38c8d3c3a5096" | ||||||
|  |   integrity sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ== | ||||||
|  |   dependencies: | ||||||
|  |     "@types/estree-jsx" "^1.0.0" | ||||||
|  |     "@types/hast" "^3.0.0" | ||||||
|  |     "@types/mdast" "^4.0.0" | ||||||
|  |     devlop "^1.0.0" | ||||||
|  |     mdast-util-from-markdown "^2.0.0" | ||||||
|  |     mdast-util-to-markdown "^2.0.0" | ||||||
|  |  | ||||||
|  | mdast-util-mdx-jsx@^3.0.0: | ||||||
|  |   version "3.2.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz#fd04c67a2a7499efb905a8a5c578dddc9fdada0d" | ||||||
|  |   integrity sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q== | ||||||
|  |   dependencies: | ||||||
|  |     "@types/estree-jsx" "^1.0.0" | ||||||
|  |     "@types/hast" "^3.0.0" | ||||||
|  |     "@types/mdast" "^4.0.0" | ||||||
|  |     "@types/unist" "^3.0.0" | ||||||
|  |     ccount "^2.0.0" | ||||||
|  |     devlop "^1.1.0" | ||||||
|  |     mdast-util-from-markdown "^2.0.0" | ||||||
|  |     mdast-util-to-markdown "^2.0.0" | ||||||
|  |     parse-entities "^4.0.0" | ||||||
|  |     stringify-entities "^4.0.0" | ||||||
|  |     unist-util-stringify-position "^4.0.0" | ||||||
|  |     vfile-message "^4.0.0" | ||||||
|  |  | ||||||
|  | mdast-util-mdxjs-esm@^2.0.0: | ||||||
|  |   version "2.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz#019cfbe757ad62dd557db35a695e7314bcc9fa97" | ||||||
|  |   integrity sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg== | ||||||
|  |   dependencies: | ||||||
|  |     "@types/estree-jsx" "^1.0.0" | ||||||
|  |     "@types/hast" "^3.0.0" | ||||||
|  |     "@types/mdast" "^4.0.0" | ||||||
|  |     devlop "^1.0.0" | ||||||
|  |     mdast-util-from-markdown "^2.0.0" | ||||||
|  |     mdast-util-to-markdown "^2.0.0" | ||||||
|  |  | ||||||
|  | mdast-util-phrasing@^4.0.0: | ||||||
|  |   version "4.1.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz#7cc0a8dec30eaf04b7b1a9661a92adb3382aa6e3" | ||||||
|  |   integrity sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== | ||||||
|  |   dependencies: | ||||||
|  |     "@types/mdast" "^4.0.0" | ||||||
|  |     unist-util-is "^6.0.0" | ||||||
|  |  | ||||||
| mdast-util-to-hast@^13.0.0: | mdast-util-to-hast@^13.0.0: | ||||||
|   version "13.2.0" |   version "13.2.0" | ||||||
|   resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz#5ca58e5b921cc0a3ded1bc02eed79a4fe4fe41f4" |   resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz#5ca58e5b921cc0a3ded1bc02eed79a4fe4fe41f4" | ||||||
| @@ -1886,11 +2014,102 @@ mdast-util-to-hast@^13.0.0: | |||||||
|     unist-util-visit "^5.0.0" |     unist-util-visit "^5.0.0" | ||||||
|     vfile "^6.0.0" |     vfile "^6.0.0" | ||||||
|  |  | ||||||
|  | mdast-util-to-markdown@^2.0.0: | ||||||
|  |   version "2.1.2" | ||||||
|  |   resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz#f910ffe60897f04bb4b7e7ee434486f76288361b" | ||||||
|  |   integrity sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA== | ||||||
|  |   dependencies: | ||||||
|  |     "@types/mdast" "^4.0.0" | ||||||
|  |     "@types/unist" "^3.0.0" | ||||||
|  |     longest-streak "^3.0.0" | ||||||
|  |     mdast-util-phrasing "^4.0.0" | ||||||
|  |     mdast-util-to-string "^4.0.0" | ||||||
|  |     micromark-util-classify-character "^2.0.0" | ||||||
|  |     micromark-util-decode-string "^2.0.0" | ||||||
|  |     unist-util-visit "^5.0.0" | ||||||
|  |     zwitch "^2.0.0" | ||||||
|  |  | ||||||
|  | mdast-util-to-string@^4.0.0: | ||||||
|  |   version "4.0.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" | ||||||
|  |   integrity sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== | ||||||
|  |   dependencies: | ||||||
|  |     "@types/mdast" "^4.0.0" | ||||||
|  |  | ||||||
| memoize-one@^6.0.0: | memoize-one@^6.0.0: | ||||||
|   version "6.0.0" |   version "6.0.0" | ||||||
|   resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" |   resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" | ||||||
|   integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== |   integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== | ||||||
|  |  | ||||||
|  | micromark-core-commonmark@^2.0.0: | ||||||
|  |   version "2.0.3" | ||||||
|  |   resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz#c691630e485021a68cf28dbc2b2ca27ebf678cd4" | ||||||
|  |   integrity sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg== | ||||||
|  |   dependencies: | ||||||
|  |     decode-named-character-reference "^1.0.0" | ||||||
|  |     devlop "^1.0.0" | ||||||
|  |     micromark-factory-destination "^2.0.0" | ||||||
|  |     micromark-factory-label "^2.0.0" | ||||||
|  |     micromark-factory-space "^2.0.0" | ||||||
|  |     micromark-factory-title "^2.0.0" | ||||||
|  |     micromark-factory-whitespace "^2.0.0" | ||||||
|  |     micromark-util-character "^2.0.0" | ||||||
|  |     micromark-util-chunked "^2.0.0" | ||||||
|  |     micromark-util-classify-character "^2.0.0" | ||||||
|  |     micromark-util-html-tag-name "^2.0.0" | ||||||
|  |     micromark-util-normalize-identifier "^2.0.0" | ||||||
|  |     micromark-util-resolve-all "^2.0.0" | ||||||
|  |     micromark-util-subtokenize "^2.0.0" | ||||||
|  |     micromark-util-symbol "^2.0.0" | ||||||
|  |     micromark-util-types "^2.0.0" | ||||||
|  |  | ||||||
|  | micromark-factory-destination@^2.0.0: | ||||||
|  |   version "2.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz#8fef8e0f7081f0474fbdd92deb50c990a0264639" | ||||||
|  |   integrity sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA== | ||||||
|  |   dependencies: | ||||||
|  |     micromark-util-character "^2.0.0" | ||||||
|  |     micromark-util-symbol "^2.0.0" | ||||||
|  |     micromark-util-types "^2.0.0" | ||||||
|  |  | ||||||
|  | micromark-factory-label@^2.0.0: | ||||||
|  |   version "2.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz#5267efa97f1e5254efc7f20b459a38cb21058ba1" | ||||||
|  |   integrity sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg== | ||||||
|  |   dependencies: | ||||||
|  |     devlop "^1.0.0" | ||||||
|  |     micromark-util-character "^2.0.0" | ||||||
|  |     micromark-util-symbol "^2.0.0" | ||||||
|  |     micromark-util-types "^2.0.0" | ||||||
|  |  | ||||||
|  | micromark-factory-space@^2.0.0: | ||||||
|  |   version "2.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz#36d0212e962b2b3121f8525fc7a3c7c029f334fc" | ||||||
|  |   integrity sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg== | ||||||
|  |   dependencies: | ||||||
|  |     micromark-util-character "^2.0.0" | ||||||
|  |     micromark-util-types "^2.0.0" | ||||||
|  |  | ||||||
|  | micromark-factory-title@^2.0.0: | ||||||
|  |   version "2.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz#237e4aa5d58a95863f01032d9ee9b090f1de6e94" | ||||||
|  |   integrity sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw== | ||||||
|  |   dependencies: | ||||||
|  |     micromark-factory-space "^2.0.0" | ||||||
|  |     micromark-util-character "^2.0.0" | ||||||
|  |     micromark-util-symbol "^2.0.0" | ||||||
|  |     micromark-util-types "^2.0.0" | ||||||
|  |  | ||||||
|  | micromark-factory-whitespace@^2.0.0: | ||||||
|  |   version "2.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz#06b26b2983c4d27bfcc657b33e25134d4868b0b1" | ||||||
|  |   integrity sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ== | ||||||
|  |   dependencies: | ||||||
|  |     micromark-factory-space "^2.0.0" | ||||||
|  |     micromark-util-character "^2.0.0" | ||||||
|  |     micromark-util-symbol "^2.0.0" | ||||||
|  |     micromark-util-types "^2.0.0" | ||||||
|  |  | ||||||
| micromark-util-character@^2.0.0: | micromark-util-character@^2.0.0: | ||||||
|   version "2.1.1" |   version "2.1.1" | ||||||
|   resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz#2f987831a40d4c510ac261e89852c4e9703ccda6" |   resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz#2f987831a40d4c510ac261e89852c4e9703ccda6" | ||||||
| @@ -1899,11 +2118,71 @@ micromark-util-character@^2.0.0: | |||||||
|     micromark-util-symbol "^2.0.0" |     micromark-util-symbol "^2.0.0" | ||||||
|     micromark-util-types "^2.0.0" |     micromark-util-types "^2.0.0" | ||||||
|  |  | ||||||
|  | micromark-util-chunked@^2.0.0: | ||||||
|  |   version "2.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz#47fbcd93471a3fccab86cff03847fc3552db1051" | ||||||
|  |   integrity sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA== | ||||||
|  |   dependencies: | ||||||
|  |     micromark-util-symbol "^2.0.0" | ||||||
|  |  | ||||||
|  | micromark-util-classify-character@^2.0.0: | ||||||
|  |   version "2.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz#d399faf9c45ca14c8b4be98b1ea481bced87b629" | ||||||
|  |   integrity sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q== | ||||||
|  |   dependencies: | ||||||
|  |     micromark-util-character "^2.0.0" | ||||||
|  |     micromark-util-symbol "^2.0.0" | ||||||
|  |     micromark-util-types "^2.0.0" | ||||||
|  |  | ||||||
|  | micromark-util-combine-extensions@^2.0.0: | ||||||
|  |   version "2.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz#2a0f490ab08bff5cc2fd5eec6dd0ca04f89b30a9" | ||||||
|  |   integrity sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg== | ||||||
|  |   dependencies: | ||||||
|  |     micromark-util-chunked "^2.0.0" | ||||||
|  |     micromark-util-types "^2.0.0" | ||||||
|  |  | ||||||
|  | micromark-util-decode-numeric-character-reference@^2.0.0: | ||||||
|  |   version "2.0.2" | ||||||
|  |   resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz#fcf15b660979388e6f118cdb6bf7d79d73d26fe5" | ||||||
|  |   integrity sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw== | ||||||
|  |   dependencies: | ||||||
|  |     micromark-util-symbol "^2.0.0" | ||||||
|  |  | ||||||
|  | micromark-util-decode-string@^2.0.0: | ||||||
|  |   version "2.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz#6cb99582e5d271e84efca8e61a807994d7161eb2" | ||||||
|  |   integrity sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ== | ||||||
|  |   dependencies: | ||||||
|  |     decode-named-character-reference "^1.0.0" | ||||||
|  |     micromark-util-character "^2.0.0" | ||||||
|  |     micromark-util-decode-numeric-character-reference "^2.0.0" | ||||||
|  |     micromark-util-symbol "^2.0.0" | ||||||
|  |  | ||||||
| micromark-util-encode@^2.0.0: | micromark-util-encode@^2.0.0: | ||||||
|   version "2.0.1" |   version "2.0.1" | ||||||
|   resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz#0d51d1c095551cfaac368326963cf55f15f540b8" |   resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz#0d51d1c095551cfaac368326963cf55f15f540b8" | ||||||
|   integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== |   integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== | ||||||
|  |  | ||||||
|  | micromark-util-html-tag-name@^2.0.0: | ||||||
|  |   version "2.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz#e40403096481986b41c106627f98f72d4d10b825" | ||||||
|  |   integrity sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA== | ||||||
|  |  | ||||||
|  | micromark-util-normalize-identifier@^2.0.0: | ||||||
|  |   version "2.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz#c30d77b2e832acf6526f8bf1aa47bc9c9438c16d" | ||||||
|  |   integrity sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q== | ||||||
|  |   dependencies: | ||||||
|  |     micromark-util-symbol "^2.0.0" | ||||||
|  |  | ||||||
|  | micromark-util-resolve-all@^2.0.0: | ||||||
|  |   version "2.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz#e1a2d62cdd237230a2ae11839027b19381e31e8b" | ||||||
|  |   integrity sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg== | ||||||
|  |   dependencies: | ||||||
|  |     micromark-util-types "^2.0.0" | ||||||
|  |  | ||||||
| micromark-util-sanitize-uri@^2.0.0: | micromark-util-sanitize-uri@^2.0.0: | ||||||
|   version "2.0.1" |   version "2.0.1" | ||||||
|   resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz#ab89789b818a58752b73d6b55238621b7faa8fd7" |   resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz#ab89789b818a58752b73d6b55238621b7faa8fd7" | ||||||
| @@ -1913,6 +2192,16 @@ micromark-util-sanitize-uri@^2.0.0: | |||||||
|     micromark-util-encode "^2.0.0" |     micromark-util-encode "^2.0.0" | ||||||
|     micromark-util-symbol "^2.0.0" |     micromark-util-symbol "^2.0.0" | ||||||
|  |  | ||||||
|  | micromark-util-subtokenize@^2.0.0: | ||||||
|  |   version "2.1.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz#d8ade5ba0f3197a1cf6a2999fbbfe6357a1a19ee" | ||||||
|  |   integrity sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA== | ||||||
|  |   dependencies: | ||||||
|  |     devlop "^1.0.0" | ||||||
|  |     micromark-util-chunked "^2.0.0" | ||||||
|  |     micromark-util-symbol "^2.0.0" | ||||||
|  |     micromark-util-types "^2.0.0" | ||||||
|  |  | ||||||
| micromark-util-symbol@^2.0.0: | micromark-util-symbol@^2.0.0: | ||||||
|   version "2.0.1" |   version "2.0.1" | ||||||
|   resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz#e5da494e8eb2b071a0d08fb34f6cefec6c0a19b8" |   resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz#e5da494e8eb2b071a0d08fb34f6cefec6c0a19b8" | ||||||
| @@ -1923,6 +2212,29 @@ micromark-util-types@^2.0.0: | |||||||
|   resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz#f00225f5f5a0ebc3254f96c36b6605c4b393908e" |   resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz#f00225f5f5a0ebc3254f96c36b6605c4b393908e" | ||||||
|   integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== |   integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== | ||||||
|  |  | ||||||
|  | micromark@^4.0.0: | ||||||
|  |   version "4.0.2" | ||||||
|  |   resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.2.tgz#91395a3e1884a198e62116e33c9c568e39936fdb" | ||||||
|  |   integrity sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA== | ||||||
|  |   dependencies: | ||||||
|  |     "@types/debug" "^4.0.0" | ||||||
|  |     debug "^4.0.0" | ||||||
|  |     decode-named-character-reference "^1.0.0" | ||||||
|  |     devlop "^1.0.0" | ||||||
|  |     micromark-core-commonmark "^2.0.0" | ||||||
|  |     micromark-factory-space "^2.0.0" | ||||||
|  |     micromark-util-character "^2.0.0" | ||||||
|  |     micromark-util-chunked "^2.0.0" | ||||||
|  |     micromark-util-combine-extensions "^2.0.0" | ||||||
|  |     micromark-util-decode-numeric-character-reference "^2.0.0" | ||||||
|  |     micromark-util-encode "^2.0.0" | ||||||
|  |     micromark-util-normalize-identifier "^2.0.0" | ||||||
|  |     micromark-util-resolve-all "^2.0.0" | ||||||
|  |     micromark-util-sanitize-uri "^2.0.0" | ||||||
|  |     micromark-util-subtokenize "^2.0.0" | ||||||
|  |     micromark-util-symbol "^2.0.0" | ||||||
|  |     micromark-util-types "^2.0.0" | ||||||
|  |  | ||||||
| micromatch@^4.0.5: | micromatch@^4.0.5: | ||||||
|   version "4.0.8" |   version "4.0.8" | ||||||
|   resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" |   resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" | ||||||
| @@ -2184,6 +2496,23 @@ react-lifecycles-compat@^3.0.4: | |||||||
|   resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" |   resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" | ||||||
|   integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== |   integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== | ||||||
|  |  | ||||||
|  | react-markdown@^10.1.0: | ||||||
|  |   version "10.1.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-10.1.0.tgz#e22bc20faddbc07605c15284255653c0f3bad5ca" | ||||||
|  |   integrity sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ== | ||||||
|  |   dependencies: | ||||||
|  |     "@types/hast" "^3.0.0" | ||||||
|  |     "@types/mdast" "^4.0.0" | ||||||
|  |     devlop "^1.0.0" | ||||||
|  |     hast-util-to-jsx-runtime "^2.0.0" | ||||||
|  |     html-url-attributes "^3.0.0" | ||||||
|  |     mdast-util-to-hast "^13.0.0" | ||||||
|  |     remark-parse "^11.0.0" | ||||||
|  |     remark-rehype "^11.0.0" | ||||||
|  |     unified "^11.0.0" | ||||||
|  |     unist-util-visit "^5.0.0" | ||||||
|  |     vfile "^6.0.0" | ||||||
|  |  | ||||||
| react-refresh@^0.18.0: | react-refresh@^0.18.0: | ||||||
|   version "0.18.0" |   version "0.18.0" | ||||||
|   resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.18.0.tgz#2dce97f4fe932a4d8142fa1630e475c1729c8062" |   resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.18.0.tgz#2dce97f4fe932a4d8142fa1630e475c1729c8062" | ||||||
| @@ -2304,6 +2633,27 @@ rehype@~13.0.0: | |||||||
|     rehype-stringify "^10.0.0" |     rehype-stringify "^10.0.0" | ||||||
|     unified "^11.0.0" |     unified "^11.0.0" | ||||||
|  |  | ||||||
|  | remark-parse@^11.0.0: | ||||||
|  |   version "11.0.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" | ||||||
|  |   integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== | ||||||
|  |   dependencies: | ||||||
|  |     "@types/mdast" "^4.0.0" | ||||||
|  |     mdast-util-from-markdown "^2.0.0" | ||||||
|  |     micromark-util-types "^2.0.0" | ||||||
|  |     unified "^11.0.0" | ||||||
|  |  | ||||||
|  | remark-rehype@^11.0.0: | ||||||
|  |   version "11.1.2" | ||||||
|  |   resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.2.tgz#2addaadda80ca9bd9aa0da763e74d16327683b37" | ||||||
|  |   integrity sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw== | ||||||
|  |   dependencies: | ||||||
|  |     "@types/hast" "^3.0.0" | ||||||
|  |     "@types/mdast" "^4.0.0" | ||||||
|  |     mdast-util-to-hast "^13.0.0" | ||||||
|  |     unified "^11.0.0" | ||||||
|  |     vfile "^6.0.0" | ||||||
|  |  | ||||||
| resolve-from@^4.0.0: | resolve-from@^4.0.0: | ||||||
|   version "4.0.0" |   version "4.0.0" | ||||||
|   resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" |   resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" | ||||||
| @@ -2439,6 +2789,20 @@ strip-indent@^3.0.0: | |||||||
|   dependencies: |   dependencies: | ||||||
|     min-indent "^1.0.0" |     min-indent "^1.0.0" | ||||||
|  |  | ||||||
|  | style-to-js@^1.0.0: | ||||||
|  |   version "1.1.18" | ||||||
|  |   resolved "https://registry.yarnpkg.com/style-to-js/-/style-to-js-1.1.18.tgz#3e6c13bd4c4db079bd2c2c94571cce5c758bc2ff" | ||||||
|  |   integrity sha512-JFPn62D4kJaPTnhFUI244MThx+FEGbi+9dw1b9yBBQ+1CZpV7QAT8kUtJ7b7EUNdHajjF/0x8fT+16oLJoojLg== | ||||||
|  |   dependencies: | ||||||
|  |     style-to-object "1.0.11" | ||||||
|  |  | ||||||
|  | style-to-object@1.0.11: | ||||||
|  |   version "1.0.11" | ||||||
|  |   resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.11.tgz#cf252c4051758b7acb18a5efb296f91fb79bb9c4" | ||||||
|  |   integrity sha512-5A560JmXr7wDyGLK12Nq/EYS38VkGlglVzkis1JEdbGWSnbQIEhZzTJhzURXN5/8WwwFCs/f/VVcmkTppbXLow== | ||||||
|  |   dependencies: | ||||||
|  |     inline-style-parser "0.2.4" | ||||||
|  |  | ||||||
| stylis@4.2.0: | stylis@4.2.0: | ||||||
|   version "4.2.0" |   version "4.2.0" | ||||||
|   resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" |   resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" | ||||||
| @@ -2757,7 +3121,7 @@ yaml@^1.10.0: | |||||||
|   resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" |   resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" | ||||||
|   integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== |   integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== | ||||||
|  |  | ||||||
| zwitch@^2.0.4: | zwitch@^2.0.0, zwitch@^2.0.4: | ||||||
|   version "2.0.4" |   version "2.0.4" | ||||||
|   resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" |   resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" | ||||||
|   integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== |   integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user