Skip to content

Commit 35540f3

Browse files
authored
Deploy v1.5.5
Develop v1.5.5
2 parents 3421afe + f54340e commit 35540f3

File tree

11 files changed

+265
-74
lines changed

11 files changed

+265
-74
lines changed

index.html

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ <h1>Pixels Visualiser</h1>
8080
</div>
8181

8282
<div class="chart-container">
83-
<h2>📈 Mood evolution</h2>
83+
<h2>📈 Time evolution</h2>
8484
<div class="options">
8585
<label for="rollingSlider">Rolling average: <span id="rollingValue">1</span> days</label>
8686
<input type="range" id="rollingSlider" class="range-slider" min="1" max="60" value="1">
@@ -94,10 +94,26 @@ <h2>📈 Mood evolution</h2>
9494
<label for="showYearsCheckbox" title="Show a vertical line for each 1st of January of each year">Show years</label>
9595
<input type="checkbox" id="showYearsCheckbox" class="checkbox">
9696
</div>
97+
98+
<div class="option-item">
99+
<label for="showPixelCheckbox" title="Show a card that displays the Pixel of the day when hovering on a point. Hover the card to fix it">Show Pixel</label>
100+
<input type="checkbox" id="showPixelCheckbox" class="checkbox">
101+
</div>
102+
103+
<div class="option-item">
104+
<label for="timeOptionSelect" title="Which statistic do you want to visualise">Option to display</label>
105+
<select id="timeOptionSelect" class="select">
106+
<option value="mood">Mood</option>
107+
<option value="words">Number of words</option>
108+
<option value="tags">Number of tags</option>
109+
</select>
110+
</div>
97111
</div>
98112
<canvas id="moodChart"></canvas>
99113
</div>
100114

115+
<div id="hoverCardContainer"></div>
116+
101117
<div class="grid-charts">
102118
<div class="chart-container" id="tagFrequencyContainer">
103119
<h2>🏷️ Tag frequency</h2>
@@ -322,10 +338,7 @@ <h3>Image settings</h3>
322338
<div class="empty-line"><hr></div>
323339
<div class="empty-line"><hr></div>
324340

325-
<!-- <label for="showDisplayCheckbox" title="Select one word to show on the image, or two words to compare">Display words</label>
326-
<input type="checkbox" id="showDisplayCheckbox" class="checkbox"> -->
327-
328-
<label for="showFilterSelect" title="Select one word to show on the image, or two words to compare">Advanced filter</label>
341+
<label for="showFilterSelect" title="Select one word to show on the image, or two words to compare">Word filter</label>
329342
<select id="showFilterSelect" class="select">
330343
<option value="0">Hide</option>
331344
<option value="1">Filter word</option>
@@ -437,8 +450,8 @@ <h2>📅 Search Pixels by date</h2>
437450
<img src="assets/pixels_memories_logo.png" alt="Pixels Memories">
438451
</a>
439452
</div>
440-
<div class="version">Version: v1.4.2</div>
441-
<div class="version">Last update: 2025-07-03</div>
453+
<div class="version">Version: v1.5.5</div>
454+
<div class="version">Last update: 2025-07-07</div>
442455
</footer>
443456

444457
<!-- Charts.js for graphs -->

scripts/card.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,26 @@ async function show_pixel_card(dateStr, scroll = false) {
117117
}
118118

119119

120+
async function display_floating_card(pixels_data, chartElement, pinCard = false) {
121+
if (hoverDelay) { return; }
122+
123+
if (chartElement.length === 0) {
124+
container_floating_card.style.display = "none";
125+
container_floating_card.innerHTML = "";
126+
return;
127+
}
128+
129+
const pixelIndex = chartElement[0].index;
130+
const pixel = pixels_data[pixelIndex];
131+
const card = await create_pixel_card(pixel);
132+
133+
container_floating_card.innerHTML = "";
134+
container_floating_card.appendChild(card);
135+
container_floating_card.style.display = "block";
136+
isCardPinned = pinCard;
137+
}
138+
139+
120140
async function setup_calendar_frame() {
121141
const { colors, scoreType } = get_image_settings();
122142

scripts/graphs.js

Lines changed: 98 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ let tags_score_chart_instance = null;
1818
let week_score_chart_instance = null;
1919
let month_score_chart_instance = null;
2020

21+
let isCardPinned = false;
22+
let hoverDelay = false;
23+
2124

2225

2326

@@ -63,17 +66,67 @@ function fill_missing_dates(data) {
6366
async function create_mood_chart(data, rollingAverage, displayAverage, displayYears) {
6467
data = fill_missing_dates(data);
6568
const dates = data.map(entry => entry.date);
66-
const rawScores = data.map(entry => average(entry.scores));
69+
70+
let rawScores;
71+
let minValue;
72+
let maxValue;
73+
if (timeOption === "mood") {
74+
rawScores = data.map(entry => average(entry.scores));
75+
minValue = 1;
76+
maxValue = 5;
77+
}
78+
else if (timeOption === "words") { // number of words
79+
rawScores = data.map(entry => {
80+
if (!entry || !entry.notes) { return null; }
81+
const words = entry.notes
82+
.toLowerCase()
83+
.replace(/[^a-zA-Z0-9]+/g, " ")
84+
.split(/\s+/)
85+
.filter(word => word.replace(/[^a-zA-Z]/g, "").length >= 3);
86+
return words.length;
87+
});
88+
minValue = maximum(rawScores);
89+
maxValue = minimum(rawScores);
90+
}
91+
else if (timeOption === "tags") { // number of tags
92+
rawScores = data.map(entry => {
93+
if (!entry || !entry.tags) { return null; }
94+
return entry.tags.reduce((count, tag) => {
95+
if (!Array.isArray(tag.entries)) return count;
96+
return count + tag.entries.length;
97+
}, 0);
98+
});
99+
minValue = maximum(rawScores);
100+
maxValue = minimum(rawScores);
101+
}
67102

68103
const annotations = {};
69104
if (displayAverage) {
70105
annotations["mean"] = {
71106
type: "line",
72107
mode: "horizontal",
73108
scaleID: "y",
74-
value: average(data.map(entry => average(entry.scores))),
109+
value: average(rawScores.filter(score => (score !== null))),
75110
borderColor: "#ff4444",
76111
borderWidth: 2,
112+
label: {
113+
enabled: false,
114+
content: `Mean: ${average(rawScores.filter(score => (score !== null))).toFixed(2)}`,
115+
position: "top",
116+
backgroundColor: "rgba(0,0,0,0.6)",
117+
font: {
118+
size: 10,
119+
weight: "bold"
120+
}
121+
},
122+
enter(ctx) {
123+
ctx.element.options.label.enabled = true;
124+
ctx.chart.draw();
125+
},
126+
leave(ctx) {
127+
ctx.element.options.label.enabled = false;
128+
ctx.chart.draw();
129+
}
77130
}
78131
}
79132

@@ -146,11 +199,13 @@ async function create_mood_chart(data, rollingAverage, displayAverage, displayYe
146199
scales: {
147200
y: {
148201
beginAtZero: true,
149-
min: 1,
150-
max: 5
202+
min: minValue,
203+
max: maxValue
151204
}
152205
},
153-
onClick: (event, elements) => {
206+
onClick: async (event, chartElement) => {
207+
// Legacy function to scroll to Pixel card
208+
/*
154209
const points = mood_chart_instance.getElementsAtEventForMode(event, 'nearest', { intersect: true }, true);
155210
if (points.length > 0) {
156211
const pointIndex = points[0].index;
@@ -159,6 +214,15 @@ async function create_mood_chart(data, rollingAverage, displayAverage, displayYe
159214
160215
show_pixel_card(dateText, scroll=true);
161216
}
217+
*/
218+
display_floating_card(data, chartElement, pinCard=true);
219+
220+
hoverDelay = true;
221+
setTimeout(() => {hoverDelay = false}, 1000);
222+
},
223+
onHover: async function (event, chartElement) {
224+
if (!showPixelCard) { return; }
225+
display_floating_card(data, chartElement);
162226
},
163227
plugins: {
164228
legend: {
@@ -351,4 +415,32 @@ async function create_month_chart(colorsByMonth) {
351415
}
352416
}
353417
});
354-
}
418+
}
419+
420+
421+
422+
423+
canvas_mood.addEventListener("mousemove", async (e) => {
424+
if (isCardPinned || hoverDelay) { return; }
425+
const x = e.clientX + window.scrollX;
426+
const y = e.clientY + window.scrollY;
427+
const margin = -10;
428+
429+
container_floating_card.style.top = `${y + margin}px`;
430+
if (2*x > window.innerWidth) {
431+
container_floating_card.style.right = `${window.innerWidth - x + margin}px`;
432+
container_floating_card.style.left = "auto";
433+
}
434+
else {
435+
container_floating_card.style.left = `${x + margin}px`;
436+
container_floating_card.style.right = "auto";
437+
}
438+
});
439+
440+
441+
container_floating_card.addEventListener("mouseleave", () => {
442+
if (!hoverDelay) {
443+
container_floating_card.style.display = "none";
444+
container_floating_card.innerHTML = "";
445+
}
446+
});

scripts/main.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ const range_pills = document.querySelectorAll(".pill");
1111
const stats_container = document.querySelector("#statsContainer");
1212
const rolling_slider = document.querySelector("#rollingSlider");
1313
const rolling_slider_text_value = document.querySelector("#rollingValue");
14-
15-
const tag_grid_charts = document.querySelector(".grid-charts");
1614
const show_average_checkbox = document.querySelector("#showAverageCheckbox");
1715
const show_years_checkbox = document.querySelector("#showYearsCheckbox");
16+
const show_pixel_checkbox = document.querySelector("#showPixelCheckbox");
17+
const select_time_option = document.querySelector("#timeOptionSelect");
18+
const container_floating_card = document.getElementById("hoverCardContainer");
19+
1820
const nb_tags_inputs = document.querySelectorAll(".input-max-tag");
1921
const tag_frequency_checkbox = document.querySelector("#tagFrequencyCheckbox");
2022

@@ -52,6 +54,8 @@ let current_data = [];
5254
let averagingValue = 1;
5355
let showAverage = false;
5456
let showYears = false;
57+
let timeOption = "mood";
58+
let showPixelCard = true;
5559

5660
// Tags
5761
let tag_stats = {};
@@ -95,13 +99,14 @@ const png_default_settings = {
9599
showBorder: false,
96100
showLegend: false,
97101
showDays: false,
102+
showFilter: 0,
98103
scoreType: "avg",
99104
layout: "vertical-weeks"
100105
};
101106
let png_settings = png_default_settings;
102107

103108
// Card
104-
let getDynamicBorders = true
109+
let getDynamicBorders = true; // not editable
105110

106111

107112

@@ -339,6 +344,15 @@ document.addEventListener("DOMContentLoaded", () => {
339344
create_mood_chart(current_data, averagingValue, showAverage, showYears);
340345
});
341346

347+
show_pixel_checkbox.addEventListener("change", (e) => {
348+
showPixelCard = e.target.checked;
349+
});
350+
351+
select_time_option.addEventListener("change", (e) => {
352+
timeOption = e.target.value;
353+
create_mood_chart(current_data, averagingValue, showAverage, showYears);
354+
});
355+
342356

343357
// Tags
344358
tag_frequency_checkbox.addEventListener("change", (e) => {

scripts/png.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const div_pixel_export_container = document.querySelector("#pixelGridExportContainer");
2+
13
const dialog_settings = document.querySelector("#dialogSettings");
24
const btn_open_dialog_settings = document.querySelector("#openImageSettingsDialog");
35
const btn_reset_dialog_settings = document.querySelector("#resetSettingsDialog");
@@ -153,6 +155,7 @@ function get_pixel_color(scores, colors, scoreType) {
153155

154156
async function set_tags_selects() {
155157
const all_tags = Object.keys(tag_stats.counts);
158+
all_tags.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
156159
if (!all_tags || all_tags.length === 0) {
157160
compareTagSelect1.innerHTML = "<option value=''>No tags available</option>";
158161
compareTagSelect2.innerHTML = "<option value=''>No tags available</option>";
@@ -355,7 +358,7 @@ async function generate_pixels_PNG(data) {
355358

356359

357360
// Draw a gradient if scores are available
358-
if ((scoreType == "gradient") && pixel && pixel.scores && (pixel.scores.length > 1)) {
361+
if ((scoreType === "gradient") && pixel && pixel.scores && (pixel.scores.length > 1)) {
359362
const gradient = ctx.createLinearGradient(x, y, x + squareSize, y); // horizontal
360363

361364
const scores = pixel.scores;
@@ -581,17 +584,18 @@ function filter_pixels_by_two_keywords(data, keyword1, keyword2, isTag1 = false,
581584

582585

583586
function get_compare_settings(data) {
587+
const showFilter = parseInt(setting_showFilter.value, 10);
584588
const compareTag1 = compareSelect1.value === "tag";
585589
const compareTag2 = compareSelect2.value === "tag";
586590
const value1 = compareTag1 ? compareTagSelect1.value : compareWordInput1.value.trim();
587591
const value2 = compareTag2 ? compareTagSelect2.value : compareWordInput2.value.trim();
588-
if (value1 && value2) {
592+
if ((showFilter > 1) && value1 && value2) {
589593
return filter_pixels_by_two_keywords(data, value1, value2, compareTag1, compareTag2);
590594
}
591-
else if (value1) {
595+
else if ((showFilter > 0) && value1) {
592596
return filter_pixels_by_keyword(data, value1, compareTag1);
593597
}
594-
else if (value2) {
598+
else if ((showFilter > 0) && value2) {
595599
return filter_pixels_by_keyword(data, value2, compareTag2);
596600
}
597601
else {
@@ -624,6 +628,7 @@ function close_dialog_settings(save = false) {
624628
if (save) { png_settings = get_image_settings(); }
625629
set_image_settings(png_settings);
626630
store_settings();
631+
setup_calendar_frame();
627632
dialog_settings.close();
628633
}
629634

@@ -677,4 +682,11 @@ setting_showFilter.addEventListener("change", () => {
677682
inputWrapper.style.display = isTag ? "none" : "block";
678683
tagWrapper.style.display = isTag ? "block" : "none";
679684
});
685+
});
686+
687+
div_pixel_export_container.addEventListener("keydown", function (e) {
688+
if (e.key === "Enter") {
689+
e.preventDefault();
690+
btn_generate_png.click();
691+
}
680692
});

scripts/statistics.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,10 +250,10 @@ function get_word_frequency(data, orderByMood, minScore, searchText) {
250250

251251
// Filter words of the notes
252252
let words = notesLower
253-
.replace(/[^a-zA-Z0-9]+/g, " ")
253+
.replace(/[^\p{L}\p{N}\p{Extended_Pictographic}\u200D\uFE0F]+/gu, " ")
254254
.split(/\s+/)
255255
.filter(word =>
256-
(word.replace(/[^a-zA-Z]/g, "").length >= 3) && // Word is at least 3 letters long
256+
(word.replace(/[^a-zA-Z]/g, "").length >= 3 || /\p{Extended_Pictographic}/u.test(word)) && // Word is at least 3 letters long or emoji
257257
(!STOP_WORDS.has(word)) && // Word is not a stop word
258258
(
259259
(searchWords.length === 0) || // Either no search words or

0 commit comments

Comments
 (0)