Skip to content

Commit bca981a

Browse files
authored
Deploy v1.1.5
Develop v1.1.5
2 parents da84c06 + b0e3919 commit bca981a

9 files changed

Lines changed: 187 additions & 110 deletions

File tree

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Pixels Visualiser
22

3-
Pixels Visualiser is a lightweight, client-side web app built with vanilla JavaScript, HTML, and CSS. It allows users to load a JSON file from [Pixels app](https://teovogel.me/pixels/) by Teo Vogel, and explore their emotional data through various statistics and charts. You can access the app at [https://pixels-visualiser.gatienh.fr/](https://pixels-visualiser.gatienh.fr/).
3+
Pixels Visualiser is a client-side web app that allows users to load a JSON file from [Pixels app](https://teovogel.me/pixels/) by Teo Vogel, and explore their emotional data through various statistics and charts. You can access the app at [https://pixels-visualiser.gatienh.fr/](https://pixels-visualiser.gatienh.fr/).
44

55
## Features
66

@@ -48,9 +48,16 @@ If you use a different app, you can still use this visualiser by converting your
4848

4949
* Vanilla HTML, JS, CSS
5050
* [Chart.js](https://www.chartjs.org/)
51+
* [Wordcloud2.js](https://github.com/timdream/wordcloud2.js)
5152

5253

5354
## Contributing
5455

5556
If you find a bug or have a feature request, you can [open an issue](https://github.com/Leogendra/Pixels-Visualiser/issues). If you want to contribute to the code, feel free to submit a pull request.
56-
Note that you can set `DEV_MODE = true;` in the `scripts/main.js` (line 31) to enable development mode, which will load the JSON file from the `data/pixels.json` directory instead of requiring a file upload.
57+
Note that you can set `DEV_MODE = true;` in the `scripts/main.js` (line 31) to enable development mode, which will load the JSON file from the `data/pixels.json` directory instead of requiring a file upload.
58+
59+
60+
## Related Projects
61+
62+
If you are using the [Pixels app](https://teovogel.me/pixels/), you might also be interested in these related projects:
63+
- [Pixels Memories](https://github.com/Leogendra/Pixels-Memories): an Android app that allows you to view Pixels from previous years

assets/settings_icon.svg

Lines changed: 1 addition & 0 deletions
Loading

index.html

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77
<title>Pixels Visualiser</title>
88
<link rel="icon" href="assets/favicon.png" type="image/png">
99

10-
<!-- Google Icons -->
11-
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0" />
12-
1310
<link rel="stylesheet" href="styles/main.css">
1411
<link rel="stylesheet" href="styles/pills.css">
1512
<link rel="stylesheet" href="styles/wordcloud.css">
@@ -21,15 +18,13 @@
2118
<body>
2219
<div class="container">
2320
<div class="header">
24-
<a href="https://pixelstracker.app/" target="_blank" rel="noopener noreferrer">
25-
<img class="hero-logo" src="assets/favicon.png" alt="Logo">
26-
</a>
21+
<img class="hero-logo" src="assets/favicon.png" alt="Logo">
2722
<h1>Pixels Visualiser</h1>
2823
</div>
2924

30-
<div class="upload-section">
25+
<div class="upload-section" id="dragAndDropZone">
26+
<label for="fileInput" class="button">Choose or drop a backup file (.json)</label>
3127
<input type="file" id="fileInput" accept=".json" style="display: none;">
32-
<label for="fileInput" class="button">Choose a Backup file (.json)</label>
3328
</div>
3429

3530
<div id="privacyNotice">
@@ -41,7 +36,11 @@ <h1>Pixels Visualiser</h1>
4136
</div>
4237

4338
<div class="help-export">
44-
<p>How to export your Pixels data ?</p>
39+
<p>How to export your
40+
<span>
41+
<a href="https://pixelstracker.app/" target="_blank" rel="noopener noreferrer" class="info-link">Pixels</a>
42+
</span>
43+
data ?</p>
4544
<img src="assets/help_export.png" alt="How to export Pixels data" title="How to export your Pixels data" class="help-image">
4645
</div>
4746
</div>
@@ -51,7 +50,7 @@ <h1>Pixels Visualiser</h1>
5150
<div class="pill-selector">
5251
<button class="pill" data-range="7">Last week</button>
5352
<button class="pill" data-range="30">Last month</button>
54-
<button class="pill" data-range="90">Last 3 months</button>
53+
<button class="pill" data-range="180">Last 6 months</button>
5554
<button class="pill" data-range="365">Last year</button>
5655
<button class="pill" data-range="1095">Last 3 years</button>
5756
<button class="pill active" data-range="99999">All time</button>
@@ -179,8 +178,7 @@ <h2>📝 Most frequent words</h2>
179178

180179
<div class="option-item">
181180
<label for="searchInput">Search words</label>
182-
<input type="text" id="searchInput" class="input-nb input-large"
183-
placeholder='e.g. "happy", "good day", "been to work"'>
181+
<input type="text" id="searchInput" class="input-nb input-large" placeholder='e.g. "happy", "good day", "been to work"'>
184182
</div>
185183
</div>
186184

@@ -211,7 +209,7 @@ <h2>🖼️ Export Pixel Grid</h2>
211209
<div class="options grid-options">
212210
<div class="option-item">
213211
<button class="button" id="openSettingsDialog">
214-
<span class="material-symbols-outlined">settings</span>
212+
<img src="assets/settings_icon.svg" alt="Settings" class="icon-settings">
215213
</button>
216214
</div>
217215

@@ -258,13 +256,10 @@ <h3>Image settings</h3>
258256
<label for="colorEmpty">Empty days</label>
259257
<input type="color" id="colorEmpty" value="#f0f2f6">
260258

261-
<div class="empty-line">
262-
<hr></div>
263-
<div class="empty-line">
264-
<hr></div>
259+
<div class="empty-line"><hr></div>
260+
<div class="empty-line"><hr></div>
265261

266-
<label for="squareSizeInput"
267-
title="The size of each Pixel on the generated image">Square size (px)</label>
262+
<label for="squareSizeInput" title="The size of each Pixel on the generated image">Square size (px)</label>
268263
<input type="number" id="squareSizeInput" class="input-nb" min="2" max="50" value="20">
269264

270265
<label for="startOfWeekSelect" title="Which day do you want the week to start on">Start of week</label>
@@ -303,12 +298,11 @@ <h3>Image settings</h3>
303298
</div>
304299

305300
<footer>
306-
<div class="github">
307-
<a href="https://github.com/Leogendra" target="_blank">
308-
<img src="assets/github.png" alt="github">
309-
</a>
310-
</div>
311-
<div class="version">v1.1.3</div>
301+
<a class="github" href="https://github.com/Leogendra" target="_blank">
302+
<img src="assets/github.png" alt="github">
303+
</a>
304+
<div class="version">Version: v1.1.5</div>
305+
<div class="version">Last update: 2025-06-22</div>
312306
</footer>
313307

314308
<!-- Charts.js -->
@@ -320,13 +314,13 @@ <h3>Image settings</h3>
320314
<script src="https://cdnjs.cloudflare.com/ajax/libs/wordcloud2.js/1.1.2/wordcloud2.min.js"></script>
321315

322316
<script src="scripts/utils.js"></script>
317+
<script src="scripts/main.js"></script>
323318
<script src="scripts/card.js"></script>
324319
<script src="scripts/graphs.js"></script>
325320
<script src="scripts/statistics.js"></script>
326321
<script src="scripts/stopwords.js"></script>
327322
<script src="scripts/generate_png.js"></script>
328323
<script src="scripts/storage.js"></script>
329-
<script src="scripts/main.js"></script>
330324
</body>
331325

332326
</html>

scripts/generate_png.js

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,6 @@ const setting_scoreType = document.getElementById("scoreTypeSelect");
2222
const setting_firstDayOfWeek = document.getElementById("startOfWeekSelect");
2323
const setting_layout = document.getElementById("layoutSelect");
2424

25-
const png_default_settings = {
26-
colors: {
27-
1: "#e22230",
28-
2: "#e28422",
29-
3: "#fbee45",
30-
4: "#a0e865",
31-
5: "#039d07",
32-
empty: "#f0f2f6"
33-
},
34-
scoreType: "avg",
35-
firstDayOfWeek: 1,
36-
squareSize: 20,
37-
layout: "vertical-weeks"
38-
};
39-
let png_settings = png_default_settings;
4025
let pixelsCanvas;
4126

4227

scripts/main.js

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const file_input = document.querySelector("#fileInput");
2+
const drag_and_drop_zone = document.querySelector("#dragAndDropZone");
23
const privacy_notice = document.querySelector("#privacyNotice");
34

45
const content_container = document.querySelector("#content");
@@ -70,6 +71,22 @@ let wordcloudSize = 4;
7071
let wordcloudSpacing = 2;
7172
let wordcloudBgColor = "#f0f2f6"; // not editable
7273
let maxWordcloudWords = 150; // not editable
74+
const png_default_settings = {
75+
colors: {
76+
1: "#e22230",
77+
2: "#e28422",
78+
3: "#fbee45",
79+
4: "#a0e865",
80+
5: "#039d07",
81+
empty: "#f0f2f6"
82+
},
83+
scoreType: "avg",
84+
firstDayOfWeek: 1,
85+
squareSize: 20,
86+
layout: "vertical-weeks"
87+
};
88+
let png_settings = png_default_settings;
89+
7390

7491

7592

@@ -228,6 +245,32 @@ document.addEventListener("DOMContentLoaded", () => {
228245
await handle_file_upload(file);
229246
});
230247

248+
drag_and_drop_zone.addEventListener("dragover", (e) => {
249+
e.preventDefault();
250+
drag_and_drop_zone.classList.add("dragover");
251+
});
252+
253+
drag_and_drop_zone.addEventListener("dragleave", () => {
254+
drag_and_drop_zone.classList.remove("dragover");
255+
});
256+
257+
drag_and_drop_zone.addEventListener("drop", (e) => {
258+
e.preventDefault();
259+
drag_and_drop_zone.classList.remove("dragover");
260+
261+
const files = e.dataTransfer.files;
262+
if (files.length > 0) {
263+
const file = files[0];
264+
if (file.type === "application/json" || file.name.endsWith(".json")) {
265+
handle_file_upload(file);
266+
}
267+
else {
268+
// TODO: add a CSS-UX-friendly message
269+
alert("Please drop a valid .json file");
270+
}
271+
}
272+
});
273+
231274

232275
// Pills filter
233276
range_pills.forEach(pill => {
@@ -243,35 +286,30 @@ document.addEventListener("DOMContentLoaded", () => {
243286
// Averaging slider
244287
rolling_slider.addEventListener("input", (e) => {
245288
averagingValue = parseInt(e.target.value);
246-
store_settings();
247289
rolling_slider_text_value.textContent = averagingValue;
248290
create_mood_chart(current_data, averagingValue, showAverage, showYears);
249291
});
250292

251293
show_average_checkbox.addEventListener("change", (e) => {
252294
showAverage = e.target.checked;
253-
store_settings();
254295
create_mood_chart(current_data, averagingValue, showAverage, showYears);
255296
});
256297

257298
show_years_checkbox.addEventListener("change", (e) => {
258299
showYears = e.target.checked;
259-
store_settings();
260300
create_mood_chart(current_data, averagingValue, showAverage, showYears);
261301
});
262302

263303

264304
// Tags
265305
tag_frequency_checkbox.addEventListener("change", (e) => {
266306
tagsPercentage = e.target.checked;
267-
store_settings();
268307
create_tag_frequency_chart(tagsPercentage, nbMaxTags);
269308
});
270309

271310
nb_tags_inputs.forEach(input => {
272311
input.addEventListener("input", (e) => {
273312
nbMaxTags = parseInt(e.target.value);
274-
store_settings();
275313
nb_tags_inputs.forEach(input => {
276314
input.value = nbMaxTags;
277315
});
@@ -284,42 +322,36 @@ document.addEventListener("DOMContentLoaded", () => {
284322
// Weekdays
285323
weekday_frequency_select.addEventListener("change", (e) => {
286324
firstDayOfWeek = parseInt(e.target.value);
287-
store_settings();
288325
create_weekday_chart(firstDayOfWeek);
289326
});
290327

291328

292329
// Months
293330
season_colors_checkbox.addEventListener("change", (e) => {
294331
seasonColors = e.target.checked;
295-
store_settings();
296332
create_month_chart(seasonColors);
297333
});
298334

299335

300336
// Word search
301337
words_percentage_checkbox.addEventListener("change", (e) => {
302338
wordcloudPercentage = e.target.checked;
303-
store_settings();
304339
create_word_frequency_section(current_data, nbMaxWords, nbMinCount, wordcloudPercentage, searchTerm);
305340
});
306341

307342
words_order_checkbox.addEventListener("change", (e) => {
308343
wordcloudOrderCount = e.target.checked;
309-
store_settings();
310344
get_word_frequency(current_data, wordcloudOrderCount, searchTerm);
311345
create_word_frequency_section(current_data, nbMaxWords, nbMinCount, wordcloudPercentage, searchTerm);
312346
});
313347

314348
words_words_input.addEventListener("input", (e) => {
315349
nbMaxWords = parseInt(e.target.value);
316-
store_settings();
317350
create_word_frequency_section(current_data, nbMaxWords, nbMinCount, wordcloudPercentage, searchTerm);
318351
});
319352

320353
words_count_input.addEventListener("input", (e) => {
321354
nbMinCount = parseInt(e.target.value);
322-
store_settings();
323355
create_word_frequency_section(current_data, nbMaxWords, nbMinCount, wordcloudPercentage, searchTerm);
324356
});
325357

@@ -332,13 +364,11 @@ document.addEventListener("DOMContentLoaded", () => {
332364
// Wordcloud
333365
wordcloud_size_input.addEventListener("input", (e) => {
334366
wordcloudSize = parseInt(e.target.value);
335-
store_settings();
336367
update_wordcloud(nbMinCount);
337368
});
338369

339370
wordcloud_spacing_input.addEventListener("input", (e) => {
340371
wordcloudSpacing = parseInt(e.target.value);
341-
store_settings();
342372
update_wordcloud(nbMinCount);
343373
});
344374

@@ -350,4 +380,22 @@ document.addEventListener("DOMContentLoaded", () => {
350380
if (isMobile) {
351381
words_search_input.placeholder = 'e.g. "good day"';
352382
}
383+
384+
385+
// Export PNG
386+
setting_scoreType.addEventListener("change", (e) => {
387+
png_settings = get_image_settings();
388+
});
389+
390+
setting_firstDayOfWeek.addEventListener("input", (e) => {
391+
png_settings = get_image_settings();
392+
});
393+
394+
395+
// Save settings
396+
const inputs = Array.from(document.querySelectorAll('input, select'));
397+
398+
inputs.forEach(input => {
399+
input.addEventListener('input', store_settings);
400+
});
353401
});

0 commit comments

Comments
 (0)