Compare commits
10 Commits
fix/buildc
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 47606ac0a8 | |||
| 16a0861f77 | |||
| f4787d0a27 | |||
| 40f051bc77 | |||
| 9f2faed91f | |||
| a98badf653 | |||
| f9d618d0dd | |||
| cbac2b0771 | |||
| 117a1385ab | |||
| 3ec528701e |
@@ -19,6 +19,7 @@ COPY package*.json ./
|
||||
|
||||
COPY --from=build-stage /app/dist /usr/share/nginx/html
|
||||
COPY --from=build-stage /app/public/icons /usr/share/nginx/html/icons
|
||||
COPY --from=build-stage /app/public/fonts /usr/share/nginx/html/fonts
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY BuildConfig.sh /docker-entrypoint.d/10-build-config.sh
|
||||
RUN chmod a+x /docker-entrypoint.d/10-build-config.sh
|
||||
|
||||
BIN
public/fonts/-F63fjptAgt5VM-kVkqdyU8n1i8q1w.woff2
Normal file
BIN
public/fonts/-F63fjptAgt5VM-kVkqdyU8n1i8q1w.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F63fjptAgt5VM-kVkqdyU8n1iAq129k.woff2
Normal file
BIN
public/fonts/-F63fjptAgt5VM-kVkqdyU8n1iAq129k.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F63fjptAgt5VM-kVkqdyU8n1iEq129k.woff2
Normal file
BIN
public/fonts/-F63fjptAgt5VM-kVkqdyU8n1iEq129k.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F63fjptAgt5VM-kVkqdyU8n1iIq129k.woff2
Normal file
BIN
public/fonts/-F63fjptAgt5VM-kVkqdyU8n1iIq129k.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F63fjptAgt5VM-kVkqdyU8n1isq129k.woff2
Normal file
BIN
public/fonts/-F63fjptAgt5VM-kVkqdyU8n1isq129k.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F6pfjptAgt5VM-kVkqdyU8n1ioa0Xdgv-s.woff2
Normal file
BIN
public/fonts/-F6pfjptAgt5VM-kVkqdyU8n1ioa0Xdgv-s.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F6pfjptAgt5VM-kVkqdyU8n1ioa1Xdg.woff2
Normal file
BIN
public/fonts/-F6pfjptAgt5VM-kVkqdyU8n1ioa1Xdg.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F6pfjptAgt5VM-kVkqdyU8n1ioa23dgv-s.woff2
Normal file
BIN
public/fonts/-F6pfjptAgt5VM-kVkqdyU8n1ioa23dgv-s.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F6pfjptAgt5VM-kVkqdyU8n1ioa2Hdgv-s.woff2
Normal file
BIN
public/fonts/-F6pfjptAgt5VM-kVkqdyU8n1ioa2Hdgv-s.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F6pfjptAgt5VM-kVkqdyU8n1ioa2ndgv-s.woff2
Normal file
BIN
public/fonts/-F6pfjptAgt5VM-kVkqdyU8n1ioa2ndgv-s.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3twJwl1FgtIU.woff2
Normal file
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3twJwl1FgtIU.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3twJwl5FgtIU.woff2
Normal file
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3twJwl5FgtIU.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3twJwl9FgtIU.woff2
Normal file
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3twJwl9FgtIU.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3twJwlBFgg.woff2
Normal file
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3twJwlBFgg.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3twJwlRFgtIU.woff2
Normal file
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3twJwlRFgtIU.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3vAOwl1FgtIU.woff2
Normal file
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3vAOwl1FgtIU.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3vAOwl5FgtIU.woff2
Normal file
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3vAOwl5FgtIU.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3vAOwl9FgtIU.woff2
Normal file
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3vAOwl9FgtIU.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3vAOwlBFgg.woff2
Normal file
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3vAOwlBFgg.woff2
Normal file
Binary file not shown.
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3vAOwlRFgtIU.woff2
Normal file
BIN
public/fonts/-F6qfjptAgt5VM-kVkqdyU8n3vAOwlRFgtIU.woff2
Normal file
Binary file not shown.
BIN
public/fonts/RWmVoLyb5fEqtsfBX9PDZIGr2tFubRh7A3eREnc.woff2
Normal file
BIN
public/fonts/RWmVoLyb5fEqtsfBX9PDZIGr2tFubRh7A3eREnc.woff2
Normal file
Binary file not shown.
BIN
public/fonts/RWmVoLyb5fEqtsfBX9PDZIGr2tFubRh7AneREnc.woff2
Normal file
BIN
public/fonts/RWmVoLyb5fEqtsfBX9PDZIGr2tFubRh7AneREnc.woff2
Normal file
Binary file not shown.
BIN
public/fonts/RWmVoLyb5fEqtsfBX9PDZIGr2tFubRh7DXeR.woff2
Normal file
BIN
public/fonts/RWmVoLyb5fEqtsfBX9PDZIGr2tFubRh7DXeR.woff2
Normal file
Binary file not shown.
207
public/fonts/fonts.css
Normal file
207
public/fonts/fonts.css
Normal file
@@ -0,0 +1,207 @@
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(./-F6pfjptAgt5VM-kVkqdyU8n1ioa2Hdgv-s.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(./-F6pfjptAgt5VM-kVkqdyU8n1ioa0Xdgv-s.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(./-F6pfjptAgt5VM-kVkqdyU8n1ioa2ndgv-s.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(./-F6pfjptAgt5VM-kVkqdyU8n1ioa23dgv-s.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(./-F6pfjptAgt5VM-kVkqdyU8n1ioa1Xdg.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(./-F63fjptAgt5VM-kVkqdyU8n1iIq129k.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(./-F63fjptAgt5VM-kVkqdyU8n1isq129k.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(./-F63fjptAgt5VM-kVkqdyU8n1iAq129k.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(./-F63fjptAgt5VM-kVkqdyU8n1iEq129k.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(./-F63fjptAgt5VM-kVkqdyU8n1i8q1w.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3twJwl1FgtIU.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3twJwlRFgtIU.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3twJwl9FgtIU.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3twJwl5FgtIU.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3twJwlBFgg.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3vAOwl1FgtIU.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3vAOwlRFgtIU.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3vAOwl9FgtIU.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3vAOwl5FgtIU.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3vAOwlBFgg.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Major Mono Display';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(./RWmVoLyb5fEqtsfBX9PDZIGr2tFubRh7AneREnc.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Major Mono Display';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(./RWmVoLyb5fEqtsfBX9PDZIGr2tFubRh7A3eREnc.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Major Mono Display';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(./RWmVoLyb5fEqtsfBX9PDZIGr2tFubRh7DXeR.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
44
public/icons/hangman-lab-favicon.svg
Normal file
44
public/icons/hangman-lab-favicon.svg
Normal file
@@ -0,0 +1,44 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 733.000000 733.000000" role="img" aria-label="Hangman Lab">
|
||||
<g transform="translate(0.000000,733.000000) scale(0.100000,-0.100000)"
|
||||
fill="#6a8018" stroke="none">
|
||||
<path d="M812 6860 c-66 -40 -73 -66 -70 -242 3 -141 5 -159 24 -185 43 -58
|
||||
63 -63 262 -64 l182 -1 0 -2694 0 -2694 -290 0 c-314 0 -332 -3 -380 -54 -24
|
||||
-27 -25 -32 -28 -199 -3 -195 4 -218 71 -250 32 -16 257 -17 3084 -17 2955 0
|
||||
3049 1 3083 19 65 34 75 66 75 236 0 186 -16 224 -104 254 -25 8 -682 11
|
||||
-2512 11 l-2479 0 0 1998 0 1997 698 697 697 697 670 -3 c369 -2 680 -4 693
|
||||
-5 22 -1 22 -1 22 -269 l0 -268 84 -12 c47 -7 103 -19 125 -27 41 -14 41 -14
|
||||
43 283 l3 297 631 1 c698 2 673 -1 714 67 18 28 20 51 20 187 0 173 -8 201
|
||||
-72 240 -33 20 -56 20 -2623 20 -2567 0 -2590 0 -2623 -20z m1725 -500 c-1 -3
|
||||
-183 -185 -404 -404 l-403 -399 0 405 0 406 405 -1 c223 -1 404 -4 402 -7z
|
||||
M4283 5701 c-459 -125 -690 -624 -483 -1046 67 -138 196 -266 335 -335 281
|
||||
-139 612 -93 837 118 235 219 297 575 152 870 -85 174 -235 306 -427 375 -111
|
||||
40 -302 48 -414 18z m267 -211 c95 -15 201 -70 274 -144 273 -274 173 -723
|
||||
-189 -851 -93 -33 -236 -35 -326 -5 -202 68 -340 249 -356 465 -18 242 153
|
||||
471 394 529 71 18 119 19 203 6z M5945 4716 c-56 -25 -80 -61 -80 -122 0 -48
|
||||
4 -57 36 -90 88 -88 219 -29 219 98 0 45 -34 96 -75 114 -42 17 -61 17 -100 0z
|
||||
M5675 4466 c-60 -26 -73 -109 -26 -157 62 -61 161 -19 161 70 0 74 -68 118
|
||||
-135 87z M3747 4397 c-10 -7 -226 -223 -479 -482 -307 -313 -467 -483 -479
|
||||
-510 -35 -75 -17 -177 38 -227 39 -35 114 -61 160 -54 100 13 104 17 583 554
|
||||
l145 162 3 -404 c3 -455 15 -342 -137 -1186 -76 -423 -101 -587 -101 -664 0
|
||||
-190 182 -307 377 -242 62 21 136 95 152 152 6 22 29 188 52 369 57 466 109
|
||||
839 118 848 4 4 52 6 106 5 l99 -3 67 -210 c38 -115 68 -220 69 -232 0 -12
|
||||
-38 -152 -85 -311 -47 -159 -85 -302 -85 -317 0 -128 109 -225 255 -225 109 1
|
||||
185 42 228 125 26 51 237 683 237 710 0 11 -54 182 -121 380 -121 360 -121
|
||||
360 -134 605 -8 135 -14 292 -14 350 l0 105 152 -158 c84 -87 166 -167 182
|
||||
-177 44 -29 98 -26 135 8 38 35 187 379 173 401 -5 8 -34 20 -64 26 -59 11
|
||||
-38 -8 -393 362 -89 93 -100 97 -174 59 -59 -30 -161 -62 -229 -71 -52 -7 -53
|
||||
-7 -53 -41 0 -44 -21 -84 -61 -118 -38 -32 -41 0 32 -455 l52 -325 -82 -78
|
||||
c-45 -43 -85 -78 -89 -78 -4 0 -42 35 -84 78 l-77 77 34 215 c19 118 46 289
|
||||
61 379 26 164 26 164 -5 188 -42 33 -54 58 -61 121 -5 54 -5 54 -92 87 -98 38
|
||||
-184 89 -243 145 -76 70 -123 87 -168 57z M5504 4170 c-74 -30 -69 -170 6
|
||||
-170 19 0 20 -7 20 -123 0 -122 0 -122 -54 -262 -30 -77 -97 -243 -150 -369
|
||||
-106 -252 -113 -287 -73 -347 46 -70 38 -69 560 -69 327 0 475 3 490 11 69 35
|
||||
107 109 88 172 -5 17 -62 142 -126 277 -225 470 -215 443 -215 586 0 114 2
|
||||
124 18 124 57 0 72 101 23 151 -29 29 -29 29 -298 28 -147 0 -278 -4 -289 -9z
|
||||
m376 -307 c1 -148 1 -148 81 -333 101 -231 98 -222 68 -216 -13 3 -116 22
|
||||
-229 42 -112 19 -208 37 -212 40 -4 2 18 75 48 160 54 156 54 156 54 305 l0
|
||||
149 95 0 95 0 0 -147z m-219 -593 c22 0 49 -42 49 -75 0 -46 -29 -75 -74 -75
|
||||
-37 0 -76 36 -76 71 0 46 45 94 78 83 8 -2 18 -4 23 -4z m327 -126 c53 -59 -7
|
||||
-144 -80 -113 -54 22 -65 83 -22 125 30 31 67 27 102 -12z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
44
public/icons/hangman-lab-logo.svg
Normal file
44
public/icons/hangman-lab-logo.svg
Normal file
@@ -0,0 +1,44 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 733.000000 733.000000" role="img" aria-label="Hangman Lab">
|
||||
<g transform="translate(0.000000,733.000000) scale(0.100000,-0.100000)"
|
||||
fill="#d8ff3e" stroke="none">
|
||||
<path d="M812 6860 c-66 -40 -73 -66 -70 -242 3 -141 5 -159 24 -185 43 -58
|
||||
63 -63 262 -64 l182 -1 0 -2694 0 -2694 -290 0 c-314 0 -332 -3 -380 -54 -24
|
||||
-27 -25 -32 -28 -199 -3 -195 4 -218 71 -250 32 -16 257 -17 3084 -17 2955 0
|
||||
3049 1 3083 19 65 34 75 66 75 236 0 186 -16 224 -104 254 -25 8 -682 11
|
||||
-2512 11 l-2479 0 0 1998 0 1997 698 697 697 697 670 -3 c369 -2 680 -4 693
|
||||
-5 22 -1 22 -1 22 -269 l0 -268 84 -12 c47 -7 103 -19 125 -27 41 -14 41 -14
|
||||
43 283 l3 297 631 1 c698 2 673 -1 714 67 18 28 20 51 20 187 0 173 -8 201
|
||||
-72 240 -33 20 -56 20 -2623 20 -2567 0 -2590 0 -2623 -20z m1725 -500 c-1 -3
|
||||
-183 -185 -404 -404 l-403 -399 0 405 0 406 405 -1 c223 -1 404 -4 402 -7z
|
||||
M4283 5701 c-459 -125 -690 -624 -483 -1046 67 -138 196 -266 335 -335 281
|
||||
-139 612 -93 837 118 235 219 297 575 152 870 -85 174 -235 306 -427 375 -111
|
||||
40 -302 48 -414 18z m267 -211 c95 -15 201 -70 274 -144 273 -274 173 -723
|
||||
-189 -851 -93 -33 -236 -35 -326 -5 -202 68 -340 249 -356 465 -18 242 153
|
||||
471 394 529 71 18 119 19 203 6z M5945 4716 c-56 -25 -80 -61 -80 -122 0 -48
|
||||
4 -57 36 -90 88 -88 219 -29 219 98 0 45 -34 96 -75 114 -42 17 -61 17 -100 0z
|
||||
M5675 4466 c-60 -26 -73 -109 -26 -157 62 -61 161 -19 161 70 0 74 -68 118
|
||||
-135 87z M3747 4397 c-10 -7 -226 -223 -479 -482 -307 -313 -467 -483 -479
|
||||
-510 -35 -75 -17 -177 38 -227 39 -35 114 -61 160 -54 100 13 104 17 583 554
|
||||
l145 162 3 -404 c3 -455 15 -342 -137 -1186 -76 -423 -101 -587 -101 -664 0
|
||||
-190 182 -307 377 -242 62 21 136 95 152 152 6 22 29 188 52 369 57 466 109
|
||||
839 118 848 4 4 52 6 106 5 l99 -3 67 -210 c38 -115 68 -220 69 -232 0 -12
|
||||
-38 -152 -85 -311 -47 -159 -85 -302 -85 -317 0 -128 109 -225 255 -225 109 1
|
||||
185 42 228 125 26 51 237 683 237 710 0 11 -54 182 -121 380 -121 360 -121
|
||||
360 -134 605 -8 135 -14 292 -14 350 l0 105 152 -158 c84 -87 166 -167 182
|
||||
-177 44 -29 98 -26 135 8 38 35 187 379 173 401 -5 8 -34 20 -64 26 -59 11
|
||||
-38 -8 -393 362 -89 93 -100 97 -174 59 -59 -30 -161 -62 -229 -71 -52 -7 -53
|
||||
-7 -53 -41 0 -44 -21 -84 -61 -118 -38 -32 -41 0 32 -455 l52 -325 -82 -78
|
||||
c-45 -43 -85 -78 -89 -78 -4 0 -42 35 -84 78 l-77 77 34 215 c19 118 46 289
|
||||
61 379 26 164 26 164 -5 188 -42 33 -54 58 -61 121 -5 54 -5 54 -92 87 -98 38
|
||||
-184 89 -243 145 -76 70 -123 87 -168 57z M5504 4170 c-74 -30 -69 -170 6
|
||||
-170 19 0 20 -7 20 -123 0 -122 0 -122 -54 -262 -30 -77 -97 -243 -150 -369
|
||||
-106 -252 -113 -287 -73 -347 46 -70 38 -69 560 -69 327 0 475 3 490 11 69 35
|
||||
107 109 88 172 -5 17 -62 142 -126 277 -225 470 -215 443 -215 586 0 114 2
|
||||
124 18 124 57 0 72 101 23 151 -29 29 -29 29 -298 28 -147 0 -278 -4 -289 -9z
|
||||
m376 -307 c1 -148 1 -148 81 -333 101 -231 98 -222 68 -216 -13 3 -116 22
|
||||
-229 42 -112 19 -208 37 -212 40 -4 2 18 75 48 160 54 156 54 156 54 305 l0
|
||||
149 95 0 95 0 0 -147z m-219 -593 c22 0 49 -42 49 -75 0 -46 -29 -75 -74 -75
|
||||
-37 0 -76 36 -76 71 0 46 45 94 78 83 8 -2 18 -4 23 -4z m327 -126 c53 -59 -7
|
||||
-144 -80 -113 -54 22 -65 83 -22 125 30 31 67 27 102 -12z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -5,20 +5,11 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="color-scheme" content="dark">
|
||||
<title>Hangman Lab</title>
|
||||
<link rel="icon" type="image/png" href="./icons/logo.png">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link rel="icon" type="image/svg+xml" href="/icons/hangman-lab-favicon.svg?v=hl-20260517b">
|
||||
<link rel="stylesheet" href="/fonts/fonts.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<link
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -2,7 +2,8 @@ import React, { useEffect, useState } from "react";
|
||||
import {Link, useParams} from "react-router-dom";
|
||||
import "katex/dist/katex.min.css";
|
||||
import "./MarkdownContent.css";
|
||||
import { Settings2, Pencil } from "lucide-react";
|
||||
import { Settings2, Pencil, User, Clock, History } from "lucide-react";
|
||||
import { formatDateTime } from "../../lib/utils";
|
||||
import MarkdownView from "./MarkdownView";
|
||||
import PatchCards from "./PatchCards";
|
||||
import PermissionGuard from "../PermissionGuard";
|
||||
@@ -85,6 +86,23 @@ const MarkdownContent = () => {
|
||||
</div>
|
||||
</PermissionGuard>
|
||||
</div>
|
||||
<div className="-mt-3 mb-6 flex flex-wrap items-center gap-x-5 gap-y-1 font-mono text-xs text-muted-foreground">
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<User className="h-3.5 w-3.5 text-secondary" />
|
||||
{markdown.author || "—"}
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<Clock className="h-3.5 w-3.5" />
|
||||
created {formatDateTime(markdown.created_at)}
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<History className="h-3.5 w-3.5" />
|
||||
updated {formatDateTime(markdown.updated_at)}
|
||||
{markdown.last_modified_by
|
||||
? ` by ${markdown.last_modified_by}`
|
||||
: ""}
|
||||
</span>
|
||||
</div>
|
||||
<MarkdownView content={parseMarkdownContent(markdown.content)} template={template}/>
|
||||
<PatchCards markdownId={id} />
|
||||
<MarkdownSettingModal
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: ui-monospace, "JetBrains Mono", "Courier New", Courier, monospace;
|
||||
font-family: ui-monospace, "IBM Plex Mono", "Courier New", Courier, monospace;
|
||||
background-color: hsl(var(--muted));
|
||||
color: hsl(var(--foreground));
|
||||
padding: 2px 6px;
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
.markdown-preview h4,
|
||||
.markdown-preview h5,
|
||||
.markdown-preview h6 {
|
||||
font-family: "JetBrains Mono", ui-monospace, monospace;
|
||||
font-family: "IBM Plex Mono", ui-monospace, monospace;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--foreground));
|
||||
line-height: 1.3;
|
||||
@@ -108,7 +108,7 @@
|
||||
|
||||
/* Inline code */
|
||||
.markdown-preview :not(pre) > code {
|
||||
font-family: "JetBrains Mono", ui-monospace, monospace;
|
||||
font-family: "IBM Plex Mono", ui-monospace, monospace;
|
||||
font-size: 0.85em;
|
||||
background-color: hsl(var(--muted));
|
||||
color: hsl(var(--primary));
|
||||
@@ -128,7 +128,7 @@
|
||||
box-shadow: inset 0 0 40px -20px hsl(var(--primary) / 0.25);
|
||||
}
|
||||
.markdown-preview pre code {
|
||||
font-family: "JetBrains Mono", ui-monospace, monospace;
|
||||
font-family: "IBM Plex Mono", ui-monospace, monospace;
|
||||
font-size: 0.85rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
@@ -150,7 +150,7 @@
|
||||
}
|
||||
.markdown-preview th {
|
||||
background-color: hsl(var(--muted));
|
||||
font-family: "JetBrains Mono", ui-monospace, monospace;
|
||||
font-family: "IBM Plex Mono", ui-monospace, monospace;
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import { Plus, Pencil, Trash2, Save, Layers } from "lucide-react";
|
||||
import { Plus, Pencil, Trash2, Save, Layers, User, Clock } from "lucide-react";
|
||||
import { formatDateTime } from "../../lib/utils";
|
||||
import MarkdownView from "./MarkdownView";
|
||||
import PermissionGuard from "../PermissionGuard";
|
||||
import {
|
||||
@@ -137,6 +138,22 @@ const PatchCards = ({ markdownId }) => {
|
||||
</div>
|
||||
</PermissionGuard>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 border-b border-border/40 px-5 py-1.5 font-mono text-[11px] text-muted-foreground">
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<User className="h-3 w-3 text-secondary" />
|
||||
{patch.author || "—"}
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<Clock className="h-3 w-3" />
|
||||
{formatDateTime(patch.created_at)}
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1">
|
||||
edited {formatDateTime(patch.updated_at)}
|
||||
{patch.last_modified_by
|
||||
? ` by ${patch.last_modified_by}`
|
||||
: ""}
|
||||
</span>
|
||||
</div>
|
||||
<div className="px-5 py-4">
|
||||
<MarkdownView content={{ markdown: patch.content }} />
|
||||
</div>
|
||||
|
||||
@@ -11,14 +11,16 @@ import {
|
||||
import { Button } from '../ui/button';
|
||||
import { Input, Label } from '../ui/input';
|
||||
|
||||
const AVAILABLE_ROLES = ['guest', 'creator', 'admin'];
|
||||
// Must match the backend allowlist (admin|creator|user).
|
||||
const AVAILABLE_ROLES = ['user', 'creator', 'agent', 'admin'];
|
||||
|
||||
const SELECT_CLASS =
|
||||
"flex h-9 w-full rounded-md border border-input bg-background/60 px-3 py-1 text-sm text-foreground transition-colors focus-visible:outline-none focus-visible:border-primary/60 focus-visible:ring-2 focus-visible:ring-ring/40 disabled:cursor-not-allowed disabled:opacity-50";
|
||||
|
||||
const ApiKeyCreationModal = ({ isOpen, onClose }) => {
|
||||
const [alias, setAlias] = useState('');
|
||||
const [name, setName] = useState('');
|
||||
const [roles, setRoles] = useState(['guest']);
|
||||
const [roles, setRoles] = useState(['user']);
|
||||
const [generatedKey, setGeneratedKey] = useState(null);
|
||||
const createApiKeyMutation = useCreateApiKey();
|
||||
|
||||
@@ -44,12 +46,17 @@ const ApiKeyCreationModal = ({ isOpen, onClose }) => {
|
||||
};
|
||||
|
||||
const handleGenerate = async () => {
|
||||
if (!alias.trim()) {
|
||||
alert('Alias is required');
|
||||
return;
|
||||
}
|
||||
if (!name.trim()) {
|
||||
alert('API key name is required');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const result = await createApiKeyMutation.mutateAsync({
|
||||
alias: alias.trim(),
|
||||
name: name.trim(),
|
||||
roles: roles
|
||||
});
|
||||
@@ -61,7 +68,7 @@ const ApiKeyCreationModal = ({ isOpen, onClose }) => {
|
||||
};
|
||||
|
||||
const handleCopy = () => {
|
||||
navigator.clipboard.writeText(generatedKey)
|
||||
navigator.clipboard.writeText(generatedKey.key)
|
||||
.then(() => alert('API key copied to clipboard'))
|
||||
.catch(err => console.error('failed to copy api key:', err));
|
||||
};
|
||||
@@ -80,6 +87,20 @@ const ApiKeyCreationModal = ({ isOpen, onClose }) => {
|
||||
</DialogHeader>
|
||||
{!generatedKey ? (
|
||||
<div className="space-y-5">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="api-key-alias">Alias</Label>
|
||||
<Input
|
||||
id="api-key-alias"
|
||||
type="text"
|
||||
placeholder="unique alias (reuse to renew)"
|
||||
value={alias}
|
||||
onChange={(e) => setAlias(e.target.value)}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Unique. Using an existing alias renews that
|
||||
key (same key string, validity reset).
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="api-key-name">Name</Label>
|
||||
<Input
|
||||
@@ -130,7 +151,9 @@ const ApiKeyCreationModal = ({ isOpen, onClose }) => {
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-lg border border-secondary/40 bg-secondary/10 px-4 py-3 font-mono text-sm text-secondary">
|
||||
Please copy your API key immediately! It will only be displayed once!
|
||||
{generatedKey.renewed
|
||||
? "Key renewed — same key string, validity reset 15 days."
|
||||
: "Please copy your API key immediately! It will only be displayed once!"}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="generated-api-key">Your API Key</Label>
|
||||
@@ -154,7 +177,7 @@ const ApiKeyCreationModal = ({ isOpen, onClose }) => {
|
||||
{!generatedKey && (
|
||||
<Button
|
||||
onClick={handleGenerate}
|
||||
disabled={createApiKeyMutation.isLoading || !name.trim()}
|
||||
disabled={createApiKeyMutation.isLoading || !alias.trim() || !name.trim()}
|
||||
>
|
||||
<KeyRound className="h-4 w-4" /> Generate
|
||||
</Button>
|
||||
|
||||
@@ -124,13 +124,13 @@ const MainNavigation = () => {
|
||||
className="group flex items-center gap-2.5 pr-3"
|
||||
>
|
||||
<img
|
||||
src="/icons/logo.png"
|
||||
alt="Logo"
|
||||
className="h-7 w-7 rounded"
|
||||
src="/icons/hangman-lab-logo.svg"
|
||||
alt="Hangman Lab"
|
||||
className="h-7 w-7"
|
||||
/>
|
||||
<span className="font-mono text-sm font-semibold tracking-tight">
|
||||
<span className="text-foreground">HANGMAN</span>
|
||||
<span className="neon-text">//LAB</span>
|
||||
<span className="font-display text-base leading-none tracking-tight">
|
||||
<span className="text-foreground">hangman</span>
|
||||
<span className="neon-text"> lab</span>
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
|
||||
@@ -2,37 +2,40 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* Dark-tech design tokens (single dark theme). HSL triplets so Tailwind
|
||||
color utilities and shadcn-style primitives resolve via hsl(var(--x)). */
|
||||
/* Lab-terminal theme — adapted from the Gitea STYLE.md for this React app.
|
||||
Single acid accent (#d8ff3e), dark blueprint surfaces, monospace UI.
|
||||
HSL triplets so Tailwind / shadcn-style primitives resolve hsl(var(--x)). */
|
||||
:root,
|
||||
.dark {
|
||||
--background: 222 32% 6%;
|
||||
--foreground: 210 22% 92%;
|
||||
--background: 216 24% 4%; /* #080a0d ink */
|
||||
--foreground: 217 35% 86%; /* #cdd8e8 text */
|
||||
|
||||
--surface: 222 28% 8.5%;
|
||||
--card: 222 26% 9.5%;
|
||||
--card-foreground: 210 22% 92%;
|
||||
--surface: 214 29% 8%; /* #0f141b panel */
|
||||
--card: 214 29% 8%;
|
||||
--card-foreground: 217 38% 95%; /* #e9f0fa strong text */
|
||||
|
||||
--muted: 222 20% 14%;
|
||||
--muted-foreground: 215 16% 60%;
|
||||
--muted: 214 27% 12%; /* hovered panel */
|
||||
--muted-foreground: 216 18% 48%; /* #647691 dim / eyebrow */
|
||||
|
||||
--accent: 222 24% 13%;
|
||||
--accent-foreground: 210 22% 92%;
|
||||
--accent: 214 27% 11%; /* row/element hover wash base */
|
||||
--accent-foreground: 217 35% 86%;
|
||||
|
||||
--border: 220 18% 18%;
|
||||
--input: 220 18% 16%;
|
||||
--ring: 186 92% 56%;
|
||||
--border: 213 28% 16%; /* #1d2733 lines / dividers */
|
||||
--input: 213 28% 14%;
|
||||
--ring: 72 100% 62%; /* acid focus ring */
|
||||
|
||||
/* cyan primary, violet secondary — the neon accents */
|
||||
--primary: 186 92% 56%;
|
||||
--primary-foreground: 222 47% 6%;
|
||||
--secondary: 258 90% 68%;
|
||||
--secondary-foreground: 0 0% 100%;
|
||||
/* THE accent — one acid, used for links/primary/active/focus. */
|
||||
--primary: 72 100% 62%; /* #d8ff3e */
|
||||
--primary-foreground: 96 24% 5%; /* #0c0f08 text on acid */
|
||||
/* No second hue (STYLE: "one acid"). Secondary == acid so any
|
||||
component using it stays on-brand. */
|
||||
--secondary: 72 100% 62%;
|
||||
--secondary-foreground: 96 24% 5%;
|
||||
|
||||
--destructive: 0 72% 56%;
|
||||
--destructive: 3 100% 66%; /* #ff5a52 errors only */
|
||||
--destructive-foreground: 0 0% 100%;
|
||||
|
||||
--radius: 0.5rem;
|
||||
--radius: 0.75rem;
|
||||
}
|
||||
|
||||
* {
|
||||
@@ -45,25 +48,37 @@ body,
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
html {
|
||||
background-color: hsl(var(--background));
|
||||
/* Larger base so all rem-based UI text scales up. */
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: hsl(var(--background));
|
||||
background-color: transparent;
|
||||
color: hsl(var(--foreground));
|
||||
font-family: Inter, ui-sans-serif, system-ui, sans-serif;
|
||||
font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, monospace;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
/* Ambient grid + glow backdrop for the app shell */
|
||||
/* Site-wide blueprint grid backdrop (STYLE §11.2): fixed, behind content,
|
||||
48px #1d2733 lines at low opacity, radial-masked, plus one acid sheen. */
|
||||
body::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
background:
|
||||
radial-gradient(60rem 60rem at 110% -10%, hsl(258 90% 68% / 0.10), transparent 60%),
|
||||
radial-gradient(50rem 50rem at -10% 110%, hsl(186 92% 56% / 0.08), transparent 60%);
|
||||
background-color: hsl(var(--background));
|
||||
background-image:
|
||||
radial-gradient(1000px 560px at 78% -8%, hsl(72 100% 62% / 0.10), transparent 60%),
|
||||
linear-gradient(hsl(213 28% 16% / 0.16) 1px, transparent 1px),
|
||||
linear-gradient(90deg, hsl(213 28% 16% / 0.16) 1px, transparent 1px);
|
||||
background-size: 100% 100%, 48px 48px, 48px 48px;
|
||||
-webkit-mask-image: radial-gradient(ellipse 90% 80% at 50% 30%, #000 55%, transparent 100%);
|
||||
mask-image: radial-gradient(ellipse 90% 80% at 50% 30%, #000 55%, transparent 100%);
|
||||
}
|
||||
|
||||
::selection {
|
||||
@@ -71,7 +86,6 @@ body::before {
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
/* Slim techy scrollbars */
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: hsl(var(--border)) transparent;
|
||||
@@ -89,16 +103,24 @@ body::before {
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
/* Display lockup font — always lowercase per STYLE §3. */
|
||||
.font-display {
|
||||
font-family: "Major Mono Display", "IBM Plex Mono", monospace;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
.neon-text {
|
||||
color: hsl(var(--primary));
|
||||
text-shadow: 0 0 12px hsl(var(--primary) / 0.55);
|
||||
text-shadow: 0 0 44px hsl(var(--primary) / 0.38);
|
||||
}
|
||||
/* kept for compatibility with components that used the old violet
|
||||
accent — now the same single acid. */
|
||||
.neon-text-violet {
|
||||
color: hsl(var(--secondary));
|
||||
text-shadow: 0 0 12px hsl(var(--secondary) / 0.55);
|
||||
color: hsl(var(--primary));
|
||||
text-shadow: 0 0 44px hsl(var(--primary) / 0.38);
|
||||
}
|
||||
.glass {
|
||||
background: hsl(var(--surface) / 0.7);
|
||||
backdrop-filter: blur(10px);
|
||||
background: hsl(214 29% 8% / 0.55);
|
||||
backdrop-filter: blur(16px) saturate(150%);
|
||||
border: 1px solid hsl(var(--primary) / 0.16);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,3 +5,17 @@ import { twMerge } from "tailwind-merge";
|
||||
export function cn(...inputs) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
/** Format a backend datetime string for display; '—' when missing/invalid. */
|
||||
export function formatDateTime(value) {
|
||||
if (!value) return "—";
|
||||
const d = new Date(value);
|
||||
if (isNaN(d.getTime())) return "—";
|
||||
return d.toLocaleString(undefined, {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -42,15 +42,10 @@ module.exports = {
|
||||
sm: "calc(var(--radius) - 4px)",
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ["Inter", "ui-sans-serif", "system-ui", "sans-serif"],
|
||||
mono: [
|
||||
"JetBrains Mono",
|
||||
"Fira Code",
|
||||
"ui-monospace",
|
||||
"SFMono-Regular",
|
||||
"Menlo",
|
||||
"monospace",
|
||||
],
|
||||
// Lab-terminal: IBM Plex Mono is the body/UI/mono face.
|
||||
sans: ["IBM Plex Mono", "ui-monospace", "SFMono-Regular", "monospace"],
|
||||
mono: ["IBM Plex Mono", "ui-monospace", "SFMono-Regular", "monospace"],
|
||||
display: ["Major Mono Display", "IBM Plex Mono", "monospace"],
|
||||
},
|
||||
boxShadow: {
|
||||
glow: "0 0 0 1px hsl(var(--primary) / 0.25), 0 0 24px -6px hsl(var(--primary) / 0.45)",
|
||||
|
||||
Reference in New Issue
Block a user