pmtiles-swarm 0.46.0 โ 0.47.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/package.json +1 -1
- package/src/web/index.html +80 -59
- package/src/web/public.html +10 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,31 @@
|
|
|
7
7
|
### ๐ Bug fixes
|
|
8
8
|
- _...Add new stuff here..._
|
|
9
9
|
|
|
10
|
+
## 0.47.1
|
|
11
|
+
### โจ Features and improvements
|
|
12
|
+
|
|
13
|
+
### ๐ Bug fixes
|
|
14
|
+
- **The magnet button copies a magnet, not a link to one.** `/latest/<category>/magnet` answers a
|
|
15
|
+
magnet URI as `text/plain`, and both pages were copying that endpoint's own address โ so what
|
|
16
|
+
landed on the clipboard was an `http://` URL, which a torrent client cannot open. The button now
|
|
17
|
+
fetches the endpoint and copies what it answers. Every other endpoint is still copied by address,
|
|
18
|
+
because for those the address is the thing.
|
|
19
|
+
|
|
20
|
+
## 0.47.0
|
|
21
|
+
### โจ Features and improvements
|
|
22
|
+
- **The console's Categories tab now matches the public page.** They were two views of one thing that
|
|
23
|
+
had drifted into two shapes: a label/value table with Copy and Open on every row here, links and a
|
|
24
|
+
printed style URL there, different labels for the same endpoints, and a preview link on one and not
|
|
25
|
+
the other.
|
|
26
|
+
|
|
27
|
+
Both now offer each endpoint the way it is used โ `.torrent` downloads, preview opens, and
|
|
28
|
+
TileJSON, magnet, RSS and the style URL copy. The console keeps `RSS, newest only`, which is its
|
|
29
|
+
own, and gains the preview link it was missing. The style URL is no longer printed on either page:
|
|
30
|
+
here it was truncated to 96 characters, which is long enough to fill the row and too short to be
|
|
31
|
+
the thing anybody wanted.
|
|
32
|
+
|
|
33
|
+
### ๐ Bug fixes
|
|
34
|
+
|
|
10
35
|
## 0.46.0
|
|
11
36
|
### โจ Features and improvements
|
|
12
37
|
- **Each category endpoint on the public page is now offered the way it is used.** A `.torrent` is a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmtiles-swarm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.47.1",
|
|
4
4
|
"description": "BitTorrent distribution for PMTiles map archives: create torrents, watch folders, publish and subscribe to RSS feeds, and seed through qBittorrent or an embedded client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
package/src/web/index.html
CHANGED
|
@@ -224,6 +224,23 @@
|
|
|
224
224
|
}
|
|
225
225
|
.tag.on { background: var(--accent); color: #fff; border-color: var(--accent); }
|
|
226
226
|
#add-error { color: var(--bad); min-height: 1.2rem; }
|
|
227
|
+
/* One row of endpoints per category, matching the public page. */
|
|
228
|
+
#category-list .links {
|
|
229
|
+
display: flex;
|
|
230
|
+
flex-wrap: wrap;
|
|
231
|
+
gap: 0.4rem 0.9rem;
|
|
232
|
+
align-items: center;
|
|
233
|
+
}
|
|
234
|
+
/* A copy button reads as a link's sibling, not as a form control. */
|
|
235
|
+
button.copy {
|
|
236
|
+
border: 1px solid var(--line);
|
|
237
|
+
border-radius: 999px;
|
|
238
|
+
background: none;
|
|
239
|
+
color: var(--muted);
|
|
240
|
+
font-size: 0.74rem;
|
|
241
|
+
padding: 0 0.45rem;
|
|
242
|
+
}
|
|
243
|
+
button.copy:hover { color: var(--fg); border-color: var(--fg); }
|
|
227
244
|
/* Category endpoints. `th, td` is nowrap for the archive table, where a
|
|
228
245
|
wrapped number is worse than a wide one โ but here the cell holds a
|
|
229
246
|
style URL and a paragraph explaining it, so nowrap pushed the table
|
|
@@ -3477,20 +3494,40 @@ Every piece is hashed against the ` +
|
|
|
3477
3494
|
list.innerHTML = categories
|
|
3478
3495
|
.map((entry, index) => {
|
|
3479
3496
|
const newest = entry.newest;
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3497
|
+
// The same shape as the public page, and for the same reason: what
|
|
3498
|
+
// an endpoint is for decides how it is offered. A `.torrent` is a
|
|
3499
|
+
// file, so it downloads; a preview is a page, so it opens. The
|
|
3500
|
+
// rest are addresses that belong somewhere else โ a style, a
|
|
3501
|
+
// torrent client, a feed reader โ and following one from here
|
|
3502
|
+
// achieves nothing, so they copy.
|
|
3503
|
+
//
|
|
3504
|
+
// The style URL is not printed. It is a TileJSON URL carrying a
|
|
3505
|
+
// `.torrent` URL and a percent-encoded magnet in its fragment,
|
|
3506
|
+
// several hundred characters of it, and it was truncated to 96
|
|
3507
|
+
// here โ long enough to fill the row and too short to be the
|
|
3508
|
+
// thing anybody wanted.
|
|
3509
|
+
const ends = entry.endpoints;
|
|
3510
|
+
const link = (url, text, download) =>
|
|
3511
|
+
url
|
|
3512
|
+
? `<a href="${escapeHtml(url)}"${download ? ' download' : ' target="_blank" rel="noreferrer"'}>${text}</a>`
|
|
3513
|
+
: '';
|
|
3514
|
+
// `fetched` for an endpoint whose *answer* is the thing worth
|
|
3515
|
+
// having rather than its address. /magnet serves a magnet URI as
|
|
3516
|
+
// text/plain, and copying the URL of that handed somebody a link
|
|
3517
|
+
// to a magnet instead of a magnet โ which a torrent client cannot
|
|
3518
|
+
// open.
|
|
3519
|
+
const copyable = (url, text, fetched) =>
|
|
3520
|
+
url
|
|
3521
|
+
? `<button class="copy" data-copy-url="${escapeHtml(url)}"${
|
|
3522
|
+
fetched ? ' data-copy-fetch="1"' : ''
|
|
3523
|
+
} title="${escapeHtml(url)}">${text}</button>`
|
|
3524
|
+
: '';
|
|
3488
3525
|
|
|
3489
3526
|
return `
|
|
3490
3527
|
<div class="panel" style="margin-bottom:0.8rem">
|
|
3491
3528
|
<h2 style="margin-top:0">
|
|
3492
3529
|
${escapeHtml(entry.category)}
|
|
3493
|
-
<span class="sub">ยท ${entry.archives}
|
|
3530
|
+
<span class="sub">ยท ${entry.archives} build${entry.archives === 1 ? '' : 's'}</span>
|
|
3494
3531
|
</h2>
|
|
3495
3532
|
<div class="sub" style="margin-bottom:0.6rem">
|
|
3496
3533
|
${
|
|
@@ -3499,60 +3536,44 @@ Every piece is hashed against the ` +
|
|
|
3499
3536
|
: 'nothing published under this tag yet'
|
|
3500
3537
|
}
|
|
3501
3538
|
</div>
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
url.includes('btpk')
|
|
3523
|
-
? ', and both follow the category rather than this build'
|
|
3524
|
-
: ''
|
|
3525
|
-
}</div>`
|
|
3526
|
-
: ''
|
|
3527
|
-
}`
|
|
3528
|
-
: '<span class="sub">not a PMTiles archive, so there is no tile endpoint</span>'
|
|
3529
|
-
}</td>
|
|
3530
|
-
<td style="width:11rem">${
|
|
3531
|
-
url
|
|
3532
|
-
? `<button data-copy="${index}-${row}">Copy</button>
|
|
3533
|
-
<a href="${escapeHtml(url)}" target="_blank" rel="noreferrer"><button type="button">Open</button></a>`
|
|
3534
|
-
: ''
|
|
3535
|
-
}</td>
|
|
3536
|
-
</tr>`,
|
|
3537
|
-
)
|
|
3538
|
-
.join('')}
|
|
3539
|
-
</tbody>
|
|
3540
|
-
</table>
|
|
3539
|
+
${
|
|
3540
|
+
ends.tileJson
|
|
3541
|
+
? `<div class="links">
|
|
3542
|
+
${copyable(ends.tileJson, 'TileJSON')}
|
|
3543
|
+
${link(ends.preview, 'preview')}
|
|
3544
|
+
${link(ends.torrent, '.torrent', true)}
|
|
3545
|
+
${copyable(ends.magnet, 'magnet', true)}
|
|
3546
|
+
${copyable(ends.feed, 'RSS')}
|
|
3547
|
+
${copyable(ends.latestFeed, 'RSS, newest only')}
|
|
3548
|
+
${copyable(ends.styleUrl, 'style URL')}
|
|
3549
|
+
</div>
|
|
3550
|
+
<div class="sub" style="margin-top:0.5rem">
|
|
3551
|
+
The style URL is the TileJSON with the .torrent URL and
|
|
3552
|
+
the magnet in its fragment, which is never sent to the
|
|
3553
|
+
server โ an ordinary client fetches the TileJSON and
|
|
3554
|
+
ignores them, a swarm-aware one reads them first, and
|
|
3555
|
+
both follow the category rather than this build.
|
|
3556
|
+
</div>`
|
|
3557
|
+
: '<div class="sub">not a PMTiles archive, so there is no tile endpoint</div>'
|
|
3558
|
+
}
|
|
3541
3559
|
</div>`;
|
|
3542
3560
|
})
|
|
3543
3561
|
.join('');
|
|
3544
3562
|
|
|
3545
|
-
for (const button of list.querySelectorAll('[data-copy]')) {
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3563
|
+
for (const button of list.querySelectorAll('[data-copy-url]')) {
|
|
3564
|
+
button.onclick = async () => {
|
|
3565
|
+
const url = button.dataset.copyUrl;
|
|
3566
|
+
if (!button.dataset.copyFetch) return copy(url, 'URL');
|
|
3567
|
+
try {
|
|
3568
|
+
// A plain fetch, not `api`: that one parses JSON, and this
|
|
3569
|
+
// endpoint answers text/plain.
|
|
3570
|
+
const response = await fetch(url);
|
|
3571
|
+
if (!response.ok) throw new Error(response.statusText);
|
|
3572
|
+
copy((await response.text()).trim(), 'magnet');
|
|
3573
|
+
} catch (error) {
|
|
3574
|
+
toast(error.message);
|
|
3575
|
+
}
|
|
3576
|
+
};
|
|
3556
3577
|
}
|
|
3557
3578
|
}
|
|
3558
3579
|
|
package/src/web/public.html
CHANGED
|
@@ -408,7 +408,11 @@
|
|
|
408
408
|
// preview is a page to visit. The rest are addresses that belong
|
|
409
409
|
// somewhere else โ a style, a torrent client, a feed reader โ and
|
|
410
410
|
// following one here achieves nothing, so those copy.
|
|
411
|
-
|
|
411
|
+
// `fetched` for an endpoint whose *answer* is the thing worth
|
|
412
|
+
// having rather than its address. /magnet serves a magnet URI as
|
|
413
|
+
// text/plain, and copying the URL of that handed somebody a link to
|
|
414
|
+
// a magnet instead of a magnet โ which a torrent client cannot open.
|
|
415
|
+
const copy = (href, text, fetched) => {
|
|
412
416
|
const button = el('button', 'copy', text);
|
|
413
417
|
button.type = 'button';
|
|
414
418
|
const absolute = new URL(href, location.href).href;
|
|
@@ -418,7 +422,10 @@
|
|
|
418
422
|
button.title = absolute;
|
|
419
423
|
button.onclick = async () => {
|
|
420
424
|
try {
|
|
421
|
-
|
|
425
|
+
const value = fetched
|
|
426
|
+
? (await (await fetch(absolute)).text()).trim()
|
|
427
|
+
: absolute;
|
|
428
|
+
await navigator.clipboard.writeText(value);
|
|
422
429
|
button.classList.add('done');
|
|
423
430
|
button.textContent = 'copied';
|
|
424
431
|
setTimeout(() => {
|
|
@@ -444,7 +451,7 @@
|
|
|
444
451
|
a.download = '';
|
|
445
452
|
links.append(a);
|
|
446
453
|
}
|
|
447
|
-
if (ends.magnet) copy(ends.magnet, 'magnet');
|
|
454
|
+
if (ends.magnet) copy(ends.magnet, 'magnet', true);
|
|
448
455
|
if (ends.feed) copy(ends.feed, 'RSS');
|
|
449
456
|
// The one worth copying and the one nobody wants to read: a TileJSON
|
|
450
457
|
// URL carrying the .torrent URL and a percent-encoded magnet in its
|