@mikitasazan/notify 1.12.1 → 1.14.0
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/render.js +106 -28
- package/package.json +1 -1
package/dist/render.js
CHANGED
|
@@ -252,13 +252,18 @@ const groupItem = (it, index, numbered) => {
|
|
|
252
252
|
// part of the heading. Longer than ~400 characters and the quote collapses
|
|
253
253
|
// on its own (`expandable`, Bot API), otherwise a stack trace or a log dump
|
|
254
254
|
// stretches the card across the whole screen.
|
|
255
|
+
// A quote collapses when it would take more than a screenful — by length OR
|
|
256
|
+
// by line count, because five short lines eat as much screen as one long
|
|
257
|
+
// paragraph and the old length-only rule let them through (v2.1).
|
|
255
258
|
const EXPAND_AT = 400;
|
|
259
|
+
const EXPAND_LINES = 5;
|
|
256
260
|
const note = (text) => {
|
|
257
261
|
if (!text) {
|
|
258
262
|
return null;
|
|
259
263
|
}
|
|
260
264
|
const body = esc(text);
|
|
261
|
-
|
|
265
|
+
const long = body.length > EXPAND_AT || body.split('\n').length > EXPAND_LINES;
|
|
266
|
+
return long ? `<blockquote expandable>${body}</blockquote>` : `<blockquote>${body}</blockquote>`;
|
|
262
267
|
};
|
|
263
268
|
/**
|
|
264
269
|
* A quote with a caption. A bare quote reads as a continuation of the
|
|
@@ -349,12 +354,17 @@ const renderGroup = (g) => [
|
|
|
349
354
|
* was the same row the issue card had already lost for the same reason: you
|
|
350
355
|
* could not read what the card was about without reading two lines.
|
|
351
356
|
*/
|
|
352
|
-
const commitRow = (hash,
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
357
|
+
const commitRow = (hash, title) => {
|
|
358
|
+
// The title is the content — what the commit DID — so it is what the row
|
|
359
|
+
// says. The hash is a pointer, and pointers live in the last block with
|
|
360
|
+
// `Log`, `Check` and `Source`; the owner caught the hash still carrying the
|
|
361
|
+
// link from the middle of the card (31.08.2026): "source, это hash", and on
|
|
362
|
+
// a manual deploy that buried link was the card's ONLY way back to its
|
|
363
|
+
// source while the pointer block sat empty.
|
|
364
|
+
//
|
|
365
|
+
// With no title there is nothing to say but the hash, so it stands here as
|
|
366
|
+
// plain text — the link to it is in the pointer block either way.
|
|
367
|
+
return field('Commit', title ? firstLine(title) : hash);
|
|
358
368
|
};
|
|
359
369
|
const bodyQuote = (body) => body ? note(body) : null;
|
|
360
370
|
/**
|
|
@@ -512,7 +522,7 @@ const renderDeploy = (e) => {
|
|
|
512
522
|
// The run URL is gone from this line too — it is the `Source:` row now.
|
|
513
523
|
typeLine(icon, 'Deploy', mechanism(e.workflowName, e.via), undefined, e.status === 'fail' ? 'Fail' : 'OK'),
|
|
514
524
|
...twoBlocks([field('Target', e.target), reason('Reason', e.note), field('Still red', e.stillRed ? `day ${e.stillRed}` : null)], [
|
|
515
|
-
commitRow(e.commit, e.
|
|
525
|
+
commitRow(e.commit, e.commitTitle),
|
|
516
526
|
fieldPerson('Author', e.commitAuthor),
|
|
517
527
|
bodyQuote(e.commitBody)
|
|
518
528
|
])
|
|
@@ -609,7 +619,7 @@ const renderCi = (e) => {
|
|
|
609
619
|
// on a nightly card, the same person everywhere else.
|
|
610
620
|
...twoBlocks([reason('Reason', e.note), field('Still red', e.stillRed ? `day ${e.stillRed}` : null)], [
|
|
611
621
|
fieldTelegram('Actor', e.actor),
|
|
612
|
-
commitRow(e.commit, e.
|
|
622
|
+
commitRow(e.commit, e.commitTitle),
|
|
613
623
|
fieldPerson('Author', e.commitAuthor),
|
|
614
624
|
bodyQuote(e.commitBody)
|
|
615
625
|
])
|
|
@@ -621,7 +631,26 @@ const renderCi = (e) => {
|
|
|
621
631
|
// the thing the card is about could not be read without reading three lines.
|
|
622
632
|
// The action is not repeated in words: the icon carries it, and no two actions
|
|
623
633
|
// of one type share an icon.
|
|
624
|
-
|
|
634
|
+
// The number left line 2 on 31.08.2026, by the same rule that moved the commit
|
|
635
|
+
// hash the same day: `#347` is a pointer, and every pointer lives in the last
|
|
636
|
+
// block. Line 2 keeps the title, which is what the thing IS; the number comes
|
|
637
|
+
// back as the text of the `Source` link, where it names exactly what opens.
|
|
638
|
+
//
|
|
639
|
+
// With no title there is nothing left to say, so line 2 falls back to the bare
|
|
640
|
+
// type word — `typeLine` already does that on an empty name. The number is not
|
|
641
|
+
// put back here: it would then appear twice on a titled card and once on an
|
|
642
|
+
// untitled one, which is the inconsistency this move exists to remove.
|
|
643
|
+
//
|
|
644
|
+
// With NO url the number stays put, because then the `Source` row does not
|
|
645
|
+
// exist and dropping it here would erase the number from everything a person
|
|
646
|
+
// reads — the card would name a task without saying which. Same law as
|
|
647
|
+
// `fieldLink`: an identifier must not vanish just because the caller passed no
|
|
648
|
+
// address for it.
|
|
649
|
+
const named = (number, title, url) => {
|
|
650
|
+
if (!title)
|
|
651
|
+
return url ? '' : `#${number}`;
|
|
652
|
+
return url ? title : `#${number} ${title}`;
|
|
653
|
+
};
|
|
625
654
|
// The people come BEFORE the text, and the text comes only when it is the
|
|
626
655
|
// news. An `assigned` card carries one new fact — who took it — and it used to
|
|
627
656
|
// sit dead last, under the issue's entire description: the owner read a card
|
|
@@ -636,32 +665,35 @@ const named = (number, title) => title ? `#${number} ${title}` : `#${number}`;
|
|
|
636
665
|
// REVIEWER'S OWN comment there, which is new text he has not seen. "Verdict:
|
|
637
666
|
// changes_requested, then nothing" was the owner's complaint: a verdict with
|
|
638
667
|
// no comment attached said less than the review itself did.
|
|
668
|
+
// Same rule as the issue card since 31.08.2026: the body prints on every
|
|
669
|
+
// action, collapsed when long. `VERDICT` stays because it changes what the
|
|
670
|
+
// body MEANS — on approved/changes_requested the sender puts the reviewer's
|
|
671
|
+
// own comment there, not the PR description — and that is what the caption
|
|
672
|
+
// has to say.
|
|
639
673
|
const VERDICT = new Set(['approved', 'changes_requested']);
|
|
640
|
-
const
|
|
674
|
+
const prBody = (action, body) => VERDICT.has(action) ? quoted('Review', body) : bodyQuote(body);
|
|
641
675
|
// The body is what the title stands for — it sits directly under the name,
|
|
642
676
|
// with nothing between them. The people come after, consolidated in one
|
|
643
677
|
// place, never splitting the title from what it names: the owner on the
|
|
644
678
|
// old order, title then Author then Assignee then finally the body — "why
|
|
645
679
|
// does the assignee cut apart what should be inseparable?"
|
|
646
680
|
const renderPr = (e) => join([
|
|
647
|
-
typeLine(iconFor(e), 'PR', named(e.number, e.title)),
|
|
648
|
-
|
|
649
|
-
|
|
681
|
+
typeLine(iconFor(e), 'PR', named(e.number, e.title, e.url)),
|
|
682
|
+
prBody(e.action, e.body),
|
|
683
|
+
e.body ? '' : null,
|
|
650
684
|
fieldPerson('Author', e.author),
|
|
651
685
|
fieldPerson('Reviewer', e.reviewer)
|
|
652
686
|
]);
|
|
653
|
-
// The
|
|
654
|
-
//
|
|
655
|
-
//
|
|
656
|
-
//
|
|
657
|
-
//
|
|
658
|
-
//
|
|
659
|
-
// short assigned card in the v2.1 mockups. On assigned/closed the card is
|
|
660
|
-
// the one new fact plus the `Source:` link to the full text.
|
|
687
|
+
// The body prints on EVERY action, as a quote that collapses when it is
|
|
688
|
+
// long. The owner settled this on 31.08.2026, after the short assigned card
|
|
689
|
+
// shipped: "если это ишью, то видеть тело ишью… везде, где это может быть
|
|
690
|
+
// полезно, не переходя по ссылке на источник." A collapsed quote costs four
|
|
691
|
+
// lines, which is what the earlier "he already saw it" reasoning was trying
|
|
692
|
+
// to save — the expandable quote buys the context back without the cost.
|
|
661
693
|
const renderIssue = (e) => join([
|
|
662
|
-
typeLine(iconFor(e), 'Issue', named(e.number, e.title)),
|
|
663
|
-
|
|
664
|
-
e.
|
|
694
|
+
typeLine(iconFor(e), 'Issue', named(e.number, e.title, e.url)),
|
|
695
|
+
bodyQuote(e.body),
|
|
696
|
+
e.body ? '' : null,
|
|
665
697
|
fieldPerson('Author', e.author),
|
|
666
698
|
fieldPerson('Assignee', e.assignee)
|
|
667
699
|
]);
|
|
@@ -847,13 +879,44 @@ const sourceUrl = (e) => {
|
|
|
847
879
|
const url = 'url' in e ? e.url : undefined;
|
|
848
880
|
return wf ?? url;
|
|
849
881
|
};
|
|
882
|
+
const link = (url, text) => `<a href="${esc(firstLine(url))}">${esc(text)}</a>`;
|
|
883
|
+
/**
|
|
884
|
+
* Everything the card can be traced back to, as one `Source:` row.
|
|
885
|
+
*
|
|
886
|
+
* A card can have more than one honest source — a deploy through Actions has
|
|
887
|
+
* both a run and the commit that triggered it — and one label with two links
|
|
888
|
+
* beside each other reads better than two rows both called `Source`. They are
|
|
889
|
+
* ordered widest first: the run contains the commit, not the other way round.
|
|
890
|
+
*/
|
|
891
|
+
const sourceLinks = (e) => {
|
|
892
|
+
const out = [];
|
|
893
|
+
const run = sourceUrl(e);
|
|
894
|
+
const commitUrl = 'commitUrl' in e ? e.commitUrl : undefined;
|
|
895
|
+
const commit = 'commit' in e ? e.commit : undefined;
|
|
896
|
+
// A pull request and an issue are named by their number and by NOTHING else.
|
|
897
|
+
// `pull request #118` says the type a second time — line 2 already opens with
|
|
898
|
+
// `PR:` — and the owner cut it the hour it shipped: "сверху написано PR,
|
|
899
|
+
// зачем здесь ещё писать PR? Здесь можно просто номер и всё."
|
|
900
|
+
//
|
|
901
|
+
// Only these two types have a number, and only for them is the type word
|
|
902
|
+
// redundant: `workflow run` and `commit 9b1fc68` name things line 2 does NOT,
|
|
903
|
+
// so they keep their nouns.
|
|
904
|
+
const numbered = 'number' in e && typeof e.number === 'number' ? `#${e.number}` : null;
|
|
905
|
+
if (run)
|
|
906
|
+
out.push(link(run, numbered ?? SOURCE_NAME[e.type] ?? 'source'));
|
|
907
|
+
// Without a hash there is no text to put on the link but the word itself,
|
|
908
|
+
// and `commit` alone next to `workflow run` says nothing a reader can use.
|
|
909
|
+
if (commitUrl && commit)
|
|
910
|
+
out.push(link(commitUrl, `commit ${firstLine(commit)}`));
|
|
911
|
+
return out;
|
|
912
|
+
};
|
|
850
913
|
const pointerBlock = (e) => {
|
|
851
|
-
const
|
|
914
|
+
const sources = sourceLinks(e);
|
|
852
915
|
const logs = 'logs' in e ? e.logs : undefined;
|
|
853
916
|
const rows = [
|
|
854
917
|
fieldCode('Log', logs),
|
|
855
918
|
fieldCode('Check', e.check),
|
|
856
|
-
|
|
919
|
+
sources.length > 0 ? `<b>Source:</b> ${sources.join(' · ')}` : null
|
|
857
920
|
].filter((r) => r !== null);
|
|
858
921
|
return rows.length > 0 ? `\n\n${rows.join('\n')}` : '';
|
|
859
922
|
};
|
|
@@ -868,8 +931,23 @@ const cutMarker = (e) => {
|
|
|
868
931
|
if (e.path) {
|
|
869
932
|
return '⋯ cut, full text attached';
|
|
870
933
|
}
|
|
934
|
+
// A bare `⋯ cut` announces a loss and then says nothing about it. The owner
|
|
935
|
+
// met one on a live PR card (31.08.2026) — a description long enough to be
|
|
936
|
+
// clamped — and asked what the three dots even referred to. The card had a
|
|
937
|
+
// `Source` link the whole time: the rest was one tap away and the marker
|
|
938
|
+
// never said so.
|
|
939
|
+
//
|
|
940
|
+
// So the marker NAMES the row that holds the rest, and never repeats its
|
|
941
|
+
// value: it used to print the log path in full, and the pointer block then
|
|
942
|
+
// printed the same path again on the very next line.
|
|
943
|
+
//
|
|
944
|
+
// `below` is literal, not a figure of speech: the marker closes the clamped
|
|
945
|
+
// body and the pointer block is appended after it.
|
|
871
946
|
const logs = 'logs' in e ? e.logs : undefined;
|
|
872
|
-
|
|
947
|
+
if (logs) {
|
|
948
|
+
return '⋯ cut, full text at Log below';
|
|
949
|
+
}
|
|
950
|
+
return sourceLinks(e).length > 0 ? '⋯ cut, full text at Source below' : '⋯ cut';
|
|
873
951
|
};
|
|
874
952
|
/**
|
|
875
953
|
* Renders an event into finished HTML text, cut to Telegram's limit.
|
package/package.json
CHANGED