@buildinternet/uploads 0.52.0 → 0.52.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/dist/commands.js
CHANGED
|
@@ -655,7 +655,7 @@ export async function syncAttachmentsComment(client, target, run, workspace, opt
|
|
|
655
655
|
target: { kind: target.kind, num: target.num },
|
|
656
656
|
});
|
|
657
657
|
const prefixes = ghListPrefixes(ghKeyPrefix(target), ghPrefix, (id) => ghPrivateKeyPrefix(id, target));
|
|
658
|
-
const items = await ghMergedList(prefixes, undefined, async (prefix) => (await client.listAll({ prefix, metadata: true })).map(({ key, url, embedUrl, pageUrl, metadata }) => {
|
|
658
|
+
const items = await ghMergedList(prefixes, undefined, async (prefix) => (await client.listAll({ prefix, metadata: true })).map(({ key, url, embedUrl, pageUrl, size, metadata }) => {
|
|
659
659
|
// The list endpoint returns every metadata key; the comment
|
|
660
660
|
// renders only these two. Narrowing here keeps both render paths
|
|
661
661
|
// byte-identical.
|
|
@@ -673,6 +673,7 @@ export async function syncAttachmentsComment(client, target, run, workspace, opt
|
|
|
673
673
|
url,
|
|
674
674
|
embedUrl,
|
|
675
675
|
pageUrl,
|
|
676
|
+
...(size != null ? { size } : {}),
|
|
676
677
|
...(path || state
|
|
677
678
|
? { meta: { ...(path ? { path } : {}), ...(state ? { state } : {}) } }
|
|
678
679
|
: {}),
|
|
@@ -120,6 +120,10 @@ export interface AttachmentItem {
|
|
|
120
120
|
width?: number;
|
|
121
121
|
height?: number;
|
|
122
122
|
};
|
|
123
|
+
/** Object size in bytes, when known — drives the non-media file table's Size column. */
|
|
124
|
+
size?: number;
|
|
125
|
+
/** Stored content type, when known — preferred over name-based inference for file classification and the table's Type column. */
|
|
126
|
+
contentType?: string;
|
|
123
127
|
}
|
|
124
128
|
/** A public gallery linked to the PR or issue whose managed comment is syncing. */
|
|
125
129
|
export interface GalleryCommentItem {
|
|
@@ -285,6 +285,47 @@ function formatMetaCaption(meta, options, mode) {
|
|
|
285
285
|
})
|
|
286
286
|
.join(" · ");
|
|
287
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Decimal-SI byte size for the non-media file table's Size column: whole
|
|
290
|
+
* bytes below 1000, one decimal place at KB/MB/GB and above. `"—"` when the
|
|
291
|
+
* size is unknown.
|
|
292
|
+
*/
|
|
293
|
+
function formatBytes(bytes) {
|
|
294
|
+
if (bytes == null || !Number.isFinite(bytes) || bytes < 0)
|
|
295
|
+
return "—";
|
|
296
|
+
if (bytes < 1000)
|
|
297
|
+
return `${Math.round(bytes)} B`;
|
|
298
|
+
const units = [
|
|
299
|
+
[1e9, "GB"],
|
|
300
|
+
[1e6, "MB"],
|
|
301
|
+
[1e3, "KB"],
|
|
302
|
+
];
|
|
303
|
+
for (const [threshold, label] of units) {
|
|
304
|
+
if (bytes >= threshold)
|
|
305
|
+
return `${(bytes / threshold).toFixed(1)} ${label}`;
|
|
306
|
+
}
|
|
307
|
+
return `${Math.round(bytes)} B`;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Type label for the non-media file table: uppercase filename extension
|
|
311
|
+
* first, then the content type's subtype, then a bare "FILE" fallback.
|
|
312
|
+
*/
|
|
313
|
+
function fileTypeLabel(name, contentType) {
|
|
314
|
+
const dot = name.lastIndexOf(".");
|
|
315
|
+
if (dot !== -1 && dot < name.length - 1)
|
|
316
|
+
return name.slice(dot + 1).toUpperCase();
|
|
317
|
+
if (contentType) {
|
|
318
|
+
const slash = contentType.indexOf("/");
|
|
319
|
+
if (slash !== -1 && slash < contentType.length - 1) {
|
|
320
|
+
return contentType.slice(slash + 1).toUpperCase();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return "FILE";
|
|
324
|
+
}
|
|
325
|
+
/** Escape `|` so a filename can't break out of a markdown table cell. */
|
|
326
|
+
function escapeTableCell(s) {
|
|
327
|
+
return s.replace(/\|/g, "\\|");
|
|
328
|
+
}
|
|
288
329
|
/** Resolved pixel width for an image site, or `null` meaning "omit the width
|
|
289
330
|
* attribute". `"auto"` defers to the caller's per-item heuristic (`autoPx`);
|
|
290
331
|
* `"full"` always omits; a number always wins. */
|
|
@@ -485,6 +526,10 @@ export function attachmentsCommentBody(items, galleries = [], marker = ATTACHMEN
|
|
|
485
526
|
const consumedByPair = new Set();
|
|
486
527
|
let inlinedImages = 0;
|
|
487
528
|
const overflowImages = [];
|
|
529
|
+
// Non-media attachments (PDFs, archives, text/data files) never inline and
|
|
530
|
+
// never overflow into the <details> link list — they render as one table
|
|
531
|
+
// after the image/video section instead (issue #946).
|
|
532
|
+
const fileItems = [];
|
|
488
533
|
for (let idx = 0; idx < sorted.length; idx++) {
|
|
489
534
|
if (consumedByPair.has(idx))
|
|
490
535
|
continue;
|
|
@@ -510,8 +555,21 @@ export function attachmentsCommentBody(items, galleries = [], marker = ATTACHMEN
|
|
|
510
555
|
const stable = item.url;
|
|
511
556
|
const src = item.embedUrl ?? item.url;
|
|
512
557
|
const link = item.pageUrl ?? stable; // click-through: file page when known, else raw
|
|
513
|
-
|
|
514
|
-
|
|
558
|
+
// "application/octet-stream" is the server's generic fallback for an
|
|
559
|
+
// object stored without an explicit content type — not a real signal —
|
|
560
|
+
// so it defers to the filename the same as an absent `contentType`.
|
|
561
|
+
const effectiveType = item.contentType && item.contentType !== "application/octet-stream"
|
|
562
|
+
? item.contentType
|
|
563
|
+
: inferContentType(name);
|
|
564
|
+
const isImage = Boolean(src) && effectiveType.startsWith("image/");
|
|
565
|
+
const isPosterVideo = Boolean(item.posterUrl) && effectiveType.startsWith("video/");
|
|
566
|
+
if (!effectiveType.startsWith("image/") && !effectiveType.startsWith("video/")) {
|
|
567
|
+
// Neither an image nor a video by content type — a non-media
|
|
568
|
+
// attachment goes into the file table, never the bullet list or
|
|
569
|
+
// overflow details.
|
|
570
|
+
fileItems.push(item);
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
515
573
|
const inlines = isImage || isPosterVideo;
|
|
516
574
|
if (inlines && inlinedImages >= options.maxInlineImages) {
|
|
517
575
|
// Cap hit — defer to the collapsed overflow list below rather than
|
|
@@ -604,6 +662,23 @@ export function attachmentsCommentBody(items, galleries = [], marker = ATTACHMEN
|
|
|
604
662
|
lines.push(`- ${name}${cap ? ` · ${cap}` : ""}`);
|
|
605
663
|
}
|
|
606
664
|
}
|
|
665
|
+
if (fileItems.length > 0) {
|
|
666
|
+
lines.push("| File | Type | Size |", "| --- | --- | --- |");
|
|
667
|
+
for (const item of fileItems) {
|
|
668
|
+
const name = item.key.slice(item.key.lastIndexOf("/") + 1);
|
|
669
|
+
const escapedName = escapeTableCell(name);
|
|
670
|
+
let fileCell = item.url ? `[${escapedName}](${item.url})` : escapedName;
|
|
671
|
+
if (item.pageUrl)
|
|
672
|
+
fileCell += ` · [page](${item.pageUrl})`;
|
|
673
|
+
const cap = formatMetaCaption(item.meta, options, "markdown");
|
|
674
|
+
if (cap)
|
|
675
|
+
fileCell += ` · ${cap}`;
|
|
676
|
+
const typeLabel = fileTypeLabel(name, item.contentType);
|
|
677
|
+
const sizeLabel = formatBytes(item.size);
|
|
678
|
+
lines.push(`| ${fileCell} | ${typeLabel} | ${sizeLabel} |`);
|
|
679
|
+
}
|
|
680
|
+
lines.push("");
|
|
681
|
+
}
|
|
607
682
|
if (overflowImages.length > 0) {
|
|
608
683
|
const n = overflowImages.length;
|
|
609
684
|
lines.push(`<details><summary>${n} more attachment${n === 1 ? "" : "s"}</summary>`, "");
|