Skip to content

Commit 6e49ab2

Browse files
committed
added news block on launch
1 parent 38ce203 commit 6e49ab2

7 files changed

Lines changed: 291 additions & 9 deletions

File tree

Electron/bundle/log.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3018,7 +3018,40 @@ debug.addEventListener('click', () => {
30183018
});
30193019

30203020
const info = document.getElementById("modal_info");
3021+
const newsFeed = document.getElementById("news_feed_items");
30213022

3023+
const escapeHtml = (text) => {
3024+
if (!text) return '';
3025+
return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
3026+
};
3027+
3028+
const renderNewsItems = (items) => {
3029+
if (!newsFeed) return;
3030+
if (!Array.isArray(items) || items.length === 0) {
3031+
newsFeed.innerHTML = '<div style="font-size: 0.75rem; color: #94a3b8;">No WLJS news available.</div>';
3032+
return;
3033+
}
3034+
3035+
newsFeed.innerHTML = items.map((item) => {
3036+
const title = escapeHtml(item.title || 'Untitled');
3037+
const summary = escapeHtml(item.summary || '');
3038+
const dateText = escapeHtml(item.dateText || '');
3039+
const source = escapeHtml(item.source || 'News');
3040+
const url = item.url ? escapeHtml(item.url) : 'https://wljs.io';
3041+
3042+
return `
3043+
<a href="${url}" target="_blank" rel="noreferrer" class="news-item">
3044+
<div class="news-source">${source}</div>
3045+
<div class="news-title">${title}</div>
3046+
<div class="news-meta">${dateText}${summary ? ' · ' + summary : ''}</div>
3047+
</a>`;
3048+
}).join('');
3049+
};
3050+
3051+
3052+
window.electronAPI.handleNews((event, items) => {
3053+
renderNewsItems(items);
3054+
});
30223055

30233056

30243057
window.electronAPI.updateInfo((event, info) => {

Electron/electron.css

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,115 @@ body[data-theme='dark'] {
4949
filter: var(--filter);
5050
}
5151

52+
/* News Feed Styling */
53+
.news-feed {
54+
margin-top: 1rem;
55+
border-radius: 0.5rem;
56+
padding: 0.75rem;
57+
padding-left: 0;
58+
padding-right: 0;
59+
font-size: 0.875rem;
60+
color: #64748b;
61+
}
62+
63+
@media (prefers-color-scheme: dark) {
64+
.news-feed {
65+
color: #cbd5e1;
66+
}
67+
}
68+
69+
.news-feed-title {
70+
font-weight: 600;
71+
color: #1e293b;
72+
margin-bottom: 0.5rem;
73+
}
74+
75+
@media (prefers-color-scheme: dark) {
76+
.news-feed-title {
77+
color: #f1f5f9;
78+
}
79+
}
80+
81+
.news-feed-items {
82+
display: flex;
83+
flex-direction: column;
84+
gap: 0.5rem;
85+
overflow-y: auto;
86+
max-height: 240px;
87+
padding-right: 0.5rem;
88+
}
89+
90+
.news-item {
91+
border-radius: 0.5rem;
92+
border: 1px solid rgba(226, 232, 240, 0.8);
93+
background-color: rgba(248, 250, 252, 0.8);
94+
padding: 0.75rem;
95+
display: block;
96+
text-decoration: none;
97+
color: inherit;
98+
cursor: pointer;
99+
transition: background-color 0.2s ease;
100+
}
101+
102+
.news-item:hover {
103+
background-color: rgba(226, 232, 240, 0.8);
104+
}
105+
106+
@media (prefers-color-scheme: dark) {
107+
.news-item {
108+
border-color: rgba(71, 85, 99, 0.8);
109+
background-color: rgba(3, 7, 18, 0);
110+
}
111+
112+
.news-item:hover {
113+
background-color: rgba(15, 23, 42, 0.8);
114+
}
115+
}
116+
117+
.news-source {
118+
font-size: 0.625rem;
119+
font-weight: 600;
120+
text-transform: uppercase;
121+
letter-spacing: 0.0045em;
122+
color: #64748b;
123+
margin-bottom: 0.25rem;
124+
}
125+
126+
@media (prefers-color-scheme: dark) {
127+
.news-source {
128+
color: #94a3b8;
129+
}
130+
}
131+
132+
.news-title {
133+
margin-top: 0.25rem;
134+
font-size: 0.875rem;
135+
font-weight: 600;
136+
color: #1e293b;
137+
}
138+
139+
.news-title:hover {
140+
text-decoration: underline;
141+
}
142+
143+
@media (prefers-color-scheme: dark) {
144+
.news-title {
145+
color: #f1f5f9;
146+
}
147+
}
148+
149+
.news-meta {
150+
margin-top: 0.25rem;
151+
font-size: 0.6875rem;
152+
color: #64748b;
153+
}
154+
155+
@media (prefers-color-scheme: dark) {
156+
.news-meta {
157+
color: #94a3b8;
158+
}
159+
}
160+
52161
.logs {
53162
height: 200px;
54163
overflow: hidden;

Electron/log.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525

2626
<div class="mt-2 w-full flex flex-row text-sm text-gray-500 dark:text-gray-300">
2727
<button id="debug_button" style="-webkit-app-region: no-drag;" class="inline-flex bg-gray-100/50 dark:bg-gray-600/50 text-xs items-center px-4 outline-gray-400 outline-offset-2 ring-1 text-gray-800 ring-inset ring-gray-300 dark:ring-gray-400 hover:outline outline-1 placeholder:text-gray-400 rounded-md dark:text-gray-400">Debug</button>
28-
28+
</div>
29+
<div id="news_feed" class="news-feed" style="-webkit-app-region: no-drag; margin-bottom:1rem;">
30+
<div class="news-feed-title">Latest WLJS news</div>
31+
<div id="news_feed_items" class="news-feed-items">Loading latest WLJS news…</div>
2932
</div>
3033

3134

Electron/log.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,35 @@ debug.addEventListener('click', () => {
6464

6565

6666
const info = document.getElementById("modal_info");
67+
const newsFeed = document.getElementById("news_feed_items");
6768

69+
const escapeHtml = (text) => {
70+
if (!text) return '';
71+
return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
72+
};
73+
74+
const renderNewsItems = (items) => {
75+
if (!newsFeed) return;
76+
if (!Array.isArray(items) || items.length === 0) {
77+
newsFeed.innerHTML = '<div style="font-size: 0.75rem; color: #94a3b8;">No WLJS news available.</div>';
78+
return;
79+
}
80+
81+
newsFeed.innerHTML = items.map((item) => {
82+
const title = escapeHtml(item.title || 'Untitled');
83+
const summary = escapeHtml(item.summary || '');
84+
const dateText = escapeHtml(item.dateText || '');
85+
const source = escapeHtml(item.source || 'News');
86+
const url = item.url ? escapeHtml(item.url) : 'https://wljs.io';
87+
88+
return `
89+
<a href="${url}" target="_blank" rel="noreferrer" class="news-item">
90+
<div class="news-source">${source}</div>
91+
<div class="news-title">${title}</div>
92+
<div class="news-meta">${dateText}${summary ? ' · ' + summary : ''}</div>
93+
</a>`;
94+
}).join('');
95+
};
6896

6997
window.electronAPI.updateInfo((event, info) => {
7098
document.getElementById("modal_info_state").innerText = info;
@@ -74,6 +102,10 @@ window.electronAPI.updateVersion((event, info) => {
74102
document.getElementById("modal_info_version").innerText = info;
75103
})
76104

105+
window.electronAPI.handleNews((event, items) => {
106+
renderNewsItems(items);
107+
});
108+
77109
window.electronAPI.addPromt((event, id, title) => {
78110
//well. implement it in a way you like, this is just a simple form
79111
const modal = document.getElementById('modal_dialog');

Electron/log_padded.html

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,13 @@
2525

2626
<div class="mt-2 w-full flex flex-row text-sm text-gray-500 dark:text-gray-300">
2727
<button id="debug_button" style="-webkit-app-region: no-drag;" class="inline-flex bg-gray-100/50 dark:bg-gray-600/50 text-xs items-center px-4 outline-gray-400 outline-offset-2 ring-1 text-gray-800 ring-inset ring-gray-300 dark:ring-gray-400 hover:outline outline-1 placeholder:text-gray-400 rounded-md dark:text-gray-400">Debug</button>
28-
29-
30-
28+
</div>
29+
<div id="news_feed" class="news-feed" style="-webkit-app-region: no-drag; margin-bottom:1rem;">
30+
<div class="news-feed-title">Latest WLJS news</div>
31+
<div id="news_feed_items" class="news-feed-items">Loading latest WLJS news…</div>
3132
</div>
3233

3334

34-
<div class="w-full mt-auto flex flex-row text-sm text-gray-500 dark:text-gray-300"><div id="modal_info_state">Setting everything up...</div><div class="relative flex ml-auto mt-1 h-3 w-3">
35-
3635
<span class="relative inline-flex rounded-full h-3 w-3"></span>
3736
</div></div>
3837

Electron/main.js

Lines changed: 108 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const {powerMonitor } = require('electron')
2323
const { net } = require('electron')
2424
const fs = require('fs');
2525
const fse = require('fs-extra');
26+
const https = require('https');
2627

2728
const { powerSaveBlocker } = require('electron')
2829

@@ -1294,6 +1295,29 @@ const windows = {
12941295
this.win.webContents.send('version', data);
12951296
},
12961297

1298+
news (items) {
1299+
if (!this.readyQ || !this.aliveQ) return;
1300+
this.win.webContents.send('news-items', items);
1301+
},
1302+
1303+
async fetchNews() {
1304+
try {
1305+
const items = await getLatestNews();
1306+
this.news(items);
1307+
console.log(items);
1308+
} catch (error) {
1309+
console.error('Failed to load WLJS news:', error);
1310+
this.news([{
1311+
source: 'WLJS news',
1312+
title: 'Unable to load latest news',
1313+
url: 'https://wljs.io',
1314+
summary: error.message,
1315+
date: new Date(),
1316+
dateText: ''
1317+
}]);
1318+
}
1319+
},
1320+
12971321
construct(cbk = (...any) => {}) {
12981322
let win;
12991323

@@ -1304,7 +1328,7 @@ const windows = {
13041328

13051329
titleBarStyle: 'hiddenInset',
13061330
width: 600,
1307-
height: 400,
1331+
height: 660,
13081332
resizable: false,
13091333
title: 'Launcher',
13101334
contextMenu: true,
@@ -1332,7 +1356,7 @@ const windows = {
13321356
},
13331357
autoHideMenuBar: true,
13341358
width: 600,
1335-
height: 400,
1359+
height: 660,
13361360
resizable: false,
13371361
title: 'Launcher',
13381362
maximizable: false,
@@ -1361,7 +1385,7 @@ const windows = {
13611385
},
13621386
autoHideMenuBar: true,
13631387
width: 600,
1364-
height: 400,
1388+
height: 660,
13651389
resizable: false,
13661390
title: 'Launcher',
13671391
maximizable: false,
@@ -1428,6 +1452,7 @@ const windows = {
14281452
win.once('ready-to-show', () => {
14291453
self.readyQ = true;
14301454
cbk(win);
1455+
self.fetchNews();
14311456
});
14321457

14331458
win.on('close', () => {
@@ -1494,6 +1519,86 @@ function ensureDirectoryExistence(filePath) {
14941519
fs.mkdirSync(dirname);
14951520
}
14961521

1522+
function fetchUrlText(url) {
1523+
return new Promise((resolve, reject) => {
1524+
const request = https.get(url, (res) => {
1525+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
1526+
return resolve(fetchUrlText(new URL(res.headers.location, url).href));
1527+
}
1528+
if (res.statusCode < 200 || res.statusCode >= 300) {
1529+
return reject(new Error(`HTTP ${res.statusCode} fetching ${url}`));
1530+
}
1531+
let body = '';
1532+
res.setEncoding('utf8');
1533+
res.on('data', (chunk) => body += chunk);
1534+
res.on('end', () => resolve(body));
1535+
});
1536+
request.on('error', reject);
1537+
});
1538+
}
1539+
1540+
function stripHtml(html) {
1541+
return html.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
1542+
}
1543+
1544+
function parseNewsItems(html, source, pathPrefix) {
1545+
const regex = new RegExp(`<a[^>]+href="/${pathPrefix}/[^"]+"[^>]*>[\\s\\S]*?</a>`, 'gi');
1546+
const items = [];
1547+
let match;
1548+
let matchCount = 0;
1549+
1550+
while ((match = regex.exec(html))) {
1551+
matchCount++;
1552+
if (matchCount > 50) break; // safety limit
1553+
1554+
const block = match[0];
1555+
const hrefMatch = block.match(/href="(\/[^\"]+)"/);
1556+
const titleMatch = block.match(/<h2[^>]*>([\s\S]*?)<\/h2>/i);
1557+
const summaryMatch = block.match(/<p[^>]*>([\s\S]*?)<\/p>/i);
1558+
const dateMatch = block.match(/([A-Z][a-z]{2,8}\s+\d{1,2},\s+\d{4})/);
1559+
1560+
const url = hrefMatch ? `https://wljs.io${hrefMatch[1]}` : 'https://wljs.io';
1561+
const title = titleMatch ? stripHtml(titleMatch[1]) : url;
1562+
const summary = summaryMatch ? stripHtml(summaryMatch[1]) : '';
1563+
const dateText = dateMatch ? dateMatch[1] : '';
1564+
const date = dateText ? new Date(dateText) : new Date(0);
1565+
1566+
if (title && title !== url && dateText) {
1567+
items.push({ source, title, url, summary, date, dateText });
1568+
}
1569+
}
1570+
1571+
console.log(`Parsed ${matchCount} matches for ${source}, extracted ${items.length} items`);
1572+
return items;
1573+
}
1574+
1575+
async function getLatestNews() {
1576+
try {
1577+
console.log('Fetching WLJS blog and releases...');
1578+
const [blogHtml, releasesHtml] = await Promise.all([
1579+
fetchUrlText('https://wljs.io/blog'),
1580+
fetchUrlText('https://wljs.io/releases')
1581+
]);
1582+
1583+
console.log(`Blog HTML length: ${blogHtml.length}, Releases HTML length: ${releasesHtml.length}`);
1584+
1585+
const items = [
1586+
...parseNewsItems(blogHtml, 'Blog', 'blog'),
1587+
...parseNewsItems(releasesHtml, 'Releases', 'releases')
1588+
];
1589+
1590+
console.log(`Total items collected: ${items.length}`);
1591+
items.sort((a, b) => b.date - a.date);
1592+
const result = items.slice(0, 8);
1593+
console.log(`Final result: ${result.length} items`);
1594+
result.forEach(item => console.log(` - ${item.source}: ${item.title} (${item.dateText})`));
1595+
return result;
1596+
} catch (error) {
1597+
console.error('Error in getLatestNews:', error);
1598+
throw error;
1599+
}
1600+
}
1601+
14971602
const dumpLogs = (cbk) => {
14981603
const p = path.join(appDataFolder, 'Debug', 'System.log');
14991604
ensureDirectoryExistence(p);

0 commit comments

Comments
 (0)