Busca por patterns e indexação de tags de patterns

This commit is contained in:
JotaChina 2025-11-13 19:47:57 -03:00
parent 3e6fdfd324
commit 3e6986e07c
7 changed files with 3738 additions and 23 deletions

View File

@ -149,35 +149,79 @@ title: "{{ page.file }}"
</span>
{% comment %}
>>> INÍCIO DA LÓGICA DE STEPS MELHORADA <<<
Container da grade de steps
>>> INÍCIO DA LÓGICA DE STEPS CLICÁVEIS (4-steps) <<<
(O container agora é 'row' para manter os chunks na horizontal)
{% endcomment %}
<div style="display: flex; flex-direction: row; border: 1px solid #ccc; padding: 2px; border-radius: 2px; background-color: #f0f0f0;">
{% for step_active in pattern_steps %}
<div class="patterns-container" style="display: flex; flex-direction: row; flex-wrap: wrap; gap: 4px;">
{% assign total_steps = pattern_steps.size %}
{% assign chunk_size = 4 %}
{% comment %} Define a cor do step {% endcomment %}
{% assign step_color = '#d9d9d9' %} {% comment %} Cor INATIVA (cinza claro) {% endcomment %}
{% comment %} Calcula quantos pedaços de 4 steps teremos {% endcomment %}
{% assign num_chunks = total_steps | divided_by: chunk_size %}
{% comment %}
FIX: Primeiro calculamos o 'resto' e salvamos numa variável
{% endcomment %}
{% assign remainder = total_steps | modulo: chunk_size %}
{% comment %}
FIX: Agora sim usamos a variável na comparação
{% endcomment %}
{% if remainder > 0 %}
{% assign num_chunks = num_chunks | plus: 1 %}
{% endif %}
{% for i in (0..num_chunks) %}
{% assign start_index = i | times: chunk_size %}
{% comment %} Pega a fatia de 4 steps {% endcomment %}
{% assign current_chunk_array = pattern_steps | slice: start_index, chunk_size %}
{% if current_chunk_array.size > 0 %}
{% comment %} 1. Serializa o chunk (ex: [1,0,0,1] -> "1001") {% endcomment %}
{% assign chunk_string = "" %}
{% for step in current_chunk_array %}
{% if step == true or step == 'true' or step == 1 %}
{% assign chunk_string = chunk_string | append: '1' %}
{% else %}
{% assign chunk_string = chunk_string | append: '0' %}
{% endif %}
{% endfor %}
{% comment %}
2. Cria o link clicável
{% endcomment %}
{% assign search_url = '/pattern/?p=' | append: chunk_string | relative_url %}
<a href="{{ search_url }}" title="Buscar padrão {{ chunk_string }}" style="display: inline-block;">
{% comment %} 3. Renderiza o visual do step DENTRO do link {% endcomment %}
<div style="display: flex; flex-direction: row; border: 1px solid #999; padding: 2px; border-radius: 2px; background-color: #f0f0f0;">
{% for step_active in current_chunk_array %}
{% assign step_color = '#d9d9d9' %}
{% if step_active == true or step_active == 'true' or step_active == 1 %}
{% assign step_color = '#4caf50' %} {% comment %} Cor ATIVA (verde) {% endcomment %}
{% assign step_color = '#4caf50' %}
{% endif %}
{% comment %} Define o espaçamento (divisão do compasso) {% endcomment %}
{% assign step_style = '' %}
{% assign step_index_zero_based = forloop.index0 %}
<div style="width: 5px; height: 10px; background-color: {{ step_color }}; border-radius: 1px; margin-right: 1px;"></div>
{% endfor %}
{% comment %} --- Calcula o módulo *antes* do 'if' --- {% endcomment %}
{% assign remainder = step_index_zero_based | modulo: 4 %}
{% if step_index_zero_based > 0 and remainder == 0 %}
{% assign step_style = 'margin-left: 3px;' %}
{% comment %} Preenche com steps vazios se o chunk for menor que 4 {% endcomment %}
{% assign remaining_steps = chunk_size | minus: current_chunk_array.size %}
{% if remaining_steps > 0 %}
{% for j in (1..remaining_steps) %}
<div style="width: 5px; height: 10px; background-color: #d9d9d9; border-radius: 1px; margin-right: 1px;"></div>
{% endfor %}
{% endif %}
<div
style="width: 5px; height: 10px; background-color: {{ step_color }}; border-radius: 1px; {{ step_style }}"
></div>
</div>
</a>
{% endif %}
{% endfor %}
</div>
{% comment %} >>> FIM DA LÓGICA DE STEPS MELHORADA <<< {% endcomment %}
{% comment %} >>> FIM DA LÓGICA DE STEPS CLICÁVEIS <<< {% endcomment %}
</div>
{% endif %}
{% endfor %}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
{"level":30,"time":1762980056562,"pid":2587819,"hostname":"ubuntu","timestamp":1762980056562,"socketId":"QvekitV8wcwavNWAAAGI","action":{"type":"AUDIO_SNAPSHOT_REQUEST","__token":"1","__senderId":"QvekitV8wcwavNWAAAGI","__senderName":"Alicer-Qvek"},"msg":"action_received"}
{"level":30,"time":1762987723672,"pid":2587819,"hostname":"ubuntu","timestamp":1762987723672,"socketId":"KmizFldq5cpxphv5AAGw","action":{"type":"AUDIO_SNAPSHOT_REQUEST","__token":"1","__senderId":"KmizFldq5cpxphv5AAGw","__senderName":"Alicer-Kmiz"},"msg":"action_received"}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,21 @@
[
"Computacao_Musical_Thiago_Rocha_de_Moraes.mmp",
"FelipeFerraz.mmp",
"Gustavo Castro.mmp",
"Iara Rodrigues.mmp",
"JoãoVitorSimão-2023008380-megaMainSong.mmp",
"Samanta Freire.mmp",
"SonsAlignígenas.mmp",
"Um começo.mmp",
"VitorAugusto.mmp",
"Wesley_Silva_Guimarães.mmp",
"beatJulioCesardeSousa.mmp",
"carlos.mmp",
"deprecado_wallace.mmp",
"eliasMendesSong.mmp",
"gabriel_gomes.mmp",
"leandro_souza_atvd1.mmp",
"teste.mmp",
"tp1 - Luiz Filipe Almada.mmp",
"tp1_MarcoAntonio.mmp"
]

File diff suppressed because it is too large Load Diff

404
pattern.html Normal file
View File

@ -0,0 +1,404 @@
---
layout: default
title: Projetos por Pattern Rítmico
permalink: /pattern/
---
<meta charset="utf-8" />
<main class="main-content">
<div class="publication">{% include sidebar.html %}</div>
<div class="container">
<div class="columns is-mobile is-vcentered" style="margin-bottom: 2rem">
<div class="column is-auto">
<h2 class="title is-4">
<code>Projetos que possuem patterns similares:</code>
</h2>
</div>
<div class="column is-auto">
<button id="clearFilterButton" class="button is-small is-light">
Limpar filtro
</button>
</div>
</div> <style>
/* Estilos para o nosso sequencer de 16 steps */
.pattern-search-box {
display: flex;
flex-wrap: wrap; /* Permite quebrar a linha se necessário */
gap: 4px;
cursor: pointer;
margin-bottom: 2rem;
padding: 8px;
background-color: #f5f5f5;
border: 1px solid #dbdbdb;
border-radius: 4px;
display: inline-flex;
}
.search-step {
/* MODIFICADO: Steps menores para caber 16 */
width: 18px;
height: 30px;
background-color: #d9d9d9; /* Cor INATIVA */
border: 1px solid #999;
border-radius: 3px;
transition: background-color 0.1s ease;
}
.search-step.is-active {
background-color: #4caf50; /* Cor ATIVA (verde) */
}
/* NOVO: Adiciona um espaço extra a cada 4 steps para legibilidade */
.search-step:nth-child(4n):not(:last-child) {
margin-right: 8px;
}
</style>
<div class="field">
<label class="label"><code>Desenhe um pattern (até 16 steps) para buscar:</code></label>
<div id="pattern-search-box" class="pattern-search-box">
<div class="search-step" data-index="0"></div>
<div class="search-step" data-index="1"></div>
<div class="search-step" data-index="2"></div>
<div class="search-step" data-index="3"></div>
<div class="search-step" data-index="4"></div>
<div class="search-step" data-index="5"></div>
<div class="search-step" data-index="6"></div>
<div class="search-step" data-index="7"></div>
<div class="search-step" data-index="8"></div>
<div class="search-step" data-index="9"></div>
<div class="search-step" data-index="10"></div>
<div class="search-step" data-index="11"></div>
<div class="search-step" data-index="12"></div>
<div class="search-step" data-index="13"></div>
<div class="search-step" data-index="14"></div>
<div class="search-step" data-index="15"></div>
</div>
</div>
<div id="project-list" class="columns is-multiline">
{% comment %}
... O SEU LOOP LIQUID DE PROJETOS VEM AQUI ...
NENHUMA MUDANÇA É NECESSÁRIA NO LIQUID.
Ele continua exatamente como estava, gerando os 'project-item'
e os 'data-patterns' com chunks de 4 steps.
{% endcomment %}
{% for projeto in site.data.all %}
{% assign project_patterns_flat = "" | split: "," %}
{% assign project_patterns_data = "" | split: "," %}
{% for track in projeto.tracks %}
{% if track.instruments %}
{% for instrument in track.instruments %}
{% assign display_name = instrument.instrument_name %}
{% if display_name contains "audiofileprocessor" and instrument.patterns %}
{% assign first_pattern_name = instrument.patterns | map: 'name' | first %}
{% if first_pattern_name and first_pattern_name != empty %}
{% assign display_name = first_pattern_name | remove: ".ogg" | remove: ".wav" | remove: ".flac" | remove: ".mp3" %}
{% elsif instrument.audiofileprocessor.src %}
{% assign src_parts = instrument.audiofileprocessor.src | split: '/' %}
{% assign file_name = src_parts | last %}
{% assign display_name = file_name | remove: ".ogg" | remove: ".wav" | remove: ".flac" | remove: ".mp3" %}
{% endif %}
{% endif %}
{% if display_name == nil or display_name == "" %}
{% assign display_name = "Instrumento" %}
{% endif %}
{% if instrument.patterns %}
{% for pattern in instrument.patterns %}
{% assign pattern_steps = pattern.steps %}
{% if pattern_steps and pattern_steps.size > 0 %}
{% assign total_steps = pattern_steps.size %}
{% assign chunk_size = 4 %}
{% assign num_chunks = total_steps | divided_by: chunk_size %}
{% assign remainder = total_steps | modulo: chunk_size %}
{% if remainder > 0 %}
{% assign num_chunks = num_chunks | plus: 1 %}
{% endif %}
{% for i in (0..num_chunks) %}
{% assign start_index = i | times: chunk_size %}
{% assign current_chunk_array = pattern_steps | slice: start_index, chunk_size %}
{% if current_chunk_array.size > 0 %}
{% assign chunk_string = "" %}
{% for step in current_chunk_array %}
{% if step == true or step == 'true' or step == 1 %}
{% assign chunk_string = chunk_string | append: '1' %}
{% else %}
{% assign chunk_string = chunk_string | append: '0' %}
{% endif %}
{% endfor %}
{% assign data_pair = chunk_string | append: '::' | append: display_name %}
{% unless project_patterns_flat contains chunk_string %}
{% assign project_patterns_flat = project_patterns_flat | push: chunk_string %}
{% endunless %}
{% unless project_patterns_data contains data_pair %}
{% assign project_patterns_data = project_patterns_data | push: data_pair %}
{% endunless %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% if project_patterns_flat.size > 0 %}
{% assign project_patterns_string = project_patterns_flat | join: ',' %}
<div
class="column is-6 project-item"
data-patterns="{{ project_patterns_string }}"
>
<div class="box">
{% assign file_url = projeto.file | downcase | replace: ' ', '-' |
replace: 'ç', 'c' | replace: 'ã', 'a' | replace: 'á', 'a' | replace:
'â', 'a' | replace: 'é', 'e' | replace: 'ê', 'e' | replace: 'í', 'i' |
replace: 'ó', 'o' | replace: 'ô', 'o' | replace: 'õ', 'o' | replace:
'ú', 'u' %}
<a
href="../mmp_pages/{{ file_url }}.html"
class="button is-link is-fullwidth"
>
{{ projeto.file }}
</a>
<div style="margin-top: 1rem">
<p><strong>Patterns (4-steps):</strong></p>
<div class="tags" style="margin-top: 0.5rem;">
{% for data_pair_string in project_patterns_data %}
{% assign pair_parts = data_pair_string | split: '::' %}
{% assign p_string = pair_parts[0] %}
{% assign p_instrument = pair_parts[1] | default: '?' %}
<a href="#" class="pattern-item tag is-info is-light" data-pattern="{{ p_string }}" title="Filtrar por {{ p_string }} (de {{ p_instrument }})">
<div style="display: flex; flex-direction: row; border: 1px solid #999; padding: 2px; border-radius: 2px; background-color: #fff; margin-right: 5px;">
{% assign p_array = p_string | split: "" %}
{% for step_char in p_array %}
{% assign step_color = '#d9d9d9' %}
{% if step_char == '1' %}
{% assign step_color = '#4caf50' %}
{% endif %}
<div style="width: 5px; height: 10px; background-color: {{ step_color }}; border-radius: 1px; margin-right: 1px;"></div>
{% endfor %}
</div>
<span class="is-size-7" style="margin-right: 5px;">{{ p_instrument }}:</span>
<code>{{ p_string }}</code>
</a>
{% endfor %}
</div>
</div>
</div>
</div>
{% endif %}
{% endfor %}
</div>
</div>
</main>
{% comment %}
SCRIPT FINAL (TOTALMENTE REESCRITO PARA 16-STEPS "AND" FILTER)
{% endcomment %}
<script>
document.addEventListener("DOMContentLoaded", function () {
const projects = document.querySelectorAll(".project-item");
const allPatternItems = document.querySelectorAll(".pattern-item");
const clearFilterButton = document.querySelector("#clearFilterButton");
const searchSteps = document.querySelectorAll(".search-step"); // Agora tem 16
/**
* "Fonte da verdade": um array de chunks de 4 steps (ex: ["1001", "0010"])
* que estão ativos no filtro.
*/
let activeChunks = [];
/**
* Pega o desenho de 16 steps e o quebra em 4 chunks.
* @returns {string[]} ex: ["1001", "0010", "0000", "0000"]
*/
function getChunksFromSearchBox() {
let fullPattern = "";
searchSteps.forEach(step => {
fullPattern += step.classList.contains("is-active") ? "1" : "0";
});
// Quebra a string "10010010..." em ["1001", "0010", ...]
return fullPattern.match(/.{1,4}/g) || [];
}
/**
* Pega os chunks ativos (ex: ["1001", "0010"]) e desenha
* o pattern de 16 steps (ex: "1001001000000000") na caixa.
*/
function setSearchBoxFromChunks(chunks) {
let fullPattern = (chunks || []).join("").padEnd(16, "0");
const bits = fullPattern.split("");
searchSteps.forEach((step, index) => {
if (bits[index] === "1") {
step.classList.add("is-active");
} else {
step.classList.remove("is-active");
}
});
}
/**
* Filtra a lista de projetos.
* Um projeto SÓ aparece se ele tiver TODOS os activeChunks.
*/
function filterByPattern(chunks) {
// Se não há chunks ativos (e não vazios), mostra tudo
const effectiveChunks = chunks.filter(c => c !== "0000");
if (effectiveChunks.length === 0) {
projects.forEach(project => project.style.display = "block");
return;
}
projects.forEach((project) => {
const projectPatterns = project
.getAttribute("data-patterns")
.split(",");
// Lógica "AND": .every() verifica se TODOS os chunks são verdadeiros
const isMatch = effectiveChunks.every(chunk =>
projectPatterns.includes(chunk)
);
project.style.display = isMatch ? "block" : "none";
});
}
/**
* Destaca MÚLTIPLAS tags que estão no filtro "AND".
*/
function highlightActiveFilter(chunks) {
allPatternItems.forEach((item) => {
const itemPattern = item.getAttribute("data-pattern");
// Se o pattern da tag ESTÁ no array de chunks ativos
if (chunks.includes(itemPattern)) {
item.classList.remove("is-light"); // Fica sólido
} else {
item.classList.add("is-info", "is-light"); // Fica light
}
});
}
/**
* Atualiza a URL com um parâmetro ?p=1001,0010
*/
function updateUrl(chunks) {
const newUrl = new URL(window.location.href);
// Filtra "0000" antes de salvar na URL
const effectiveChunks = chunks.filter(c => c !== "0000");
if (effectiveChunks.length > 0) {
newUrl.searchParams.set("p", effectiveChunks.join(","));
} else {
newUrl.searchParams.delete("p");
}
window.history.replaceState({}, "", newUrl);
}
/**
* A NOVA função principal que sincroniza tudo.
*/
function runFilter() {
// 1. Lê o desenho e pega os chunks ativos
const chunksFromBox = getChunksFromSearchBox();
activeChunks = chunksFromBox.filter(c => c !== "0000");
// 2. Filtra os projetos (lógica "AND")
filterByPattern(activeChunks);
// 3. Destaca as tags
highlightActiveFilter(activeChunks);
// 4. Atualiza a URL
updateUrl(activeChunks);
}
// ========================================================
// Event Listeners (Gatilhos)
// ========================================================
// 1. Ao carregar a página (lê a URL)
const urlParams = new URLSearchParams(window.location.search);
let patternFromUrl = urlParams.get("p");
if (patternFromUrl) {
activeChunks = patternFromUrl.split(","); // ex: ["1001", "0010"]
setSearchBoxFromChunks(activeChunks); // Desenha "100100100000..."
filterByPattern(activeChunks);
highlightActiveFilter(activeChunks);
}
// 2. Ao clicar numa TAG (na lista de projetos)
allPatternItems.forEach((item) => {
item.addEventListener("click", function (event) {
event.preventDefault();
const selectedPattern = item.getAttribute("data-pattern");
// Lógica: Clicar numa tag a "adiciona" ao filtro "AND"
// Pega o que já está desenhado
let currentChunks = getChunksFromSearchBox().filter(c => c !== "0000");
// Se já está lá, remove (toggle)
if (currentChunks.includes(selectedPattern)) {
currentChunks = currentChunks.filter(c => c !== selectedPattern);
}
// Se não está, adiciona (se houver espaço)
else if (currentChunks.length < 4) {
currentChunks.push(selectedPattern);
}
activeChunks = currentChunks;
// Atualiza tudo
setSearchBoxFromChunks(activeChunks);
filterByPattern(activeChunks);
highlightActiveFilter(activeChunks);
updateUrl(activeChunks);
});
});
// 3. Ao clicar em um STEP da caixa de busca
searchSteps.forEach(step => {
step.addEventListener("click", function() {
// 1. Liga/desliga o step
step.classList.toggle("is-active");
// 2. Roda o filtro principal
runFilter();
});
});
// 4. Botão para limpar filtro
clearFilterButton.addEventListener("click", function () {
activeChunks = [];
setSearchBoxFromChunks([]); // Limpa o desenho
filterByPattern([]); // Mostra todos os projetos
highlightActiveFilter([]); // Limpa os destaques
updateUrl([]); // Limpa a URL
});
});
</script>