From a9b015fd7971b722deaade7973ef263a8a284b9a Mon Sep 17 00:00:00 2001 From: "Kellen.Xavier" Date: Thu, 27 Aug 2026 15:20:22 -0300 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20lint=20de=20markdown=20(mdl)=20e=20?= =?UTF-8?q?changelog/release=20autom=C3=A1ticos=20(git-cliff)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adiciona lint de markdown com mdl (workflow em todo PR e push para develop/main) e corrige as violações reais já existentes: listas de tags no front matter viraram estilo flow (evita falso positivo de MD032 tratando a lista YAML como lista markdown), indentação de lista aninhada (MD007) e numeração sequencial de lista ordenada permitida via config (MD029). MD002/MD013/MD033/MD041 desligadas por não fazerem sentido neste conteúdo (H1 vem do front matter, prosa livre, HTML cru intencional). Adiciona geração automática de CHANGELOG.md (formato Keep a Changelog) via git-cliff a partir de Conventional Commits: ao mergear develop em main, o workflow calcula a próxima versão semver, atualiza o CHANGELOG.md, cria a tag vX.Y.Z e publica a GitHub Release com as notas — sem intervenção manual, e sem publicar nada se não houver commit relevante desde a última tag. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01DArf4UAVNWE4Ln1kgrsege --- .devcontainer/devcontainer.json | 6 +- .github/workflows/lint.yaml | 22 ++++++ .github/workflows/release.yaml | 77 +++++++++++++++++++ .gitpod.yml | 4 + .mdl_style.rb | 16 ++++ .mdlrc | 1 + Gemfile | 3 + Gemfile.lock | 48 ++++++++++++ README.md | 26 +++++++ cliff.toml | 70 +++++++++++++++++ .../2024/outubro/cypress-boas-praticas.md | 7 +- content/docs/2025/dezembro/linux-basico.md | 5 +- .../configurando-o-sonar-qube-localmente.md | 9 +-- 13 files changed, 277 insertions(+), 17 deletions(-) create mode 100644 .github/workflows/lint.yaml create mode 100644 .github/workflows/release.yaml create mode 100644 .mdl_style.rb create mode 100644 .mdlrc create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 cliff.toml diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f83118d..ec16b64 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,8 +5,12 @@ "extended": true, "version": "0.147.9" }, - "ghcr.io/devcontainers/features/node:1": {} + "ghcr.io/devcontainers/features/node:1": {}, + "ghcr.io/devcontainers/features/ruby:1": { + "version": "3.3" + } }, + "postCreateCommand": "bundle install", "customizations": { "vscode": { "extensions": [ diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000..83987e2 --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,22 @@ +name: Lint Markdown + +on: + pull_request: + push: + branches: [develop, main] + +jobs: + mdl: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.3" + bundler-cache: true + + - name: Rodar mdl + run: bundle exec mdl content README.md diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..16bc7f8 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,77 @@ +name: Release + +# Roda sempre que develop é mergeada em main (ou qualquer outro push direto +# em main). Gera o CHANGELOG.md (Keep a Changelog) com o git-cliff, cria a +# tag semver automaticamente e publica a GitHub Release com as notas. + +on: + push: + branches: [main] + +permissions: + contents: write + +concurrency: + group: release + cancel-in-progress: false + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Instalar git-cliff + uses: taiki-e/install-action@git-cliff + + - name: Verificar se há algo para lançar + id: release + run: | + LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || true) + NEXT_VERSION=$(git-cliff --bumped-version 2>/dev/null) + + if [ -n "$LAST_TAG" ] && [ "$LAST_TAG" = "$NEXT_VERSION" ]; then + echo "Nada para lançar (nenhum commit relevante desde $LAST_TAG)." + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "Próxima versão: $NEXT_VERSION" + echo "skip=false" >> "$GITHUB_OUTPUT" + echo "version=$NEXT_VERSION" >> "$GITHUB_OUTPUT" + fi + + - name: Atualizar CHANGELOG.md + if: steps.release.outputs.skip == 'false' + run: git-cliff --tag "${{ steps.release.outputs.version }}" -o CHANGELOG.md + + - name: Commitar CHANGELOG.md + if: steps.release.outputs.skip == 'false' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add CHANGELOG.md + git commit -m "chore(release): ${{ steps.release.outputs.version }}" + git push origin HEAD:main + + - name: Criar tag + if: steps.release.outputs.skip == 'false' + run: | + git tag "${{ steps.release.outputs.version }}" + git push origin "${{ steps.release.outputs.version }}" + + - name: Gerar notas da release + if: steps.release.outputs.skip == 'false' + run: | + git-cliff --tag "${{ steps.release.outputs.version }}" --latest --strip header \ + -o /tmp/release-notes.md + + - name: Publicar GitHub Release + if: steps.release.outputs.skip == 'false' + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + gh release create "${{ steps.release.outputs.version }}" \ + --title "${{ steps.release.outputs.version }}" \ + --notes-file /tmp/release-notes.md diff --git a/.gitpod.yml b/.gitpod.yml index 8bc7732..9bf6bbc 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -10,6 +10,10 @@ tasks: init: echo "Your version of Hugo is `hugo version`" && hugo mod tidy command: hugo server -D -F --baseURL $(gp url 1313) --liveReloadPort=443 --appendPort=false --bind=0.0.0.0 --disableFastRender + - name: Lint (mdl) + init: bundle install + command: bundle exec mdl content README.md + ports: - port: 1313 onOpen: open-preview diff --git a/.mdl_style.rb b/.mdl_style.rb new file mode 100644 index 0000000..a03394a --- /dev/null +++ b/.mdl_style.rb @@ -0,0 +1,16 @@ +all + +# O

de cada post vem do front matter (`title`), renderizado pelo tema +# Hextra — o corpo do markdown propositalmente começa em "##". +exclude_rule "MD002" # first header should be a top level header +exclude_rule "MD041" # first line in a file should be a top level header + +# Prosa, não código — não faz sentido impor um limite fixo de coluna. +exclude_rule "MD013" # line length + +# `goldmark.unsafe: true` no hugo.yaml permite HTML puro nos posts de propósito. +exclude_rule "MD033" # inline HTML + +# Prefere numeração sequencial (1. 2. 3.) em vez do padrão do mdl (1. 1. 1.). +rule "MD029", :style => :ordered + diff --git a/.mdlrc b/.mdlrc new file mode 100644 index 0000000..74a339a --- /dev/null +++ b/.mdlrc @@ -0,0 +1 @@ +style "#{File.dirname(__FILE__)}/.mdl_style.rb" diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..2ff00db --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem "mdl", "~> 0.18" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..b8006aa --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,48 @@ +GEM + remote: https://rubygems.org/ + specs: + chef-utils (19.3.15) + concurrent-ruby + concurrent-ruby (1.3.8) + kramdown (2.5.2) + rexml (>= 3.4.4) + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + mdl (0.18.1) + kramdown (~> 2.5) + kramdown-parser-gfm (~> 1.1) + mixlib-cli + mixlib-config + mixlib-shellout + uri + mixlib-cli (2.1.8) + mixlib-config (3.0.27) + tomlrb + mixlib-shellout (3.4.10) + chef-utils + rexml (3.4.4) + tomlrb (2.0.4) + uri (1.1.1) + +PLATFORMS + ruby + x86_64-linux + +DEPENDENCIES + mdl (~> 0.18) + +CHECKSUMS + chef-utils (19.3.15) sha256=1c537a9c85b55b6d897de8e8af6b66b017a6dfae2b5e7b25e0bf320637e0b86a + concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1 + kramdown (2.5.2) sha256=1ba542204c66b6f9111ff00dcc26075b95b220b07f2905d8261740c82f7f02fa + kramdown-parser-gfm (1.1.0) sha256=fb39745516427d2988543bf01fc4cf0ab1149476382393e0e9c48592f6581729 + mdl (0.18.1) sha256=14c7ab8264bbeac4010bd54adb5d125cf1d29b3848c3b6bfacb239b8bc70037a + mixlib-cli (2.1.8) sha256=e6f27be34d580f6ed71731ca46b967e57793a627131c1f6e1ed2dad39ea3bdf9 + mixlib-config (3.0.27) sha256=d7748b1898e4f16502afec1de00b5ad65c6de405114b1b0c65ec61b1a9100148 + mixlib-shellout (3.4.10) sha256=7c8a6c5a6c90922a8871ed682129c19c9b1a514eedea2e867077229e6a415f0c + rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142 + tomlrb (2.0.4) sha256=262f77947ac3ac9b3366a0a5940ecd238300c553e2e14f22009e2afcd2181b99 + uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6 + +BUNDLED WITH + 4.0.16 diff --git a/README.md b/README.md index 86cbadb..690bb4b 100644 --- a/README.md +++ b/README.md @@ -22,3 +22,29 @@ hugo mod tidy hugo server -D -F ``` + +## Lint de Markdown + +Os posts são checados com o [`mdl`](https://github.com/markdownlint/markdownlint) (regras em +`.mdl_style.rb`). Roda automaticamente em todo PR e push para `develop`/`main` +(`.github/workflows/lint.yaml`); para rodar localmente: + +```bash +bundle install +bundle exec mdl content README.md +``` + +## Changelog e releases + +O `CHANGELOG.md` (formato [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)) é gerado +automaticamente pelo [`git-cliff`](https://git-cliff.org/) a partir dos commits em +[Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, ...). Não é para +editar esse arquivo manualmente. + +Quando `develop` é mergeada em `main`, o workflow `.github/workflows/release.yaml`: + +1. calcula a próxima versão a partir dos commits desde a última tag; +2. atualiza o `CHANGELOG.md` e commita em `main`; +3. cria a tag `vX.Y.Z` e publica a GitHub Release com as notas dessa versão. + +Se não houver commit relevante (`feat`/`fix`/...) desde a última tag, nada é publicado. diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 0000000..19013c3 --- /dev/null +++ b/cliff.toml @@ -0,0 +1,70 @@ +[changelog] +header = """ +# Changelog\n +Todas as mudanças notáveis deste projeto serão documentadas neste arquivo. + +O formato é baseado em [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR/).\n +""" +body = """ +{% if version -%} + ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} +{% else -%} + ## [Não lançado] +{% endif -%} +{% for group, commits in commits | group_by(attribute="group") %} +### {{ group | upper_first }} +{% for commit in commits %} +- {% if commit.scope %}**{{ commit.scope }}:** {% endif %}{{ commit.message | upper_first | trim }} ([{{ commit.id | truncate(length=7, end="") }}](https://github.com/kellen-xavier/ladydebug.github.io/commit/{{ commit.id }})) +{%- endfor %} +{% endfor -%} +""" +footer = """ +{% for release in releases -%} + {% if release.version -%} + {% if release.previous and release.previous.version -%} +[{{ release.version | trim_start_matches(pat="v") }}]: https://github.com/kellen-xavier/ladydebug.github.io/compare/{{ release.previous.version }}...{{ release.version }} + {% endif -%} + {% else -%} + {% if release.previous and release.previous.version -%} +[Não lançado]: https://github.com/kellen-xavier/ladydebug.github.io/compare/{{ release.previous.version }}...HEAD + {% endif -%} + {% endif -%} +{% endfor -%} +""" +trim = true + +[git] +conventional_commits = true +filter_unconventional = false +commit_preprocessors = [ + { pattern = '\(#([0-9]+)\)', replace = "" }, +] +commit_parsers = [ + { message = "^[Mm]erge pull request", skip = true }, + { message = "^[Mm]erge branch", skip = true }, + { message = "^feat", group = "Added" }, + { message = "^fix", group = "Fixed" }, + { message = "^perf", group = "Changed" }, + { message = "^refactor", group = "Changed" }, + { message = "^revert", group = "Changed" }, + { message = "^docs", group = "Changed" }, + { message = "^style", skip = true }, + { message = "^test", skip = true }, + { message = "^chore\\(release\\)", skip = true }, + { message = "^chore", skip = true }, + { message = "^ci", skip = true }, + { message = "^build", skip = true }, + { body = ".*[Ss]ecurity", group = "Security" }, + { message = ".*", group = "Changed" }, +] +filter_commits = false +tag_pattern = "v[0-9]*" +topo_order = false +sort_commits = "oldest" + +[bump] +features_always_bump_minor = true +breaking_always_bump_major = true +initial_tag = "v0.1.0" +no_increment_regex = "chore|ci|docs|style|test|build" diff --git a/content/docs/2024/outubro/cypress-boas-praticas.md b/content/docs/2024/outubro/cypress-boas-praticas.md index 59ce78f..0120e19 100644 --- a/content/docs/2024/outubro/cypress-boas-praticas.md +++ b/content/docs/2024/outubro/cypress-boas-praticas.md @@ -2,12 +2,7 @@ title: Cypress e Boas Práticas de Automação date: '2024-10-24T13:07:00-03:00' slug: cypress-e-boas-praticas-de-automacao -tags: -- cypress -- automacao -- boas -- praticas -- docs +tags: [cypress, automacao, boas, praticas, docs] draft: false featured: true --- diff --git a/content/docs/2025/dezembro/linux-basico.md b/content/docs/2025/dezembro/linux-basico.md index 389334d..8213e1b 100644 --- a/content/docs/2025/dezembro/linux-basico.md +++ b/content/docs/2025/dezembro/linux-basico.md @@ -2,10 +2,7 @@ title: "Linux Básico" date: 2025-12-01 weight: 1 -tags: - - linux - - terminal - - estudos +tags: [linux, terminal, estudos] --- ## Minhas anotações sobre o básicos do Linux diff --git a/content/docs/2025/setembro/20/configurando-o-sonar-qube-localmente.md b/content/docs/2025/setembro/20/configurando-o-sonar-qube-localmente.md index 53520b6..75484cd 100644 --- a/content/docs/2025/setembro/20/configurando-o-sonar-qube-localmente.md +++ b/content/docs/2025/setembro/20/configurando-o-sonar-qube-localmente.md @@ -2,10 +2,7 @@ title: "Configurando o SonarQube Localmente" date: 2025-09-20 weight: 1 -tags: - - sonarqube - - qualidade-de-codigo - - docker +tags: [sonarqube, qualidade-de-codigo, docker] featured: true --- @@ -73,8 +70,8 @@ Ajuste as configurações de senha e segue para a página: - Acesse [http://localhost:9000](http://localhost:9000/) - Login padrão: - - Usuário: admin - - Senha: admin + - Usuário: admin + - Senha: admin Agora vamos configurar para fazer um escaneamento do projeto From ea165a0d886d05e8daa9e9d68533d3ad7ff594cd Mon Sep 17 00:00:00 2001 From: "Kellen.Xavier" Date: Thu, 27 Aug 2026 16:47:24 -0300 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20doca=20fixa=20no=20rodap=C3=A9=20co?= =?UTF-8?q?m=20janelas=20flutuantes=20(m=C3=BAsica,=20calend=C3=A1rio,=20r?= =?UTF-8?q?ascunhos)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adiciona uma barra fixa no rodapé no estilo desktop/OS do fim dos anos 2000, com janelas flutuantes e arrastáveis ( nativo, sem lib de drag) para três painéis: player Spotify (embed carregado só quando a janela abre), lista de próximos eventos e lista de rascunhos. Isolado do resto do tema via os hooks públicos do Hextra (layouts/partials/custom/{footer,head-end}.html), com CSS e JS em arquivos próprios (desktop-dock.css/.js) e configuração via data (data/dock.yaml, data/events.yaml) — trocar o link do Spotify ou adicionar um evento não mexe em template. Rascunhos viram conteúdo Hugo de verdade em content/drafts/, com build.list: local (visível só pela própria seção) pra ficar fora do blog principal, do RSS e do sitemap sem deixar de ter página própria. Ícones: Pixelarticons (MIT). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01DArf4UAVNWE4Ln1kgrsege --- README.md | 18 ++ assets/css/desktop-dock.css | 215 ++++++++++++++++++ assets/image/icons/pixelarticons-archive.svg | 3 + assets/image/icons/pixelarticons-calendar.svg | 3 + assets/image/icons/pixelarticons-music.svg | 3 + assets/js/desktop-dock.js | 111 +++++++++ content/drafts/_index.md | 15 ++ content/drafts/exemplo-rascunho.md | 12 + data/dock.yaml | 21 ++ data/events.yaml | 11 + layouts/partials/custom/footer.html | 82 +++++++ layouts/partials/custom/head-end.html | 4 + 12 files changed, 498 insertions(+) create mode 100644 assets/css/desktop-dock.css create mode 100644 assets/image/icons/pixelarticons-archive.svg create mode 100644 assets/image/icons/pixelarticons-calendar.svg create mode 100644 assets/image/icons/pixelarticons-music.svg create mode 100644 assets/js/desktop-dock.js create mode 100644 content/drafts/_index.md create mode 100644 content/drafts/exemplo-rascunho.md create mode 100644 data/dock.yaml create mode 100644 data/events.yaml create mode 100644 layouts/partials/custom/footer.html create mode 100644 layouts/partials/custom/head-end.html diff --git a/README.md b/README.md index 690bb4b..8027124 100644 --- a/README.md +++ b/README.md @@ -48,3 +48,21 @@ Quando `develop` é mergeada em `main`, o workflow `.github/workflows/release.ya 3. cria a tag `vX.Y.Z` e publica a GitHub Release com as notas dessa versão. Se não houver commit relevante (`feat`/`fix`/...) desde a última tag, nada é publicado. + +## Doca (dock) do rodapé + +Barra fixa no rodapé com janelas flutuantes (``, arrastáveis, sem +libs), no estilo desktop/OS do fim dos anos 2000. Fica isolada do resto do +tema em arquivos próprios: + +- `data/dock.yaml` — quais botões existem, rótulo, ícone e link do Spotify. +- `data/events.yaml` — lista de próximos eventos do painel "Calendário". +- `content/drafts/` — rascunhos (`hugo new drafts/nome.md`); cada um vira uma + página própria, mas fica fora do blog, do RSS e do sitemap. +- `assets/css/desktop-dock.css` e `assets/js/desktop-dock.js` — estilo e + comportamento, separados do `custom.css`/tema. + +Pra trocar o link do Spotify ou adicionar um evento, é só editar os `.yaml` +acima — não precisa mexer nos templates. + +Ícones: [Pixelarticons](https://github.com/halfmage/pixelarticons) (MIT). diff --git a/assets/css/desktop-dock.css b/assets/css/desktop-dock.css new file mode 100644 index 0000000..972d53a --- /dev/null +++ b/assets/css/desktop-dock.css @@ -0,0 +1,215 @@ +/* Doca fixa no rodapé (inspirada na estética de desktop/OS do fim dos anos + 2000) + janelas flutuantes. Reaproveita as variáveis de cor de custom.css + (--ld-bg, --ld-bg-soft, --ld-fg, --ld-muted, --ld-border, --ld-accent) pra + ficar consistente com o resto do site nos dois temas. */ + +:root { + --dock-height: 4rem; +} + +body { + padding-bottom: var(--dock-height); +} + +.dock { + position: fixed; + left: 0; + right: 0; + bottom: 0; + z-index: 40; + display: flex; + justify-content: center; + gap: 0.5rem; + height: var(--dock-height); + padding: 0.5rem 1rem calc(0.5rem + env(safe-area-inset-bottom)); + background: var(--ld-bg-soft); + border-top: 1px solid var(--ld-border); + box-shadow: 0 -6px 16px rgba(0, 0, 0, 0.06); + overflow-x: auto; +} + +.dock__item { + flex: 0 0 auto; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.15rem; + width: 4.25rem; + padding: 0.35rem 0.25rem; + border: 1px solid var(--ld-border); + border-radius: 0.5rem; + background: var(--ld-bg); + color: var(--ld-muted); + cursor: pointer; + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.6), + 0 1px 2px rgba(0, 0, 0, 0.06); + transition: + transform 0.1s ease, + color 0.15s ease, + border-color 0.15s ease; +} + +.dock__item svg { + width: 1.25rem; + height: 1.25rem; +} + +.dock__item:hover { + color: var(--ld-accent); + border-color: var(--ld-accent); +} + +.dock__item:active, +.dock__item[aria-pressed="true"] { + transform: translateY(1px); + color: var(--ld-accent); + border-color: var(--ld-accent); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.15); +} + +.dock__label { + font-size: 0.65rem; + line-height: 1; + white-space: nowrap; +} + +/* Janelas flutuantes () */ +.dock-window { + position: fixed; + width: min(22rem, calc(100vw - 2rem)); + max-height: min(28rem, calc(100vh - var(--dock-height) - 3rem)); + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + border: 1px solid var(--ld-border); + border-radius: 0.5rem; + background: var(--ld-bg); + color: var(--ld-fg); + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18); +} + +.dock-window::backdrop { + background: transparent; +} + +.dock-window__bar { + flex: 0 0 auto; + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; + padding: 0.5rem 0.75rem; + background: var(--ld-bg-soft); + border-bottom: 1px solid var(--ld-border); + border-radius: 0.5rem 0.5rem 0 0; + font-size: 0.8rem; + font-weight: 700; + cursor: grab; + user-select: none; +} + +.dock-window__bar:active { + cursor: grabbing; +} + +.dock-window__close { + flex: 0 0 auto; + width: 1.5rem; + height: 1.5rem; + display: inline-flex; + align-items: center; + justify-content: center; + border: 1px solid var(--ld-border); + border-radius: 0.25rem; + background: var(--ld-bg); + color: var(--ld-muted); + font-size: 0.9rem; + line-height: 1; + cursor: pointer; +} + +.dock-window__close:hover { + color: var(--ld-accent); + border-color: var(--ld-accent); +} + +.dock-window__body { + flex: 1 1 auto; + overflow-y: auto; + padding: 0.75rem 1rem 1rem; +} + +.dock-window__body:empty::before { + content: ""; +} + +.dock-window__empty { + color: var(--ld-muted); + font-size: 0.875rem; +} + +/* Player Spotify (carregado sob demanda) */ +.dock-window__embed iframe { + width: 100%; + height: 152px; + border: 0; + border-radius: 0.5rem; +} + +.dock-window__spotify-link { + display: inline-block; + margin-top: 0.5rem; + font-size: 0.8rem; + color: var(--ld-accent); +} + +/* Lista de eventos / rascunhos */ +.dock-window__list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.dock-window__list li { + border-bottom: 1px solid var(--ld-border); + padding-bottom: 0.6rem; +} + +.dock-window__list li:last-child { + border-bottom: none; + padding-bottom: 0; +} + +.dock-window__item-title { + display: block; + font-weight: 600; + color: var(--ld-fg); + text-decoration: none; +} + +a.dock-window__item-title:hover { + color: var(--ld-accent); +} + +.dock-window__item-meta { + display: block; + margin-top: 0.15rem; + font-size: 0.75rem; + color: var(--ld-muted); +} + +@media (max-width: 30rem) { + .dock__label { + display: none; + } + + .dock__item { + width: 2.75rem; + } +} diff --git a/assets/image/icons/pixelarticons-archive.svg b/assets/image/icons/pixelarticons-archive.svg new file mode 100644 index 0000000..7b28391 --- /dev/null +++ b/assets/image/icons/pixelarticons-archive.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/image/icons/pixelarticons-calendar.svg b/assets/image/icons/pixelarticons-calendar.svg new file mode 100644 index 0000000..41ee5f1 --- /dev/null +++ b/assets/image/icons/pixelarticons-calendar.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/image/icons/pixelarticons-music.svg b/assets/image/icons/pixelarticons-music.svg new file mode 100644 index 0000000..6fb6510 --- /dev/null +++ b/assets/image/icons/pixelarticons-music.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/js/desktop-dock.js b/assets/js/desktop-dock.js new file mode 100644 index 0000000..c84ea94 --- /dev/null +++ b/assets/js/desktop-dock.js @@ -0,0 +1,111 @@ +(function () { + var dock = document.querySelector(".dock"); + if (!dock) return; + + var openOffset = 0; + + function placeWindow(win) { + var margin = 16; + var width = win.offsetWidth || 352; + var height = win.offsetHeight || 280; + var maxLeft = window.innerWidth - width - margin; + var maxTop = window.innerHeight - height - margin; + var left = Math.min(maxLeft, margin + openOffset); + var top = Math.min(maxTop, margin + openOffset); + win.style.left = Math.max(margin, left) + "px"; + win.style.top = Math.max(margin, top) + "px"; + openOffset = (openOffset + 24) % 96; + } + + function loadEmbed(win) { + var body = win.querySelector("[data-embed-src]"); + if (!body || body.dataset.loaded) return; + var iframe = document.createElement("iframe"); + iframe.src = body.dataset.embedSrc; + iframe.loading = "lazy"; + iframe.allow = "encrypted-media"; + body.appendChild(iframe); + body.dataset.loaded = "true"; + } + + function openWindow(win) { + if (win.open) return; + win.show(); + placeWindow(win); + loadEmbed(win); + } + + function closeWindow(win) { + if (win.open) win.close(); + } + + dock.addEventListener("click", function (event) { + var btn = event.target.closest("[data-dock-target]"); + if (!btn) return; + var win = document.getElementById(btn.getAttribute("data-dock-target")); + if (!win) return; + + if (win.open) { + closeWindow(win); + btn.setAttribute("aria-pressed", "false"); + } else { + openWindow(win); + btn.setAttribute("aria-pressed", "true"); + } + }); + + document.querySelectorAll(".dock-window").forEach(function (win) { + var btn = dock.querySelector( + '[data-dock-target="' + win.id + '"]' + ); + + win.addEventListener("close", function () { + if (btn) btn.setAttribute("aria-pressed", "false"); + }); + + var closeBtn = win.querySelector("[data-dock-close]"); + if (closeBtn) { + closeBtn.addEventListener("click", function () { + closeWindow(win); + }); + } + + var bar = win.querySelector(".dock-window__bar"); + if (!bar) return; + + var dragging = false; + var startX = 0; + var startY = 0; + var startLeft = 0; + var startTop = 0; + + bar.addEventListener("pointerdown", function (event) { + if (event.target.closest("[data-dock-close]")) return; + dragging = true; + startX = event.clientX; + startY = event.clientY; + var rect = win.getBoundingClientRect(); + startLeft = rect.left; + startTop = rect.top; + bar.setPointerCapture(event.pointerId); + }); + + bar.addEventListener("pointermove", function (event) { + if (!dragging) return; + var left = startLeft + (event.clientX - startX); + var top = startTop + (event.clientY - startY); + win.style.left = Math.max(0, left) + "px"; + win.style.top = Math.max(0, top) + "px"; + }); + + function stopDrag(event) { + dragging = false; + if (bar.hasPointerCapture(event.pointerId)) { + bar.releasePointerCapture(event.pointerId); + } + } + + bar.addEventListener("pointerup", stopDrag); + bar.addEventListener("pointercancel", stopDrag); + }); +})(); diff --git a/content/drafts/_index.md b/content/drafts/_index.md new file mode 100644 index 0000000..3df5593 --- /dev/null +++ b/content/drafts/_index.md @@ -0,0 +1,15 @@ +--- +title: Rascunhos +cascade: + type: draftnote + build: + list: local + render: always + sitemap: + disable: true +build: + list: never + render: never +sitemap: + disable: true +--- diff --git a/content/drafts/exemplo-rascunho.md b/content/drafts/exemplo-rascunho.md new file mode 100644 index 0000000..0906949 --- /dev/null +++ b/content/drafts/exemplo-rascunho.md @@ -0,0 +1,12 @@ +--- +title: "Exemplo de rascunho" +date: 2026-08-27 +--- + +Isso é um rascunho de exemplo — uma nota solta que ainda não virou post. +Aparece só no painel "Rascunhos" da doca, fora do blog principal, sem entrar +no RSS nem no sitemap. + +Pra escrever um rascunho de verdade: crie um `.md` novo dentro de +`content/drafts/` com um `title` e uma `date` — ele já aparece automaticamente +no painel. Pode apagar este arquivo de exemplo quando quiser. diff --git a/data/dock.yaml b/data/dock.yaml new file mode 100644 index 0000000..f01f6b7 --- /dev/null +++ b/data/dock.yaml @@ -0,0 +1,21 @@ +# Botões da doca fixa no rodapé do site (layouts/partials/custom/footer.html). +# icon: nome do arquivo em assets/image/icons/pixelarticons-.svg +# panel: qual painel abre — "spotify", "events" ou "drafts" (lógica de cada +# um está direto no partial, já que cada tipo renderiza algo diferente) + +- id: music + label: "Música" + icon: music + panel: spotify + # Troque pelo link de um perfil, playlist ou álbum seu no Spotify. + spotify_url: "https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M" + +- id: calendar + label: "Calendário" + icon: calendar + panel: events + +- id: drafts + label: "Rascunhos" + icon: archive + panel: drafts diff --git a/data/events.yaml b/data/events.yaml new file mode 100644 index 0000000..da777be --- /dev/null +++ b/data/events.yaml @@ -0,0 +1,11 @@ +# Lista de próximos eventos mostrada no painel "Calendário" da doca. +# date no formato "AAAA-MM-DD" (ordena e filtra certo por comparação de texto). +# Deixe a lista vazia ([]) se não houver nada no momento — o painel mostra um +# aviso nesse caso. +# +# Exemplo: +# - date: "2026-09-15" +# title: "Exemplo de evento" +# url: "https://example.com" + +[] diff --git a/layouts/partials/custom/footer.html b/layouts/partials/custom/footer.html new file mode 100644 index 0000000..e615ea9 --- /dev/null +++ b/layouts/partials/custom/footer.html @@ -0,0 +1,82 @@ +{{/* Doca fixa + janelas flutuantes. Botões vêm de data/dock.yaml; cada + "panel" (spotify | events | drafts) tem seu próprio corpo abaixo. */}} +{{- $today := now.Format "2006-01-02" -}} + + + +{{- range site.Data.dock }} + {{- $item := . }} + +
+ {{ .label }} + +
+ + {{- if eq .panel "spotify" }} + + + {{- else if eq .panel "events" }} + {{- $upcoming := where site.Data.events "date" ">=" $today }} +
+ {{- if $upcoming }} +
    + {{- range (sort $upcoming "date") }} +
  • + {{- if .url }} + {{ .title }} + {{- else }} + {{ .title }} + {{- end }} + {{ .date }} +
  • + {{- end }} +
+ {{- else }} +

Nenhum evento no momento.

+ {{- end }} +
+ + {{- else if eq .panel "drafts" }} + {{- $drafts := slice }} + {{- with site.GetPage "/drafts" }}{{ $drafts = .RegularPages.ByDate.Reverse }}{{ end }} +
+ {{- if $drafts }} +
    + {{- range $drafts }} +
  • + {{ .Title }} + {{ .Date.Format "2006-01-02" }} +
  • + {{- end }} +
+ {{- else }} +

Nenhum rascunho no momento.

+ {{- end }} +
+ {{- end }} +
+{{- end }} + +{{- with resources.Get "js/desktop-dock.js" }} + {{ $js := . | minify | fingerprint }} + +{{ end }} diff --git a/layouts/partials/custom/head-end.html b/layouts/partials/custom/head-end.html new file mode 100644 index 0000000..37c71b4 --- /dev/null +++ b/layouts/partials/custom/head-end.html @@ -0,0 +1,4 @@ +{{ with resources.Get "css/desktop-dock.css" }} + {{ $css := . | minify | fingerprint }} + +{{ end }} From a2229b3ddf38c822d7f6956211033f17623c9dc8 Mon Sep 17 00:00:00 2001 From: "Kellen.Xavier" Date: Thu, 27 Aug 2026 16:52:24 -0300 Subject: [PATCH 3/4] docs: detalha como testar o site localmente no README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Substitui a seção de setup (em inglês, com um typo que quebrava o comando cd) por um passo a passo em português: pré-requisitos, como rodar o servidor local com -D/-F, onde acessar, e o build de produção que vale rodar antes de abrir um PR. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01DArf4UAVNWE4Ln1kgrsege --- README.md | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8027124..ac73740 100644 --- a/README.md +++ b/README.md @@ -10,17 +10,37 @@ Programming and Quality. This is not a tutorials. This is my daily study, buildi Well ok. Where from this idea? Good, this ideia emerge from the book The Programatic Programmer. Specifically, of the chapter one [get to know the book here](https://www.amazon.com.br/Programador-Pragm%C3%A1tico-Aprendiz-Mestre/dp/8577807002). -## Clone Runner and Testing +## Testando localmente + +**Pré-requisitos:** [Hugo extended](https://gohugo.io/installation/) `0.147.9`+ +e [Go](https://go.dev/dl/) (só é usado pra baixar o tema via Hugo Modules, +não tem código Go no projeto). Se preferir não instalar nada na sua máquina, +o repo já vem pronto pra abrir num Dev Container (`.devcontainer/`) ou no +[Gitpod](https://gitpod.io/#https://github.com/kellen-xavier/ladydebug.github.io) — +os dois sobem o ambiente completo (Hugo, Go, Ruby) sozinhos. ```bash git clone https://github.com/kellen-xavier/ladydebug.github.io.git +cd ladydebug.github.io -cd ladydebug.github. io - +# baixa o tema (Hextra) via Hugo Modules hugo mod tidy +# sobe o servidor local com live reload hugo server -D -F +``` +Abra [http://localhost:1313/](http://localhost:1313/). `-D` mostra posts marcados como `draft: true` +e `-F` mostra posts com data no futuro — assim dá pra revisar tudo antes de +publicar. Qualquer mudança em `content/`, `layouts/`, `assets/` ou +`data/` recarrega a página sozinha. + +Antes de abrir um PR, vale rodar também o [lint](#lint-de-markdown) e um build +de produção igual ao do CI, pra pegar erro de template que só aparece com +`--minify`: + +```bash +hugo --gc --minify ``` ## Lint de Markdown From 38e09eb067e06f43b0502571c1990cada91ab555 Mon Sep 17 00:00:00 2001 From: "Kellen.Xavier" Date: Thu, 27 Aug 2026 17:33:14 -0300 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20janelas=20da=20doca=20coladas=20na?= =?UTF-8?q?=20navbar,=20bot=C3=A3o=20fechar,=20e=20limpa=20rodap=C3=A9=20d?= =?UTF-8?q?uplicado?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrige os 4 problemas reportados no layout da doca: - Janelas abriam ancoradas perto do topo (16px), sobrepondo a navbar fixa do tema. Reposicionadas para abrir coladas acima da doca, ancoradas horizontalmente perto do ícone clicado. - Botão fechar (x) não fechava de fato: a própria regra "display: flex" do .dock-window sobrescrevia o "display: none" nativo do fechado (author beats user-agent no cascade), deixando a janela "fechada" invisível mas ainda clicável por cima do que estivesse atrás. Corrigido com uma regra explícita .dock-window:not([open]) { display: none }, e a transição de abertura trocada por uma animação simples (sem o antigo risco de allow-discrete). - Efeito "pixel art" nas janelas: cantos retos, borda bevel 3D (via --bevel-light/--bevel-dark), sombra dura sem blur, barra de título com fonte monoespaçada em maiúsculas na cor de destaque, e um pop-in leve ao abrir. - Também corrigido, achado ao testar interação real com várias janelas abertas: com 3 painéis simultâneos, uma janela aberta depois podia cobrir por completo a barra de título de outra. Adiciona "trazer para frente" ao interagir com qualquer janela (raiseWindow) e aumenta o espaçamento do cascateamento. - hugo.yaml: theme.displayToggle: false e footer.displayPoweredBy: false — o Hextra só mostra o seletor de tema completo e "Com a tecnologia de Hextra" nesse rodapé como fallback de quando a sidebar esquerda está oculta (nosso caso na home). O toggle continua funcionando normalmente pelo item "Tema" do menu, no cabeçalho. Testado com Playwright (chromium real, não só screenshot) simulando clique, drag e fechar em cenários com 1 e 3 janelas abertas — inclusive o bug do botão fechar só apareceu assim, screenshot estático não pegava. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01DArf4UAVNWE4Ln1kgrsege --- assets/css/desktop-dock.css | 98 +++++++++++++++++++++++++------------ assets/js/desktop-dock.js | 53 +++++++++++++++----- hugo.yaml | 6 ++- 3 files changed, 115 insertions(+), 42 deletions(-) diff --git a/assets/css/desktop-dock.css b/assets/css/desktop-dock.css index 972d53a..c69e6d5 100644 --- a/assets/css/desktop-dock.css +++ b/assets/css/desktop-dock.css @@ -5,6 +5,8 @@ :root { --dock-height: 4rem; + --bevel-light: rgba(255, 255, 255, 0.75); + --bevel-dark: rgba(0, 0, 0, 0.35); } body { @@ -37,18 +39,17 @@ body { gap: 0.15rem; width: 4.25rem; padding: 0.35rem 0.25rem; - border: 1px solid var(--ld-border); - border-radius: 0.5rem; + border-width: 2px; + border-style: solid; + border-color: var(--bevel-light) var(--bevel-dark) var(--bevel-dark) var(--bevel-light); + border-radius: 0; background: var(--ld-bg); color: var(--ld-muted); cursor: pointer; - box-shadow: - inset 0 1px 0 rgba(255, 255, 255, 0.6), - 0 1px 2px rgba(0, 0, 0, 0.06); + box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.15); transition: transform 0.1s ease, - color 0.15s ease, - border-color 0.15s ease; + color 0.15s ease; } .dock__item svg { @@ -58,15 +59,14 @@ body { .dock__item:hover { color: var(--ld-accent); - border-color: var(--ld-accent); } .dock__item:active, .dock__item[aria-pressed="true"] { - transform: translateY(1px); + transform: translate(2px, 2px); color: var(--ld-accent); - border-color: var(--ld-accent); - box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.15); + border-color: var(--bevel-dark) var(--bevel-light) var(--bevel-light) var(--bevel-dark); + box-shadow: none; } .dock__label { @@ -75,20 +75,44 @@ body { white-space: nowrap; } -/* Janelas flutuantes () */ +/* Janelas flutuantes () — visual "pixel art" (cantos retos, bevel + 3D, sombra dura sem blur) inspirado em UI de desktop do fim dos anos 2000 */ .dock-window { position: fixed; + z-index: 50; width: min(22rem, calc(100vw - 2rem)); max-height: min(28rem, calc(100vh - var(--dock-height) - 3rem)); margin: 0; padding: 0; display: flex; flex-direction: column; - border: 1px solid var(--ld-border); - border-radius: 0.5rem; + border-width: 2px; + border-style: solid; + border-color: var(--bevel-light) var(--bevel-dark) var(--bevel-dark) var(--bevel-light); + border-radius: 0; background: var(--ld-bg); color: var(--ld-fg); - box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18); + box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.25); + animation: dock-window-in 0.12s ease; +} + +/* Garante que a janela fechada some de vez (clique/hit-test incluídos). + Sem isso, a regra "display: flex" acima ganha do "display: none" nativo + do fechado e a janela "fechada" continua invisível-mas-clicável + por cima do que estiver atrás dela. */ +.dock-window:not([open]) { + display: none; +} + +@keyframes dock-window-in { + from { + transform: scale(0.94); + opacity: 0; + } + to { + transform: scale(1); + opacity: 1; + } } .dock-window::backdrop { @@ -101,12 +125,15 @@ body { align-items: center; justify-content: space-between; gap: 0.5rem; - padding: 0.5rem 0.75rem; - background: var(--ld-bg-soft); - border-bottom: 1px solid var(--ld-border); - border-radius: 0.5rem 0.5rem 0 0; - font-size: 0.8rem; + padding: 0.4rem 0.5rem; + background: var(--ld-accent); + color: var(--ld-bg); + border-bottom: 2px solid var(--bevel-dark); + font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; + font-size: 0.75rem; font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; cursor: grab; user-select: none; } @@ -117,23 +144,29 @@ body { .dock-window__close { flex: 0 0 auto; - width: 1.5rem; - height: 1.5rem; + width: 1.25rem; + height: 1.25rem; display: inline-flex; align-items: center; justify-content: center; - border: 1px solid var(--ld-border); - border-radius: 0.25rem; - background: var(--ld-bg); - color: var(--ld-muted); - font-size: 0.9rem; + border-width: 2px; + border-style: solid; + border-color: var(--bevel-light) var(--bevel-dark) var(--bevel-dark) var(--bevel-light); + border-radius: 0; + background: var(--ld-bg-soft); + color: var(--ld-fg); + font-size: 0.8rem; line-height: 1; cursor: pointer; } .dock-window__close:hover { color: var(--ld-accent); - border-color: var(--ld-accent); +} + +.dock-window__close:active { + border-color: var(--bevel-dark) var(--bevel-light) var(--bevel-light) var(--bevel-dark); + transform: translate(1px, 1px); } .dock-window__body { @@ -151,12 +184,17 @@ body { font-size: 0.875rem; } -/* Player Spotify (carregado sob demanda) */ +/* Player Spotify (carregado sob demanda). min-height no wrapper garante que + a altura da janela já nasça correta, mesmo antes do iframe existir — + evita a janela "crescer" (e cobrir a doca) depois de posicionada. */ +.dock-window__embed [data-embed-src] { + min-height: 152px; +} + .dock-window__embed iframe { width: 100%; height: 152px; border: 0; - border-radius: 0.5rem; } .dock-window__spotify-link { diff --git a/assets/js/desktop-dock.js b/assets/js/desktop-dock.js index c84ea94..8c11271 100644 --- a/assets/js/desktop-dock.js +++ b/assets/js/desktop-dock.js @@ -3,18 +3,43 @@ if (!dock) return; var openOffset = 0; + var topZ = 50; - function placeWindow(win) { + function navbarHeight() { + var nav = document.querySelector(".hextra-nav-container"); + return nav ? nav.offsetHeight : 0; + } + + function raiseWindow(win) { + topZ += 1; + win.style.zIndex = topZ; + } + + function placeWindow(win, anchorBtn) { var margin = 16; + var gapAboveDock = 12; + var dockHeight = dock.offsetHeight; var width = win.offsetWidth || 352; var height = win.offsetHeight || 280; + + // Ancora perto do ícone clicado, horizontalmente; verticalmente sempre + // colada acima da doca (nunca perto do cabeçalho). + var left = margin + openOffset; + if (anchorBtn) { + var rect = anchorBtn.getBoundingClientRect(); + left = rect.left + rect.width / 2 - width / 2 + openOffset; + } + var top = window.innerHeight - dockHeight - gapAboveDock - height - openOffset; + var maxLeft = window.innerWidth - width - margin; - var maxTop = window.innerHeight - height - margin; - var left = Math.min(maxLeft, margin + openOffset); - var top = Math.min(maxTop, margin + openOffset); - win.style.left = Math.max(margin, left) + "px"; - win.style.top = Math.max(margin, top) + "px"; - openOffset = (openOffset + 24) % 96; + var maxTop = window.innerHeight - dockHeight - gapAboveDock - height; + var minTop = navbarHeight() + margin; + left = Math.min(maxLeft, Math.max(margin, left)); + top = Math.min(maxTop, Math.max(minTop, top)); + + win.style.left = left + "px"; + win.style.top = top + "px"; + openOffset = (openOffset + 40) % 120; } function loadEmbed(win) { @@ -28,11 +53,12 @@ body.dataset.loaded = "true"; } - function openWindow(win) { + function openWindow(win, anchorBtn) { if (win.open) return; win.show(); - placeWindow(win); loadEmbed(win); + placeWindow(win, anchorBtn); + raiseWindow(win); } function closeWindow(win) { @@ -49,7 +75,7 @@ closeWindow(win); btn.setAttribute("aria-pressed", "false"); } else { - openWindow(win); + openWindow(win, btn); btn.setAttribute("aria-pressed", "true"); } }); @@ -63,6 +89,10 @@ if (btn) btn.setAttribute("aria-pressed", "false"); }); + win.addEventListener("pointerdown", function () { + raiseWindow(win); + }); + var closeBtn = win.querySelector("[data-dock-close]"); if (closeBtn) { closeBtn.addEventListener("click", function () { @@ -92,10 +122,11 @@ bar.addEventListener("pointermove", function (event) { if (!dragging) return; + var minTop = navbarHeight(); var left = startLeft + (event.clientX - startX); var top = startTop + (event.clientY - startY); win.style.left = Math.max(0, left) + "px"; - win.style.top = Math.max(0, top) + "px"; + win.style.top = Math.max(minTop, top) + "px"; }); function stopDrag(event) { diff --git a/hugo.yaml b/hugo.yaml index 901c209..160b766 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -54,6 +54,10 @@ params: theme: default: light + # O toggle de tema já vive no menu (item "Tema", type: theme-toggle). + # Isso desliga só as instâncias extras que o Hextra mostra sozinho + # (rodapé / fundo da sidebar) quando a sidebar esquerda está oculta. + displayToggle: false navbar: displayTitle: true @@ -61,7 +65,7 @@ params: footer: displayCopyright: false - displayPoweredBy: true + displayPoweredBy: false editURL: enable: false