+ {format(parseISO(post.date), 'MMMM dd, yyyy')} +
++ + + {post.title} + + +
+{post.description}
++ + Подробнее... + +
+Робототехника, программирование
+ {posts.map((post) => ( ++ {format(parseISO(post.date), 'MMMM dd, yyyy')} +
+{post.description}
++ + Подробнее... + +
++ {format(parseISO(frontMatter.date), 'MMMM dd, yyyy')} +
+dAMQaiH zm^r3v#$Q#2T=X>bsY#D%s!bhs^M9PMAcHbCc0FMHV{u-dwlL;a1eJ63v5U*?Q_8JO zT#50!RD619#j_Uf))0ooADz~*9&lN!bBDRUgE>Vud-i5ck%vT=r^yD*^?Mp@Q^v+V zG#-?gKlr}Eeqifb{|So?HM&g91P8|av8hQoCmQXkd?7wIJw b z_^v8bbg` SAn{I*4bH$u(RZ6*x UhuA~hc=8czK8SHEKTzSxgbwi~9(OqJB&gwb^l4+m`k*Q;_?>Y-APi1{k zAHQ)P)G)f|AyjSgcCFps)Fh6Bca*Xznq36!pV6Az&m{O8$wGFD? zY&O*3*J0;_EqM#jh6^gMQKpXV?#1?>$ml1xvh8nSN>-?H=V;nJIwB07YX$e6vLxH( zqYwQ>qxwR(i4f)DLd)-$P>T-no_c!LsN@)8`e;W@)-Hj0>nJ-}Kla4-ZdPJzI&Mce zv)V_j;(3ERN3_@I$N<^|4Lf`B;8n+bX@bHbcZTopEmDI*Jfl)-pFDvo6svPRoo@(x z);_{lY<;);XzT`dBFpRmGrr}z5u1=p C^ S-{ce6iXQlLGcItwJ^mZx{m$&DA_oEZ)B{_bYPq-HA zcH8WGoBG(aBU_j)vEy+_71T34@4dmSg!|M8Vf92Zj6WH7Q7t#OHQqWgFE3ARt+%!T z?oLovLVlnf?2c7pTc)~cc^($_8nyKwsN`RA-23ed3sdj(ys%pjjM+9JrctL;dy8a( z@en&CQmnV(()bu|Y%G1-4a(6x{aLytn$T-;(&{QIJB9vMox11U-1HpD@d(QkaJdEb zG{)+6Dos_L+O3NpWo^=gR?evp|CqEG?L&Ut#D*KLaRFOgOEK(Kq1@!EGcTfo+%A&I z=dLbB+d$u{sh?u)xP{PF8L%;YPPW53+@{>5W=Jt#wQpN;0_HYdw1{ksf_XhO4#2F= zyPx6Lx2<92L-;L5PD`zn6zwIH`Jk( $?Qw({erA$^bC;q33hv!d!>%wRhj# zal^hk+WGNg;rJtb-EB(?czvOM=H7dl=vblBwAv>}%1@{}mnpUznfq1cE^sgsL0*4I zJ##!*B?=vI_OEVis5o+_IwMIRrpQyT_Sq~ZU%oY7c5JMIADzpD!Upz9h@iWg_>>~j zOLS;wp^i$-E?4<_cp?RiS%Rd?i;f*mOz=~(&3lo<=@(nR!_Rqiprh@weZlL!t#NCc zO!QTcInq|%#>OVgobj{~ixEUec`E25zJ~*DofsQdzIa@5^nOXj2T;8O`l--(QyU ^$t?TGY^7#&FQ+2SS3B#qK*k3`ye?8jUYSajE5iBbJls75CCc(m3dk{t?- zopcER9{Z?TC)mk~gpi^kbbu>b-+a{m#8-y2^p$ka4n60w;Sc2}HMf<8JUvh CL0B&Btk)T`ctE$*qNW8L$`7!r^9T+>=<=2qaq-;ll2{`{Rg zc5a0ZUI$oG&j-qVOuKa=*v4aY#IsoM+1|c4Z)<}lEDvy;5huB@1RJPquU2U*U-;gu z=En2m+qjBzR#DEJDO`WU)hdd{Vj%^0V*KoyZ|5lzV87&g_j~NCjwv0uQVqXOb*QrQ zy|Qn`hxx(58c 70$E;L(X0uZZ72M1!6oeg)(cdKO ze0gDaTz+ohR-#d)NbAH4x{I(21yjwvBQfmpLu$)|m{XolbgF!pmsqJ#D}(ylp6uC> z{bqtcI#hT#HW=wl7>p!38sKsJ`r8}lt-q%Keqy%u(xk=yiIJiUw6|5IvkS+#?JTBl z8H5(Q?l#wzazujH!8o>1xtn8#_w+397* _cy8!pQGP%K(Ga3pAjsaTbbXJlQF_+m+-UpUUent@xM zg%jqLUExj~o^vQ3Gl*>wh=_gOr2*|U64_iXb+-111a H}$TjeajM+I20xw(((>fej-@CIz4S1pi$(#}P7`4({6QS2CaQS4NPENDp>sAqD z$bH4KGzXGffkJ7R>V>)>tC)uax{UsN*dbeNC*v}#8Y#OWYwL4t$ePR?VTyIs!wea+ z5Urmc)X|^`MG~*dS6pGSbU+gPJoq*^a=_>$n4|P^w$sMBBy@f*Z^Jg6?n5?oId6f{ z$LW4M|4m502z0t7g<#Bx%X;9<=)smFolV&(V^(7Cv2-sxbxopQ!)*#ZRhTBpx1)Fc zNm1T%bONzv6@#|dz(w02AH8OXe>kQ#1FMCzO}2J_mST)+ExmBr9cva-@?;wnmWMOk z{3_~EX_xadgJGv&H@zK_8{(x84`}+c?oSBX*Ge3VdfTt&F}yCpFP?CpW+BE^cWY0^ zb&uBN!Ja3UzYHK-CTyA5=L zEMW{l3Usky#ly=7px648W31UNV@K)&Ub&zP1c7%)`{);I4b0Q<)B}3;NMG2JH=X$U zfIW4)4n9ZM`-yRj67I)YSLDK)qfUJ_ij}a#aZN~9EXrh8eZY2&=uY%2N0UFF7<~%M zsB8=erOWZ>Ct_#^tHZ|*q`H;A)5;ycw*I cmVxi8_0Xk}aJA^ath+E;xg!x+As(M#0=)3!NJR6H&9+zd#iP(m0PIW8$ z1Y^VX`>jm`W!=WpF*{ioM?C9`yOR>@0q=u7o>BP-eSHqCgMDj!2anwH?s%i2p+Q7D zzszIf5XJpE)IG4;d_(La-xenmF(tgAxK`Y4sQ}BSJEPs6N_U2vI{8=0C_F?@7<(G; zo$~G=8p+076G;`}>{MQ>t>7cm=zGtfbdDXm6||jUU|?X?CaE?(<6bKDYKeHlz}DA8 zXT={X=yp_R;HfJ9h%?eWvQ!dRgz&Su*JfNt!Wu>|XfU &68iRikRrHRW|ZxzRR^`eIGt zIeiDgVS>IeExKVRWW8-= A= yA`}`)ZkWBrZD`hpWIxBGkh&f#ijr449~m`j6{4jiJ*C!oVA8ZC?$1RM#K(_b zL9TW)kN*Y4%^-qPpMP7d4)o?Nk#>aoYHT(*g)qmRUb?**F@pnNiy6Fv9rEiUqD(^O zzyS?nBrX63BTRYduaG(0VVG2yJRe%o&rVrLjbxTaAFTd8s;<<@Qs>u(<193R8>}2_ zuwp{7;H2a*X7_jryzriZXMg?bTuegABb^87@SsKkr2)0Gyiax8KQWstw^v #ix45EVrcEhr>!NMhprl $InQMzjSFH54x5k9qHc`@9uKQzvL4ihcq{^B zPrVR=o_ic%Y>6&rMN)hTZsI7I<3&`#(nl+3y3ys9A~ &^=4?PL&nd8)`OfG#n zwAMN$1&>K++c{^|7< 4P=2y(B{jJsQ0a#U;HTo4ZmWZYvI{+s;Td{Yzem%0*k#)vjpB zia;J&>}ICate44SFYY3vEelqStQWFihx%^vQ@Do(sOy7yR2@WNv7Y9I^yL=nZr3mb zXKV5t@=?-Sk|b{XMhA7ZGB@2hqsx}4xwCW!in#C zI@}sc Zlr3-NFJ@NFaJlhyfcw{k^vvtGl`N9xSo**rDW4S}i zM9{fMPWo%4wYDG~BZ18BD+}h|GQKc-g^{++3MY>}W_uq7jGHx{mwE9fZiPCoxN$+7 zrODGGJrOkcPQUB(FD5aoS4g~7#6NR^ma7-!>mHuJfY5kTe6PpNNKC9GGRiu^L31uG z$7v`*JknQHsYB!Tm_W{a32TM099djW%5e+j0Ve_ct}IM>XLF1Ap+YvcrLV=|CKo6S zb+ 9Nl3_YdKP6%Cxy@6TxZ>;4&nTneadr z_ES90ydCev)LV!dN=#(*f}|ZORFdvkYBni^aLbUk>BajeWIOcmHP#8S)*2U~QKI%S zyrLmtPqb&TphJ;>yAxri#;{uyk`JJqODDw%(Z=2 `1uc}br^V%>j!gS)D*q*f_-qf8&D;W1dJgQMlaH5er zN2U<%Smb7==vE}dDI8K7cKz!vs^73o9f>2sgiTzWcwY|BMYHH5%Vn7#kiw&eItCqa zIkR2~Q}>X=Ar8W|^Ms41Fm8o6IB2_j60eOeBB1Br!boW7JnoeX6Gs)?7rW0^5psc- zjS16yb>dFn>KPOF;imD}e!enuIniFzv}n$m2#gCCv4jM#ArwlzZ$7@9&XkFxZ4n!V zj3dyiwW4Ki2QG{@i>yuZXQizw_OkZI^-3otXC{!(lUpJF33gI60ak;Uqitp74|B6I zgg{b=Iz}WkhCGj1M =hu4#Aw173YxIVbISaoc z-nLZC*6Tgivd5V`K%GxhBsp@SUU60-rfc$=wb>zdJzXS&-5(NRRodFk;Kxk!S( O(a0e7oY=E( zAyS;Ow?6Q&XA+cnkCb{28_1N8H#?J!*$MmIwLq^*T_9-z^&UE@A(z9oGYtFy6EZef LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/public/vercel.svg b/public/vercel.svg new file mode 100644 index 0000000..fbf0e25 --- /dev/null +++ b/public/vercel.svg @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/styles/globals.css b/styles/globals.css new file mode 100644 index 0000000..05bcca2 --- /dev/null +++ b/styles/globals.css @@ -0,0 +1,119 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + h1 { + @apply mb-6 text-3xl font-semibold; + } + h2 { + @apply text-2xl font-semibold; + } + p { + @apply mb-4; + } + a { + @apply text-blue-500 hover:text-blue-400 dark:text-blue-400 dark:hover:text-blue-300; + } +} + +/* Post styles */ +.prose { + max-width: 100vh; +} + +.prose pre { + @apply bg-gray-50 border border-gray-200 dark:border-gray-700 dark:bg-gray-900; +} + +.prose code { + @apply text-gray-800 dark:text-gray-200 px-1 py-0.5 border border-gray-100 dark:border-gray-800 rounded-md bg-gray-100 dark:bg-gray-900; +} + +.prose img { + /* Don't apply styles to next/image */ + @apply m-0; +} + +/* Prism Styles */ +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + @apply text-gray-700 dark:text-gray-300; +} + +.token.punctuation { + @apply text-gray-700 dark:text-gray-300; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + @apply text-green-500; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + @apply text-purple-500; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + @apply text-yellow-500; +} + +.token.atrule, +.token.attr-value, +.token.keyword { + @apply text-blue-500; +} + +.token.function, +.token.class-name { + @apply text-pink-500; +} + +.token.regex, +.token.important, +.token.variable { + @apply text-yellow-500; +} + +code[class*='language-'], +pre[class*='language-'] { + @apply text-gray-800 dark:text-gray-50; +} + +pre::-webkit-scrollbar { + display: none; +} + +pre { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} + +/* Remark Styles */ +.remark-code-title { + @apply text-gray-800 dark:text-gray-200 px-5 py-3 border border-b-0 border-gray-200 dark:border-gray-700 rounded-t bg-gray-200 dark:bg-gray-800 text-sm font-mono font-bold; +} + +.remark-code-title + pre { + @apply mt-0 rounded-t-none; +} + +.mdx-marker { + @apply block -mx-4 px-4 bg-gray-100 dark:bg-gray-800 border-l-4 border-blue-500; +} diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..dfd4e1d --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,75 @@ +const { spacing } = require('tailwindcss/defaultTheme'); + +module.exports = { + mode: 'jit', + purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], + darkMode: 'class', // or 'media' or 'class' + theme: { + extend: { + typography: (theme) => ({ + DEFAULT: { + css: { + color: theme('colors.gray.700'), + a: { + color: theme('colors.blue.500'), + '&:hover': { + color: theme('colors.blue.700'), + }, + code: { color: theme('colors.blue.400') }, + }, + 'h2,h3,h4': { + 'scroll-margin-top': spacing[32], + }, + code: { color: theme('colors.pink.500') }, + 'blockquote p:first-of-type::before': false, + 'blockquote p:last-of-type::after': false, + }, + }, + dark: { + css: { + color: theme('colors.gray.300'), + a: { + color: theme('colors.blue.400'), + '&:hover': { + color: theme('colors.blue.600'), + }, + code: { color: theme('colors.blue.400') }, + }, + blockquote: { + borderLeftColor: theme('colors.gray.800'), + color: theme('colors.gray.300'), + }, + 'h2,h3,h4': { + color: theme('colors.gray.100'), + 'scroll-margin-top': spacing[32], + }, + hr: { borderColor: theme('colors.gray.800') }, + ol: { + li: { + '&:before': { color: theme('colors.gray.500') }, + }, + }, + ul: { + li: { + '&:before': { backgroundColor: theme('colors.gray.500') }, + }, + }, + strong: { color: theme('colors.gray.300') }, + thead: { + color: theme('colors.gray.100'), + }, + tbody: { + tr: { + borderBottomColor: theme('colors.gray.700'), + }, + }, + }, + }, + }), + }, + }, + variants: { + typography: ['dark'], + }, + plugins: [require('@tailwindcss/typography')], +}; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..99710e8 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/types/layout.ts b/types/layout.ts new file mode 100644 index 0000000..e23391a --- /dev/null +++ b/types/layout.ts @@ -0,0 +1,9 @@ +import { PostType } from './post'; + +export interface MetaProps + extends Pick { + /** + * For the meta tag `og:type` + */ + type?: string; +} diff --git a/types/post.ts b/types/post.ts new file mode 100644 index 0000000..db1eba6 --- /dev/null +++ b/types/post.ts @@ -0,0 +1,7 @@ +export type PostType = { + date?: string; + description?: string; + image?: string; + slug: string; + title: string; +}; diff --git a/utils/mdxUtils.ts b/utils/mdxUtils.ts new file mode 100644 index 0000000..28ca311 --- /dev/null +++ b/utils/mdxUtils.ts @@ -0,0 +1,11 @@ +import fs from 'fs'; +import path from 'path'; + +// POSTS_PATH is useful when you want to get the path to a specific file +export const POSTS_PATH = path.join(process.cwd(), 'posts'); + +// postFilePaths is the list of all mdx files inside the POSTS_PATH directory +export const postFilePaths = fs + .readdirSync(POSTS_PATH) + // Only include md(x) files + .filter((path) => /\.mdx?$/.test(path));