Блог

Додавање тамне теме

Категорије:
  • ажурирања сајта

Најновија ставка коју сам додао мом сајту док радим на подршци за пријављивање преко друштвених мрежа је подршка за тамну тему прилагођену смерницама материјалног дизајна. То се у Vuetify-у изводи релативно једноставно; најтеже је одабрати саме боје тамне палете.

Када се дефинишу боје, потребно је проследити тему Vuetify-у. Ја сам се одлучио да тему поставим у два засебна фајла, који се затим учитавају преко директиве import.

export default {
primary: {
base: '#426344',
lighten1: '#6e916f',
darken1: '#19391d',
darken2: '#003300',
},
secondary: {
base: '#7f9a7f',
lighten1: '#afcbae',
darken1: '#526c53',
},
accent: '#d3dc7b',
error: '#d45858',
info: '#4592cf',
success: '#69b069',
warning: '#c78634'
};

import light from './theme/dunedain-light';
import dark from './theme/dunedain-dark';
// ...
export default {
// ...
vuetify: {
// ...
theme: {
// ...
themes: {
light, dark
}
}
},
};

Од Vim-а до IDE-а

Категорије:
  • програмирање

Једно од питања које ми други програмери поставе кад чују да се бавим програмирањем је које интегрисано окружење користим за развој апликација. По правилу се изненаде кад уместо очекиваних одговора као што су VS Code, JetBrains Web Storm или NetBeans, чују да користим Vim. Овај програм је током година доста напредовао у односу на своју инспирацију, програм vi, тако да данас сасвим комотно може да парира горенаведеним окружењима, или их чак и престигне.

Чуђење вероватно потиче од нетривијалног начина задавања чак и најосновнијих операција: рецимо, vi се, за разлику од велике већине других уређивача, подразумевано налази у „командном режиму“, па га није могуће само покренути и одмах почети са уношењем текста, већ се мора претходно пребацити у режим уноса пречицом i. Међутим, кад се једном „уђе у штос“, могуће су прилично напредне операције, које се све активирају са неколико тастера, рецимо ddp у командном режиму мења редослед два реда текста, а cw брише знаке до краја речи и пребацује се у insert mode, па тако можете заменити реч. Наредба за замену текста у читавом пасусу је само незнатно компликованија: vipc. Програмерима је изузетно корисно пребацивање између отворене и затворене заграде у пару тастером % или селекција свега између витичастих заграда комбинацијом vi}.

Ја константно допуњавам моја подешавања Vim-а, па ћу укратко написати и о томе.

Инсталација

Пре свега, програм који користим за развој апликација се тачније зове Neovim, и још прецизније, користим његову варијанту neovim-qt. Ради се о програму који је написан „од нуле“, уз задржану компатибилност са Vim-ом (а и он сам, VimVi IMproved, је побољшана верзија програма vi). Њега можете инсталирати пратећи упутства са neovim.io. Ипак, јасноће ради, овде ћу наставити да га зовем Vim.

Додаци

Иако је Vim сам по себи веома моћан, прави разлог тога што се може поредити са интегрисаним окружењима су додаци. Само неки од додатака које користим су:

  • Vim-CtrlSpace, додатак за управљање радним окружењем и чување сесија.

  • ALE, додатак за проверу и исправљање синтаксе.

  • Coc, IntelliSense додатак за Vim.

  • vim-airline, додатак за улепшано приказивање напредних информација у вези са фајлом који се уређује.

  • vimcolors/phosphor.vim, тема за Vim која подсећа на старе текстуалне терминале или боје кода у филму „Matrix“.

Фајл са подешавањима Vim-а који користим одржавам у репозиторијуму Strahinja/dotfiles.

Фонт

Можда није толико очито, али и избор фонта је битан. Мени се од постојећих опција највише свиђа фонт PxPlus IBM VGA са сајта int10h.org, коме сам додао симболе уз помоћ Nerd Fonts patcher-а. Резултат је следећи:

Снимак екрана програма Vim

Природно друштво

Категорије:
  • ажурирања сајта

Пажљивији пратиоци ове странице су сигурно приметили додатно дугме у app-bar-у, које није радило ништа значајно. Данас сам започео додавање подршке за пријављивање преко друштвених мрежа мом сајту. Ако сада притиснете дугме „ Пријава“, на дну екрана ће се отворити панел сличан овоме:

Панел за пријављивање преко друштвених мрежа

У наредном периоду ћу радити на подршци за логовање. ETA unknown.

Гистови у Markdown-у

Категорије:
  • ажурирања сајта

Ових дана после мање паузе радим на неколико делова мог сајта, па једва стижем да нешто и напишем о ономе шта додам. Недавно сам тако додао подршку за учитавање гистова у чланке на овом блогу.

За оне који не знају шта су то гистови (gists), ради се о фајловима, или деловима фајлова, са изворним кодом које је могуће делити и чак fork-овати као било који други фајл на GitHub-у. Просто поставите мини-репо са једним или више фајлова и поделите његов УРЛ. Сјајно! 😃

Ова могућност, која је уједно и тест динамичких компонената о којима сам недавно писао, ми је била посебан изазов и уједно извор материјала за учење Nuxt.js-а. Видећемо које су неке од карактеристичних тачака које сам прешао приликом њеног развоја.

Почетак

Рад са гистовима почињемо посетом УРЛhttps://gist.github.com, где ће они које означимо као јавне бити доступни на адреси облика

https://gist.github.com/[корисничко_име]/[хеш]

Верзији гиста у формату JSON можемо приступити додавањем .json на крај те адресе, дакле:

https://gist.github.com/[корисничко_име]/[хеш].json

Простим позивом this.$axios.$get() или this.$http.$get(), у зависности од тога да ли користимо модул axios или http, учитамо тај JSON и посао је готов, зар не?

Нажалост, то није баш тако. Уколико вам не смета подразумевано HTML форматирање поља div насталог JSON објекта, или чињеница да се сви фајлови заједно налазе у том (скаларном) пољу, и то је довољно. За било шта више, потребно је учитати гист преко API-ја.

API

За просто учитавање довољно је методом GET приступити УРЛ

http://api.github.com/gists/[хеш]

где је [хеш] хеш код вашег гиста, исти као у претходном случају. Резултат је опет JSON објекат, али који укључује фајлове у сировом облику као засебна поља.

Код

Конкретан код сам раздвојио на онај који је оријентисан на податке (Vuex store) и презентациони (компонента Gist). Ево најзанимљивијих делова:

<template>
<!-- ... -->
</template>
<script>
export default {
name: 'Gist',
props: {
gist: { type: Object, default: () => ({}), required: true },
filename: { type: String, default: '', required: true },
highlightedLine: { type: Number, default: 0, required: false },
},
computed: {
gistRawUrl()
{
if (!this || !this.gist || !this.gist.files || this.filename.length==0
|| !this.gist.files[this.filename])
{
return null;
}
return this.gist.files[this.filename].raw_url;
},
gistLines()
{
if (!this || !this.gist || !this.gist.files || this.filename.length==0
|| !this.gist.files[this.filename])
{
return null;
}
return this.gist.files[this.filename].content;
},
},
methods: {
escapeHtml(html)
{
return html
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
},
formatGistLinesAsHtml(lines)
{
let result = '';
if (lines && lines.length>0)
{
let linesArray = lines.split('\n');
for (let line in linesArray)
{
result += (this.highlightedLine==parseInt(line)+1 ?
'<tr class="highlighted-line">\n' :
'<tr>\n') +
'<td class="blob-num js-line-number" data-line-number="' +
(parseInt(line)+1).toString() + '"></td>\n' +
'<td class="blob-code blob-code-inner js-file-line">' +
this.escapeHtml(linesArray[line]) +
'</td>\n' +
'</tr>\n';
}
}
return result;
}
},
};
</script>
<style lang="sass">
@import '~/assets/sass/gist.sass'
</style>

Gist.vue је компонента за приказивање гиста која је намењена директном укључивању у Markdown фајл. Пошто се фајл преко API-ја учитава као чист текст, даље се са његовим садржајем може радити било шта. На пример, можемо означити појединачне линије или истаћи жељени keyword. Ова компонента се у Markdown фајл укључује на следећи начин:

<component :is="extraComponentLoader"
:filename="'nuxt.config.js'"
:highlighted-line="15"
:gist="gist"></component>

Динамичке руте у статичкој апликацији

Категорије:
  • Програмирање

Наставивши развој сајта, приметио сам једну специфичност Nuxt.js-а. Уколико статички генеришете апликацију преко nuxt generate, а не наведете експлицитно које руте ће бити генерисане, руте са параметрима ће и даље функционисати, али само ако су активиране кроз саму апликацију, преко Vue Router-а (рецимо, <nuxt-link>). Ако унесете УРЛ странице у адресном пољу, или притиснете F5, добићете поруку о грешци, као да рута не постоји!

Проблем

На пример, нека у апликацији имамо следеће странице:

/pages
 ├──/search
 │   └──_docid.vue
 └──index.vue

Нека одговарајући фајлови садрже следећи код:

<!--pages/index.vue-->
<template>
<section>
<nuxt-link to="/search/first">Search: "first"</nuxt-link>
<nuxt-link to="/search/second">Search: "second"</nuxt-link>
</section>
</template>
<script>
export default {
name: "Index"
};
</script>
<!-- ... -->

<!--pages/search/_docid.vue-->
<template>
<div class="search">
<nuxt-link to="/">Back to home</nuxt-link>
<p>{{docBodies[documentId]}}</p>
</div>
<!--doc-->
</template>
<script>
export default {
name: "Search",
asyncData({ params }) {
return {
documentId: params.docid,
docBodies: {
first: "First document",
second: "Second document"
}
};
}
};
</script>
<!-- ... -->

Покретањем nuxt generate без посебно подешене generate секције фајла nuxt.config.js биће генерисана само основна рута, /. Ако приступимо почетној страници, па затим активирамо линк Search: "first", учитаће се страница за претрагу, /search/first. Међутим, притиском на F5 или Ctrl+R страница неће бити исправно учитана. Исто се дешава приступом преко линка /search/first.

Са овим понашањем Nuxt.js-а сам се суочио када сам желео да у мој блог додам подршку за листање свих страница са одређеном ознаком. Рецимо, у мом блогу се за то користи рута /blog/tag/:tagname, и овај чланак ће се појавити у листи чланака на рути /blog/tag/SSR.

Ако негде у апликацији, рецимо у приказу појединачних чланака, позовемо

<nuxt-link :to="`/blog/tag/${tag.name}`">
    {{tag.name}}
</nuxt-link>

све ће функционисати, али не и у поменута два случаја: ако директно посетимо УРЛ или освежимо већ учитану страницу.

Решење

Решење овог проблема је генерисање странице која ће бити „резервна опција“ у случају када задата рута није статички генерисана. Ово се постиже додавањем опције fallback у секцију generate у датотеци nuxt.config.js:

export default {
/*
** Rendering mode
** Doc: https://nuxtjs.org/api/configuration-mode
*/
mode: "universal",
// ...
generate: {
// ***************************************************
// *** Without the next line, F5 on dynamic routes ***
// *** will fail ***
// ***************************************************
fallback: true
},
// ...
};

Ова опција има три могуће вредности:

  • true: fallback руту ће опслуживати фајл /404.html,
  • false: fallback руту ће опслуживати фајл /200.html,
  • '[назив_фајла].html': експлицитно задавање фајла fallback руте.

Ова лако занемарљива опција може представљати разлику између функционалног и нефункционалног сајта.