1(function () { 2 /* global React */ 3 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 4 // Chrome â Sidebar, TopNav, BotanicalAnchor, Footer 5 // Persistent across all pages 6 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 7 8 const { 9 useEffect, 10 useState 11 } = React; 12 13 // ###### THEME (LIGHT / DARK) ###### 14 // Drives [data-theme="dark"] on <html>. theme-dark.css picks it up. 15 // Persisted in localStorage so the choice sticks across reloads. 16 const THEME_KEY = "cg.theme"; 17 function readStoredTheme() { 18 try { 19 return localStorage.getItem(THEME_KEY) === "dark" ? "dark" : "light"; 20 } catch (e) { 21 return "light"; 22 } 23 } 24 function applyTheme(theme) { 25 if (theme === "dark") document.documentElement.setAttribute("data-theme", "dark");else document.documentElement.removeAttribute("data-theme"); 26 try { 27 localStorage.setItem(THEME_KEY, theme); 28 } catch (e) {} 29 } 30 31 // ###### SVG ICON SET ###### 32 // Stroke-based, forest green 33 function Ico({ 34 name 35 }) { 36 const common = { 37 width: 18, 38 height: 18, 39 viewBox: "0 0 24 24", 40 fill: "none", 41 stroke: "currentColor", 42 strokeWidth: 1.6, 43 strokeLinecap: "round", 44 strokeLinejoin: "round", 45 "aria-hidden": true, 46 focusable: false 47 }; 48 if (name === "github") return /*#__PURE__*/React.createElement("svg", common, /*#__PURE__*/React.createElement("path", { 49 d: "M9 19c-4.3 1.4-4.3-2.5-6-3M15 22v-4a3 3 0 0 0-.9-2.4c3-.3 6-1.5 6-6.7A5.2 5.2 0 0 0 18.7 5a4.8 4.8 0 0 0-.1-3.6S17.3 1 15 2.7a13.4 13.4 0 0 0-7 0C5.7 1 4.4
491.4 4.4 1.4A4.8 4.8 0 0 0 4.3 5a5.2 5.2 0 0 0-1.4 3.6c0 5.2 3 6.4 6 6.7A3 3 0 0 0 8 18v4" 50 })); 51 if (name === "linkedin") return /*#__PURE__*/React.createElement("svg", common, /*#__PURE__*/React.createElement("path", { 52 d: "M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-4 0v7h-4v-7a6 6 0 0 1 6-6z" 53 }), /*#__PURE__*/React.createElement("rect", { 54 x: 2, 55 y: 9, 56 width: 4, 57 height: 12 58 }), /*#__PURE__*/React.createElement("circle", { 59 cx: 4, 60 cy: 4, 61 r: 2 62 })); 63 if (name === "stack") return /*#__PURE__*/React.createElement("svg", common, /*#__PURE__*/React.createElement("path", { 64 d: "M3 12h14M3 16h14M3 8l13 2M5 4l11 4M17 18v3H3v-3" 65 })); 66 if (name === "pin") return /*#__PURE__*/React.createElement("svg", common, /*#__PURE__*/React.createElement("path", { 67 d: "M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" 68 }), /*#__PURE__*/React.createElement("circle", { 69 cx: 12, 70 cy: 10, 71 r: 3 72 })); 73 if (name === "mail") return /*#__PURE__*/React.createElement("svg", common, /*#__PURE__*/React.createElement("path", { 74 d: "M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z" 75 }), /*#__PURE__*/React.createElement("path", { 76 d: "M22 6l-10 7L2 6" 77 })); 78 return null; 79 } 80 81 // ###### SIDEBAR ###### 82 function Sidebar({ 83 onNav 84 }) { 85 const [theme, setTheme] = useState(readStoredTheme); 86 useEffect(() => { 87 applyTheme(theme); 88 }, [theme]); 89 const toggleTheme = () => setTheme(t => t === "dark" ? "light" : "dark"); 90 return /*#__PURE__*/React.createElement("aside", { 91 className: "sidebar" 92 }, /*#__PURE__*/React.createElement("a", { 93 className: "brand", 94 href: "#", 95 onClick: e => { 96 e.preventDefault(); 97 if (onNav) onNav("home"); 98 }, 99 "aria-label": "Cristian Gabriel, Home", 100 title: "Home" 101 }, /*#__PURE__*/React.createElement("span", { 102 className: "brand-mark" 103 }, /*#__PURE__*/React.createElement("svg", { 104 viewBox: "0 0 40 56", 105 width: 44, 106 height: 60, 107 fill: "none" 108 }, /*#__PURE__*/React.createElement("path", { 109 d: "M 20 54 C 20 44 18 36 18 28", 110 stroke: "var(--forest)", 111 strokeWidth: 1.4, 112 strokeLinecap: "round" 113 }), /*#__PURE__*/React.createElement("path", { 114 d: "M 18 28 C 4 22 2 10 6 2 C 18 4 22 18 18 28 Z", 115 fill: "var(--forest)" 116 }), /*#__PURE__*/React.createElement("path", { 117 d: "M 19 36 C 30 32 34 24 32 16 C 22 18 18 28 19 36 Z", 118 stroke: "var(--forest)", 119 strokeWidth: 1.3 120 }), /*#__PURE__*/React.createElement("circle", { 121 cx: 20, 122 cy: 54, 123 r: 1.4, 124 fill: "var(--forest)" 125 }))), /*#__PURE__*/React.createElement("div", { 126 className: "brand-rule" 127 }), /*#__PURE__*/React.createElement("div", { 128 className: "brand-name" 129 }, /*#__PURE__*/React.createElement("span", null, "Cristian"), /*#__PURE__*/React.createElement("span", null, "Gabriel")), /*#__PURE__*/React.createElement("div", { 130 className: "brand-role" 131 }, "Software Developer")), /*#__PURE__*/React.createElement("nav", { 132 className: "sidebar-socials", 133 "aria-label": "Social and contact links" 134 }, /*#__PURE__*/React.createElement("a", { 135 className: "social", 136 href: "https://github.com/cristiangabriel88", 137 target: "_blank", 138 rel: "noopener" 139 }, /*#__PURE__*/React.createElement("span", { 140 className: "social-ico" 141 }, /*#__PURE__*/React.createElement(Ico, { 142 name: "github" 143 })), /*#__PURE__*/React.createElement("span", { 144 className: "social-label" 145 }, "GitHub")), /*#__PURE__*/React.createElement("a", { 146 className: "social", 147 href: "https://www.linkedin.com/in/cristian-gabriel-constantinescu-781a6b237", 148 target: "_blank", 149 rel: "noopener" 150 }, /*#__PURE__*/React.createElement("span", { 151 className: "social-ico" 152 }, /*#__PURE__*/React.createElement(Ico, { 153 name: "linkedin" 154 })), /*#__PURE__*/React.createElement("span", { 155 className: "social-label" 156 }, "LinkedIn")), /*#__PURE__*/React.createElement("a", { 157 className: "social", 158 href: "https://stackoverflow.com/users/18754276/cristiangabriel", 159 target: "_blank", 160 rel: "noopener" 161 }, /*#__PURE__*/React.createElement("span", { 162 className: "social-ico" 163 }, /*#__PURE__*/React.createElement(Ico, { 164 name: "stack" 165 })), /*#__PURE__*/React.createElement("span", { 166 className: "social-label" 167 }, "Stack Overflow")), /*#__PURE__*/React.createElement("a", { 168 className: "social", 169 href: "https://www.google.com/maps/search/?api=1&query=Bucharest%2C+Romania", 170 target: "_blank", 171 rel: "noopener noreferrer" 172 }, /*#__PURE__*/React.createElement("span", { 173 className: "social-ico" 174 }, /*#__PURE__*/React.createElement(Ico, { 175 name: "pin" 176 })), /*#__PURE__*/React.createElement("span", { 177 className: "social-label" 178 }, "Bucharest, RO")), /*#__PURE__*/React.createElement("a", { 179 className: "social", 180 href: "mailto:[email protected]" 181 }, /*#__PURE__*/React.createElement("span", { 182 className: "social-ico" 183 }, /*#__PURE__*/React.createElement(Ico, { 184 name: "mail" 185 })), /*#__PURE__*/React.createElement("span", { 186 className: "social-label" 187 }, "Email me"))), /*#__PURE__*/React.createElement("div", { 188 className: "sidebar-footer" 189 }, /*#__PURE__*/React.createElement("span", null, "\xA9", " ", /*#__PURE__*/React.createElement("span", { 190 style: { 191 fontFamily: "var(--serif)", 192 fontStyle: "italic", 193 textTransform: "none", 194 fontSize: 12, 195 letterSpacing: 0 196 } 197 }, new Date().getFullYear())), /*#__PURE__*/React.createElement("button", { 198 className: "theme-toggle", 199 onClick: toggleTheme, 200 title: theme === "dark" ? "Light mode" : "Dark mode", 201 "aria-label": "Toggle theme" 202 }, theme === "dark" ? /*#__PURE__*/React.createElement("svg", {
203 viewBox: "0 0 16 16", 204 width: 13, 205 height: 13, 206 fill: "currentColor", 207 "aria-hidden": "true" 208 }, /*#__PURE__*/React.createElement("circle", { 209 cx: 8, 210 cy: 8, 211 r: 3 212 }), /*#__PURE__*/React.createElement("g", { 213 stroke: "currentColor", 214 strokeWidth: 1.2, 215 strokeLinecap: "round" 216 }, /*#__PURE__*/React.createElement("line", { 217 x1: 8, 218 y1: 1, 219 x2: 8, 220 y2: 3 221 }), /*#__PURE__*/React.createElement("line", { 222 x1: 8, 223 y1: 13, 224 x2: 8, 225 y2: 15 226 }), /*#__PURE__*/React.createElement("line", { 227 x1: 1, 228 y1: 8, 229 x2: 3, 230 y2: 8 231 }), /*#__PURE__*/React.createElement("line", { 232 x1: 13, 233 y1: 8, 234 x2: 15, 235 y2: 8 236 }), /*#__PURE__*/React.createElement("line", { 237 x1: 3, 238 y1: 3, 239 x2: 4.4, 240 y2: 4.4 241 }), /*#__PURE__*/React.createElement("line", { 242 x1: 11.6, 243 y1: 11.6, 244 x2: 13, 245 y2: 13 246 }), /*#__PURE__*/React.createElement("line", { 247 x1: 3, 248 y1: 13, 249 x2: 4.4, 250 y2: 11.6 251 }), /*#__PURE__*/React.createElement("line", { 252 x1: 11.6, 253 y1: 4.4, 254 x2: 13, 255 y2: 3 256 }))) : /*#__PURE__*/React.createElement("svg", { 257 viewBox: "0 0 16 16", 258 width: 13, 259 height: 13, 260 fill: "currentColor", 261 "aria-hidden": "true" 262 }, /*#__PURE__*/React.createElement("path", { 263 d: "M 8 1 A 7 7 0 1 0 15 8 A 5 5 0 0 1 8 1 Z" 264 }))))); 265 } 266 267 // ###### TOP NAV ###### 268 function TopNav({ 269 page, 270 onNav 271 }) { 272 const pages = [{ 273 id: "home", 274 label: "Home" 275 }, {
276 id: "about", 277 label: "About" 278 }, { 279 id: "projects", 280 label: "Projects" 281 }]; 282 return /*#__PURE__*/React.createElement("nav", { 283 className: "topnav", 284 "aria-label": "Primary" 285 }, pages.map(p => /*#__PURE__*/React.createElement("button", { 286 key: p.id, 287 className: "nav-btn" + (page === p.id ? " active" : ""), 288 onClick: () => onNav(p.id), 289 "aria-current": page === p.id ? "page" : undefined 290 }, p.label))); 291 } 292 293 // ###### BOTANICAL ANCHOR ###### 294 // Decorative plant photo per page. home.png and about.png have black/colored 295 // backgrounds and render as-is; the projects anchor is a white-bg PNG that 296 // uses mix-blend-multiply (see styles.css) to drop the white cleanly. 297 const BOTANICAL_SRC = { 298 home: "resources/images/home.webp", 299 about: "resources/images/about.webp", 300 projects: "resources/images/contact2.webp" 301 }; 302 function BotanicalAnchor({ 303 page 304 }) { 305 return /*#__PURE__*/React.createElement("img", { 306 key: page, 307 className: `botanical ${page} fade-in`, 308 src: BOTANICAL_SRC[page], 309 alt: "", 310 "aria-hidden": "true", 311 draggable: "false" 312 }); 313 } 314 315 // ###### FOOTER ###### 316 function PageFooter({ 317 page 318 }) { 319 return /*#__PURE__*/React.createElement("div", { 320 className: "footer" 321 }, /*#__PURE__*/React.createElement("span", { 322 className: "ssh-hint", 323 style: { 324 color: "var(--forest)", 325 letterSpacing: "0.22em" 326 } 327 }, "Type ", /*#__PURE__*/React.createElement("span", { 328 className: "ssh-word" 329 }, "ssh"), " to open a shell")); 330 } 331 Object.assign(window, { 332 Sidebar, 333 TopNav, 334 BotanicalAnchor, 335 PageFooter, 336 Ico 337 }); 338})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.