11import { useState , useRef , useEffect , useMemo } from "react" ;
22import Asciidoctor from "@asciidoctor/core" ;
33import T from "./i18n.js" ;
4+ import { useTheme } from "./theme.js" ;
45
5- const VERSION = "1.2 .0" ;
6+ const VERSION = "1.3 .0" ;
67
78const TIER_BG = [ "#10b981" , "#f59e0b" , "#f97316" , "#ef4444" ] ;
89const TYPE_COLORS = {
@@ -31,24 +32,24 @@ function RadarChart({ values, dimensions, size = 320 }) {
3132 { [ 1 , 2 , 3 , 4 , 5 ] . map ( ( l ) => {
3233 const r = ( maxR / levels ) * l ;
3334 const pts = Array . from ( { length : n } , ( _ , i ) => polarToCartesian ( cx , cy , r , i * step ) ) ;
34- return < polygon key = { l } points = { pts . map ( ( p ) => `${ p . x } ,${ p . y } ` ) . join ( " " ) } fill = "none" stroke = { l === 5 ? "#475569 " : "#334155 " } strokeWidth = { l === 5 ? 1.5 : 0.7 } /> ;
35+ return < polygon key = { l } points = { pts . map ( ( p ) => `${ p . x } ,${ p . y } ` ) . join ( " " ) } fill = "none" stroke = { l === 5 ? "var(--grid-line-outer) " : "var(--grid-line) " } strokeWidth = { l === 5 ? 1.5 : 0.7 } /> ;
3536 } ) }
3637 { dimensions . map ( ( _ , i ) => {
3738 const p = polarToCartesian ( cx , cy , maxR , i * step ) ;
38- return < line key = { `a${ i } ` } x1 = { cx } y1 = { cy } x2 = { p . x } y2 = { p . y } stroke = "#334155 " strokeWidth = { 0.7 } /> ;
39+ return < line key = { `a${ i } ` } x1 = { cx } y1 = { cy } x2 = { p . x } y2 = { p . y } stroke = "var(--grid-line) " strokeWidth = { 0.7 } /> ;
3940 } ) }
4041 { ( ( ) => {
4142 const pts = dimensions . map ( ( d , i ) => polarToCartesian ( cx , cy , ( maxR / levels ) * ( values [ d . key ] + 1 ) , i * step ) ) ;
4243 return (
4344 < >
4445 < polygon points = { pts . map ( ( p ) => `${ p . x } ,${ p . y } ` ) . join ( " " ) } fill = { tc } fillOpacity = { 0.25 } stroke = { tc } strokeWidth = { 2.5 } />
45- { pts . map ( ( p , i ) => < circle key = { i } cx = { p . x } cy = { p . y } r = { 5 } fill = { tc } stroke = "#0f172a " strokeWidth = { 1.5 } /> ) }
46+ { pts . map ( ( p , i ) => < circle key = { i } cx = { p . x } cy = { p . y } r = { 5 } fill = { tc } stroke = "var(--dot-stroke) " strokeWidth = { 1.5 } /> ) }
4647 </ >
4748 ) ;
4849 } ) ( ) }
4950 { dimensions . map ( ( d , i ) => {
5051 const lp = polarToCartesian ( cx , cy , maxR + 26 , i * step ) ;
51- return < text key = { `l${ i } ` } x = { lp . x } y = { lp . y } textAnchor = "middle" dominantBaseline = "middle" fill = "#94a3b8 " fontSize = "11" fontWeight = "600" > { d . shortLabel } </ text > ;
52+ return < text key = { `l${ i } ` } x = { lp . x } y = { lp . y } textAnchor = "middle" dominantBaseline = "middle" fill = "var(--text-secondary) " fontSize = "11" fontWeight = "600" > { d . shortLabel } </ text > ;
5253 } ) }
5354 </ svg >
5455 ) ;
@@ -57,30 +58,30 @@ function RadarChart({ values, dimensions, size = 320 }) {
5758function MitigationCard ( { group, active, accent, t } ) {
5859 const [ open , setOpen ] = useState ( false ) ;
5960 return (
60- < div style = { { border : `2px solid ${ active ? accent : "#1e293b " } ` , borderRadius : 12 , background : active ? `${ accent } 10` : "#0f172a " , padding : "12px 14px" , opacity : active ? 1 : 0.5 , transition : "all 0.3s" } } >
61+ < div style = { { border : `2px solid ${ active ? accent : "var(--border-subtle) " } ` , borderRadius : 12 , background : active ? `${ accent } 10` : "var(--bg-main) " , padding : "12px 14px" , opacity : active ? 1 : 0.5 , transition : "all 0.3s" } } >
6162 < div onClick = { ( ) => active && setOpen ( ! open ) } style = { { display : "flex" , justifyContent : "space-between" , alignItems : "center" , cursor : active ? "pointer" : "default" } } >
6263 < div style = { { display : "flex" , alignItems : "center" , gap : 8 } } >
6364 < span style = { { fontSize : 18 } } > { group . icon } </ span >
6465 < div >
65- < div style = { { fontWeight : 700 , fontSize : 13 , color : active ? "#f8fafc " : "#94a3b8 " } } > { group . title } </ div >
66- < div style = { { fontSize : 10 , color : "#94a3b8 " } } > { group . measures . length } { group . measures . length !== 1 ? t . measures : t . measure } </ div >
66+ < div style = { { fontWeight : 700 , fontSize : 13 , color : active ? "var(--text-heading) " : "var(--text-secondary) " } } > { group . title } </ div >
67+ < div style = { { fontSize : 10 , color : "var(--text-secondary) " } } > { group . measures . length } { group . measures . length !== 1 ? t . measures : t . measure } </ div >
6768 </ div >
6869 </ div >
69- { active && < span style = { { fontSize : 16 , color : "#94a3b8 " , transform : open ? "rotate(180deg)" : "rotate(0)" , transition : "transform 0.2s" } } > ▾</ span > }
70+ { active && < span style = { { fontSize : 16 , color : "var(--text-secondary) " , transform : open ? "rotate(180deg)" : "rotate(0)" , transition : "transform 0.2s" } } > ▾</ span > }
7071 </ div >
7172 { active && open && (
7273 < div style = { { marginTop : 10 , display : "flex" , flexDirection : "column" , gap : 6 } } >
7374 { group . measures . map ( ( m , i ) => {
7475 const tc = TYPE_COLORS [ m . type ] ;
7576 return (
76- < div key = { i } style = { { background : "#1e293b " , borderRadius : 8 , padding : "8px 10px" , borderLeft : `3px solid ${ tc . color } ` } } >
77+ < div key = { i } style = { { background : "var(--bg-card) " , borderRadius : 8 , padding : "8px 10px" , borderLeft : `3px solid ${ tc . color } ` } } >
7778 < div style = { { display : "flex" , justifyContent : "space-between" , alignItems : "center" , gap : 6 , flexWrap : "wrap" } } >
78- < span style = { { fontWeight : 600 , fontSize : 12 , color : "#e2e8f0 " } } > { m . name } </ span >
79+ < span style = { { fontWeight : 600 , fontSize : 12 , color : "var(--text-primary) " } } > { m . name } </ span >
7980 < span style = { { fontSize : 8 , fontWeight : 700 , color : tc . color , background : tc . bg , padding : "2px 5px" , borderRadius : 3 , textTransform : "uppercase" , letterSpacing : "0.05em" } } >
8081 { t . typeBadges [ m . type ] }
8182 </ span >
8283 </ div >
83- < div style = { { fontSize : 11 , color : "#94a3b8 " , marginTop : 3 , lineHeight : 1.4 } } > { m . desc } </ div >
84+ < div style = { { fontSize : 11 , color : "var(--text-secondary) " , marginTop : 3 , lineHeight : 1.4 } } > { m . desc } </ div >
8485 </ div >
8586 ) ;
8687 } ) }
@@ -94,10 +95,10 @@ const adoc = Asciidoctor();
9495
9596const ADOC_SIDEBAR_STYLES = `
9697 .adoc-content p { margin: 0.5em 0; }
97- .adoc-content a { color: #38bdf8 ; text-decoration: underline; text-decoration-color: #38bdf844 ; }
98- .adoc-content a:hover { text-decoration-color: #38bdf8 ; }
99- .adoc-content strong { color: #e2e8f0 ; }
100- .adoc-content code { background: #1e293b ; padding: 1px 4px; border-radius: 3px; font-size: 0.92em; }
98+ .adoc-content a { color: var(--link) ; text-decoration: underline; text-decoration-color: var(--link-underline) ; }
99+ .adoc-content a:hover { text-decoration-color: var(--link) ; }
100+ .adoc-content strong { color: var(--text-primary) ; }
101+ .adoc-content code { background: var(--bg-card) ; padding: 1px 4px; border-radius: 3px; font-size: 0.92em; }
101102` ;
102103
103104function DocSidebar ( { docs, open, onClose } ) {
@@ -126,36 +127,36 @@ function DocSidebar({ docs, open, onClose }) {
126127 style = { {
127128 position : "fixed" , top : 0 , right : 0 , bottom : 0 ,
128129 width : open ? "min(480px, 85vw)" : "0" ,
129- background : "#111827 " , borderLeft : open ? "1px solid #334155 " : "none" ,
130+ background : "var(--bg-sidebar) " , borderLeft : open ? "1px solid var(--border) " : "none" ,
130131 overflowY : "auto" , overflowX : "hidden" ,
131132 transition : "width 0.3s ease" ,
132133 zIndex : 1000 ,
133- boxShadow : open ? "-8px 0 30px rgba(0,0,0,0.5 )" : "none" ,
134+ boxShadow : open ? "-8px 0 30px var(--shadow )" : "none" ,
134135 } }
135136 >
136137 { open && (
137138 < div style = { { padding : "24px 20px" } } >
138139 < style > { ADOC_SIDEBAR_STYLES } </ style >
139140 < div style = { { display : "flex" , justifyContent : "space-between" , alignItems : "center" , marginBottom : 24 } } >
140- < h2 style = { { margin : 0 , fontSize : 18 , fontWeight : 700 , color : "#f8fafc " } } > 📖 { docs . title } </ h2 >
141- < button onClick = { onClose } style = { { background : "#1e293b " , border : "1px solid #334155 " , borderRadius : 6 , color : "#94a3b8 " , padding : "4px 10px" , cursor : "pointer" , fontSize : 12 } } > ✕</ button >
141+ < h2 style = { { margin : 0 , fontSize : 18 , fontWeight : 700 , color : "var(--text-heading) " } } > { docs . title } </ h2 >
142+ < button onClick = { onClose } style = { { background : "var(--bg-card) " , border : "1px solid var(--border) " , borderRadius : 6 , color : "var(--text-secondary) " , padding : "4px 10px" , cursor : "pointer" , fontSize : 12 } } > ✕</ button >
142143 </ div >
143144 { rendered . map ( ( sec ) => (
144145 < div key = { sec . id } style = { {
145146 marginBottom : 28 ,
146- ...( sec . id === "disclaimer" ? { background : "#1e293b " , borderRadius : 10 , padding : "14px 16px" , border : "1px solid #334155 " } : { } ) ,
147+ ...( sec . id === "disclaimer" ? { background : "var(--bg-card) " , borderRadius : 10 , padding : "14px 16px" , border : "1px solid var(--border) " } : { } ) ,
147148 } } >
148- < h3 style = { { fontSize : 15 , fontWeight : 700 , color : sec . id === "disclaimer" ? "#f59e0b" : "#e2e8f0 " , margin : "0 0 10px" , paddingBottom : 6 , borderBottom : sec . id === "disclaimer" ? "none" : "1px solid #1e293b " } } >
149+ < h3 style = { { fontSize : 15 , fontWeight : 700 , color : sec . id === "disclaimer" ? "#f59e0b" : "var(--text-primary) " , margin : "0 0 10px" , paddingBottom : 6 , borderBottom : sec . id === "disclaimer" ? "none" : "1px solid var(--border-subtle) " } } >
149150 { sec . id === "disclaimer" ? "\u26A0\uFE0F " : "" } { sec . title }
150151 </ h3 >
151152 < div
152153 className = "adoc-content"
153- style = { { fontSize : 13 , color : "#94a3b8 " , lineHeight : 1.7 } }
154+ style = { { fontSize : 13 , color : "var(--text-secondary) " , lineHeight : 1.7 } }
154155 dangerouslySetInnerHTML = { { __html : sec . html } }
155156 />
156157 </ div >
157158 ) ) }
158- < div style = { { borderTop : "1px solid #1e293b " , paddingTop : 16 , marginTop : 8 , fontSize : 11 , color : "#94a3b8 " , textAlign : "center" } } >
159+ < div style = { { borderTop : "1px solid var(--border-subtle) " , paddingTop : 16 , marginTop : 8 , fontSize : 11 , color : "var(--text-secondary) " , textAlign : "center" } } >
159160 Generated with data from Veracode, CodeRabbit, BaxBench, Unit 42, Aikido Security, CSA, and others.
160161 </ div >
161162 </ div >
@@ -164,8 +165,18 @@ function DocSidebar({ docs, open, onClose }) {
164165 ) ;
165166}
166167
168+ function detectBrowserLanguage ( ) {
169+ const nav = navigator . language || navigator . userLanguage || "" ;
170+ return nav . startsWith ( "de" ) ? "de" : "en" ;
171+ }
172+
167173export default function RiskRadar ( ) {
168- const [ lang , setLang ] = useState ( "de" ) ;
174+ const [ lang , setLang ] = useState ( ( ) => {
175+ const saved = localStorage . getItem ( "lang" ) ;
176+ if ( saved === "de" || saved === "en" ) return saved ;
177+ return detectBrowserLanguage ( ) ;
178+ } ) ;
179+ const { theme, setTheme, isDark } = useTheme ( ) ;
169180 const [ docsOpen , setDocsOpen ] = useState ( false ) ;
170181 const [ values , setValues ] = useState ( { codeType : 0 , language : 1 , deployment : 0 , data : 0 , blastRadius : 0 } ) ;
171182 const t = T [ lang ] ;
@@ -175,33 +186,41 @@ export default function RiskRadar() {
175186 const set = ( k , v ) => setValues ( ( p ) => ( { ...p , [ k ] : v } ) ) ;
176187 const activeCount = t . mitigations . filter ( ( g ) => g . tier <= ti + 1 ) . reduce ( ( s , g ) => s + g . measures . length , 0 ) ;
177188
189+ const toggleLang = ( ) => {
190+ const next = lang === "de" ? "en" : "de" ;
191+ setLang ( next ) ;
192+ localStorage . setItem ( "lang" , next ) ;
193+ } ;
194+
195+ const btnStyle = { background : "var(--bg-card)" , border : "1px solid var(--border)" , borderRadius : 6 , color : "var(--text-secondary)" , padding : "4px 10px" , cursor : "pointer" , fontSize : 12 , fontWeight : 600 } ;
196+
178197 return (
179- < div style = { { fontFamily : "'Inter', system-ui, sans-serif" , background : "#0f172a " , color : "#e2e8f0 " , minHeight : "100vh" , padding : "20px 16px" , transition : "margin-right 0.3s" , marginRight : docsOpen ? "min(480px, 85vw)" : 0 } } >
198+ < div style = { { fontFamily : "'Inter', system-ui, sans-serif" , background : "var(--bg-main) " , color : "var(--text-primary) " , minHeight : "100vh" , padding : "20px 16px" , transition : "margin-right 0.3s" , marginRight : docsOpen ? "min(480px, 85vw)" : 0 } } >
180199 { /* Top bar */ }
181200 < div style = { { display : "flex" , justifyContent : "flex-end" , gap : 6 , marginBottom : 12 , maxWidth : 800 , margin : "0 auto 12px" } } >
182- < button
183- onClick = { ( ) => setLang ( lang === "de" ? "en " : "de" ) }
184- style = { { background : "#1e293b" , border : "1px solid #334155" , borderRadius : 6 , color : "#94a3b8" , padding : "4px 10px" , cursor : "pointer" , fontSize : 12 , fontWeight : 600 } }
185- >
186- 🌐 { t . langSwitch }
201+ < button onClick = { ( ) => setTheme ( isDark ? "light" : "dark" ) } style = { btnStyle } aria-label = "Toggle theme" >
202+ { isDark ? "\u2600\uFE0F" : "\uD83C\uDF19" } { isDark ? "Light " : "Dark" }
203+ </ button >
204+ < button onClick = { toggleLang } style = { btnStyle } >
205+ { t . langSwitch }
187206 </ button >
188207 < button
189208 onClick = { ( ) => setDocsOpen ( ! docsOpen ) }
190- style = { { background : docsOpen ? `${ tc } 22` : "#1e293b " , border : `1px solid ${ docsOpen ? tc : "#334155 " } ` , borderRadius : 6 , color : docsOpen ? "#f8fafc " : "#94a3b8" , padding : "4px 10px" , cursor : "pointer" , fontSize : 12 , fontWeight : 600 } }
209+ style = { { ... btnStyle , background : docsOpen ? `${ tc } 22` : "var(--bg-card) " , border : `1px solid ${ docsOpen ? tc : "var(--border) " } ` , color : docsOpen ? "var(--text-heading) " : "var(--text-secondary)" } }
191210 >
192- 📖 { docsOpen ? t . closeButton : t . docsButton }
211+ { docsOpen ? t . closeButton : t . docsButton }
193212 </ button >
194213 </ div >
195214
196- < h1 style = { { fontSize : 22 , fontWeight : 700 , textAlign : "center" , margin : "0 0 4px" , color : "#f8fafc " } } > { t . title } </ h1 >
197- < p style = { { textAlign : "center" , color : "#94a3b8 " , fontSize : 13 , margin : "0 0 18px" } } > { t . subtitle } </ p >
215+ < h1 style = { { fontSize : 22 , fontWeight : 700 , textAlign : "center" , margin : "0 0 4px" , color : "var(--text-heading) " } } > { t . title } </ h1 >
216+ < p style = { { textAlign : "center" , color : "var(--text-secondary) " , fontSize : 13 , margin : "0 0 18px" } } > { t . subtitle } </ p >
198217
199218 { /* Presets */ }
200219 < div style = { { display : "flex" , flexWrap : "wrap" , gap : 5 , justifyContent : "center" , marginBottom : 18 } } >
201220 { t . presets . map ( ( p ) => {
202221 const active = JSON . stringify ( values ) === JSON . stringify ( p . values ) ;
203222 return (
204- < button key = { p . name } onClick = { ( ) => setValues ( p . values ) } style = { { padding : "4px 9px" , fontSize : 11 , borderRadius : 6 , border : active ? `2px solid ${ tc } ` : "1px solid #334155 " , background : active ? `${ tc } 22` : "#1e293b " , color : active ? "#f8fafc " : "#cbd5e1 " , cursor : "pointer" , fontWeight : active ? 600 : 400 , transition : "all 0.15s" } } >
223+ < button key = { p . name } onClick = { ( ) => setValues ( p . values ) } style = { { padding : "4px 9px" , fontSize : 11 , borderRadius : 6 , border : active ? `2px solid ${ tc } ` : "1px solid var(--border) " , background : active ? `${ tc } 22` : "var(--bg-card) " , color : active ? "var(--text-heading) " : "var(--text-muted) " , cursor : "pointer" , fontWeight : active ? 600 : 400 , transition : "all 0.15s" } } >
205224 { p . name }
206225 </ button >
207226 ) ;
@@ -216,7 +235,7 @@ export default function RiskRadar() {
216235 < span style = { { fontSize : 26 , fontWeight : 800 , color : tc } } > { ti + 1 } </ span >
217236 < div >
218237 < div style = { { fontWeight : 700 , fontSize : 14 , color : tc } } > { tier . label } </ div >
219- < div style = { { fontSize : 11 , color : "#94a3b8 " } } > { tier . desc } </ div >
238+ < div style = { { fontSize : 11 , color : "var(--text-secondary) " } } > { tier . desc } </ div >
220239 </ div >
221240 </ div >
222241
@@ -232,7 +251,7 @@ export default function RiskRadar() {
232251 < span style = { { fontSize : 10 , color : sc , fontWeight : 600 } } > { dim . levels [ v ] } </ span >
233252 </ div >
234253 < input type = "range" min = { 0 } max = { 4 } step = { 1 } value = { v } onChange = { ( e ) => set ( dim . key , parseInt ( e . target . value ) ) } style = { { width : "100%" , accentColor : sc , height : 5 , cursor : "pointer" } } />
235- < div style = { { display : "flex" , justifyContent : "space-between" , fontSize : 9 , color : "#94a3b8 " , marginTop : 1 } } >
254+ < div style = { { display : "flex" , justifyContent : "space-between" , fontSize : 9 , color : "var(--text-secondary) " , marginTop : 1 } } >
236255 < span > { t . low } </ span > < span > { t . high } </ span >
237256 </ div >
238257 </ div >
@@ -241,30 +260,30 @@ export default function RiskRadar() {
241260 </ div >
242261
243262 { /* Mitigations */ }
244- < div style = { { width : "100%" , maxWidth : 500 , borderTop : "1px solid #1e293b " , paddingTop : 18 } } >
263+ < div style = { { width : "100%" , maxWidth : 500 , borderTop : "1px solid var(--border-subtle) " , paddingTop : 18 } } >
245264 < div style = { { display : "flex" , justifyContent : "space-between" , alignItems : "baseline" , marginBottom : 12 } } >
246265 < h2 style = { { fontSize : 16 , fontWeight : 700 , margin : 0 } } > { t . mitigationHeading } </ h2 >
247- < span style = { { fontSize : 11 , color : "#94a3b8 " } } > { activeCount } { t . active } </ span >
266+ < span style = { { fontSize : 11 , color : "var(--text-secondary) " } } > { activeCount } { t . active } </ span >
248267 </ div >
249268 < div style = { { display : "flex" , gap : 10 , flexWrap : "wrap" , marginBottom : 12 } } >
250269 { Object . entries ( TYPE_COLORS ) . map ( ( [ key , c ] ) => (
251270 < div key = { key } style = { { display : "flex" , alignItems : "center" , gap : 4 } } >
252271 < div style = { { width : 9 , height : 9 , borderRadius : 2 , background : c . color } } />
253- < span style = { { fontSize : 10 , color : "#94a3b8 " } } > { t . typeBadges [ key ] } </ span >
272+ < span style = { { fontSize : 10 , color : "var(--text-secondary) " } } > { t . typeBadges [ key ] } </ span >
254273 </ div >
255274 ) ) }
256275 </ div >
257- < div style = { { background : "#1e293b " , borderRadius : 8 , padding : "8px 12px" , marginBottom : 14 , fontSize : 11 , color : "#94a3b8 " , lineHeight : 1.5 , borderLeft : `3px solid ${ tc } ` } } >
258- < strong style = { { color : "#e2e8f0 " } } > { t . cumulative } :</ strong > { t . cumulativeNote ( ti , t . mitigations [ ti ] . title ) }
276+ < div style = { { background : "var(--bg-card) " , borderRadius : 8 , padding : "8px 12px" , marginBottom : 14 , fontSize : 11 , color : "var(--text-secondary) " , lineHeight : 1.5 , borderLeft : `3px solid ${ tc } ` } } >
277+ < strong style = { { color : "var(--text-primary) " } } > { t . cumulative } :</ strong > { t . cumulativeNote ( ti , t . mitigations [ ti ] . title ) }
259278 </ div >
260279 < div style = { { display : "flex" , flexDirection : "column" , gap : 8 } } >
261280 { t . mitigations . map ( ( g ) => < MitigationCard key = { g . tier } group = { g } active = { g . tier <= ti + 1 } accent = { TIER_BG [ g . tier - 1 ] } t = { t } /> ) }
262281 </ div >
263282 </ div >
264283
265- < div style = { { marginTop : 24 , fontSize : 10 , color : "#94a3b8 " , textAlign : "center" , lineHeight : 1.8 } } >
266- < div > v{ VERSION } · < a href = "https://github.com/LLM-Coding/vibe-coding-risk-radar" target = "_blank" rel = "noopener" style = { { color : "#94a3b8 " } } > { t . footer . github } </ a > · < a href = { `docs/risk-radar${ lang === "en" ? "-en" : "" } .html` } target = "_blank" rel = "noopener" style = { { color : "#94a3b8 " } } > { t . footer . fullDocs } </ a > </ div >
267- < div > { t . footer . madeBy } < a href = "https://www.linkedin.com/in/rdmueller" target = "_blank" rel = "noopener" style = { { color : "#94a3b8 " } } > Ralf D. Müller</ a > </ div >
284+ < div style = { { marginTop : 24 , fontSize : 10 , color : "var(--text-secondary) " , textAlign : "center" , lineHeight : 1.8 } } >
285+ < div > v{ VERSION } · < a href = "https://github.com/LLM-Coding/vibe-coding-risk-radar" target = "_blank" rel = "noopener" style = { { color : "var(--text-secondary) " } } > { t . footer . github } </ a > · < a href = { `docs/risk-radar${ lang === "en" ? "-en" : "" } .html` } target = "_blank" rel = "noopener" style = { { color : "var(--text-secondary) " } } > { t . footer . fullDocs } </ a > </ div >
286+ < div > { t . footer . madeBy } < a href = "https://www.linkedin.com/in/rdmueller" target = "_blank" rel = "noopener" style = { { color : "var(--text-secondary) " } } > Ralf D. Müller</ a > </ div >
268287 </ div >
269288 </ div >
270289
0 commit comments