@stage5/lumine 0.3.62 → 0.3.63

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.
@@ -23,6 +23,16 @@ function indent(text, prefix) {
23
23
  .join("\n");
24
24
  }
25
25
 
26
+ // A member's platinum cover photo goes through the same report queue
27
+ // (subjectType "profile_cover"); its snapshot is the cover as one line.
28
+ function isCoverReport(report) {
29
+ return report?.subjectType === "profile_cover";
30
+ }
31
+
32
+ function subjectNoun(report) {
33
+ return isCoverReport(report) ? "cover photo" : "message";
34
+ }
35
+
26
36
  function formatMessageLine(message, marker = " ") {
27
37
  const author = message.username || (message.userId ? `user #${message.userId}` : "unknown");
28
38
  const content = String(message.content ?? "").trim() || "(no text)";
@@ -32,6 +42,7 @@ function formatMessageLine(message, marker = " ") {
32
42
  }
33
43
 
34
44
  function where(report) {
45
+ if (isCoverReport(report)) return "their profile cover photo (only Mikey can take it down)";
35
46
  const parts = [];
36
47
  if (report.channelKind) parts.push(report.channelKind);
37
48
  if (report.channelId) parts.push(`channel ${report.channelId}`);
@@ -45,7 +56,7 @@ export function formatChatReportLine(report) {
45
56
  .replace(/\s+/g, " ")
46
57
  .trim();
47
58
  const short = preview.length > 80 ? `${preview.slice(0, 79)}…` : preview;
48
- return `#${report.id} ${String(report.status || "open").toUpperCase()} · ${report.reasonLabel || report.reason || "no reason"} · ${who(report.reporter)} → ${who(report.reported)} · ${formatTime(report.createdAt)}${Number(report.reportsOfThisMessage) > 1 ? ` · ${report.reportsOfThisMessage} reports of this message` : ""}${short ? `\n "${short}"` : ""}`;
59
+ return `#${report.id} ${String(report.status || "open").toUpperCase()} · ${report.reasonLabel || report.reason || "no reason"} · ${who(report.reporter)} → ${who(report.reported)} · ${formatTime(report.createdAt)}${Number(report.reportsOfThisMessage) > 1 ? ` · ${report.reportsOfThisMessage} reports of this ${subjectNoun(report)}` : ""}${short ? `\n "${short}"` : ""}`;
49
60
  }
50
61
 
51
62
  export function formatChatReportList(data = {}) {
@@ -71,7 +82,7 @@ export function formatChatReportList(data = {}) {
71
82
  export function formatChatReport(report = {}) {
72
83
  const lines = [];
73
84
  lines.push(
74
- `Chat report #${report.id}: ${String(report.status || "open").toUpperCase()} · filed ${formatTime(report.createdAt)}${report.ownerNotifiedAt ? ` · Mikey notified ${formatTime(report.ownerNotifiedAt)}` : ""}`,
85
+ `${isCoverReport(report) ? "Cover photo report" : "Chat report"} #${report.id}: ${String(report.status || "open").toUpperCase()} · filed ${formatTime(report.createdAt)}${report.ownerNotifiedAt ? ` · Mikey notified ${formatTime(report.ownerNotifiedAt)}` : ""}`,
75
86
  );
76
87
  lines.push(`Reporter → reported: ${who(report.reporter)} → ${who(report.reported)}`);
77
88
  lines.push(
@@ -81,7 +92,7 @@ export function formatChatReport(report = {}) {
81
92
  const place = where(report);
82
93
  if (place) lines.push(`Where: ${place}`);
83
94
  if (Number(report.reportsOfThisMessage) > 1) {
84
- lines.push(`This message has ${report.reportsOfThisMessage} reports.`);
95
+ lines.push(`This ${subjectNoun(report)} has ${report.reportsOfThisMessage} reports.`);
85
96
  }
86
97
  if (Array.isArray(report.safetyHoldIds) && report.safetyHoldIds.length) {
87
98
  lines.push(`Safety hold(s): #${report.safetyHoldIds.join(", #")}`);
@@ -96,8 +107,14 @@ export function formatChatReport(report = {}) {
96
107
 
97
108
  const message = report.message;
98
109
  lines.push("");
99
- lines.push(`Reported message${report.messageId ? ` #${report.messageId}` : ""}:`);
110
+ lines.push(
111
+ isCoverReport(report)
112
+ ? "Reported cover photo:"
113
+ : `Reported message${report.messageId ? ` #${report.messageId}` : ""}:`,
114
+ );
100
115
  lines.push(message ? formatMessageLine(message, ">") : " (no snapshot kept)");
116
+ // a cover has no surrounding conversation
117
+ if (isCoverReport(report)) return lines;
101
118
 
102
119
  const before = Array.isArray(report.context?.before) ? report.context.before : [];
103
120
  const after = Array.isArray(report.context?.after) ? report.context.after : [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stage5/lumine",
3
- "version": "0.3.62",
3
+ "version": "0.3.63",
4
4
  "description": "Command line tools for launching Lumine builds on Twinkle.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,7 +2,7 @@
2
2
 
3
3
  Version: 1.71.0
4
4
  Updated: 2026-10-24
5
- Generated: 2026-10-24T05:59:44.939Z
5
+ Generated: 2026-10-24T10:03:41.930Z
6
6
 
7
7
  ## Platform rules
8
8
  Each rule below is a way an app breaks once it runs inside Twinkle's sandboxed frame, even when the code itself is correct. They outrank every other guideline: follow them in every change, including small ones, and fix any break of them you see in code you touch.
@@ -1500,17 +1500,19 @@ world.updatePresence({ x, y, z, facing });
1500
1500
 
1501
1501
  ### Twinkle.reflections
1502
1502
  - async getDailyReflections({ userIds, cursor, lastId, limit } = {}) | scopes: dailyReflections:read
1503
- - Returns: { reflections: [{ id, userId, response, questionId, submittedAt, sharedAt, username, profilePicUrl, question }], cursor? }
1503
+ - Returns: { reflections: [{ id, userId, response, questionId, submittedAt, sharedAt, username, profilePicUrl, question, dayType, pictureUrl, pictureStyle }], cursor? }
1504
1504
  - Read only currently public Daily Reflection shares. response is the exact text the author chose to share publicly, never an unshared raw/private answer.
1505
1505
  - Daily reflections are Daily Question answers shown in the Twinkle feed.
1506
1506
  - Only currently shared public reflections are returned. The response field is the stored shared response version, which may be raw or polished depending on what the author explicitly shared.
1507
1507
  - submittedAt and sharedAt are Unix timestamps (seconds). Use new Date(value * 1000) to convert to JS Date.
1508
+ - dayType is 'word' (Word Day), 'picture' (Picture Day) or null (an ordinary reflection). On a Picture Day the member described a picture instead of reflecting: pictureUrl is the picture they chose (null when they finished with words only), pictureStyle the style they asked for, and response is their description. Show the picture with the description, not the stock question.
1508
1509
  - async getDailyReflectionsByUser(userId, { cursor, lastId, limit } = {}) | scopes: dailyReflections:read
1509
- - Returns: { reflections: [{ id, userId, response, questionId, submittedAt, sharedAt, username, profilePicUrl, question }], cursor? }
1510
+ - Returns: { reflections: [{ id, userId, response, questionId, submittedAt, sharedAt, username, profilePicUrl, question, dayType, pictureUrl, pictureStyle }], cursor? }
1510
1511
  - Read one user's currently public Daily Reflection shares. response is the exact text the author chose to share publicly, never an unshared raw/private answer.
1511
1512
  - Daily reflections are Daily Question answers shown in the Twinkle feed.
1512
1513
  - Only currently shared public reflections are returned. The response field is the stored shared response version, which may be raw or polished depending on what the author explicitly shared.
1513
1514
  - submittedAt and sharedAt are Unix timestamps (seconds). Use new Date(value * 1000) to convert to JS Date.
1515
+ - dayType is 'word' (Word Day), 'picture' (Picture Day) or null (an ordinary reflection). On a Picture Day the member described a picture instead of reflecting: pictureUrl is the picture they chose (null when they finished with words only), pictureStyle the style they asked for, and response is their description. Show the picture with the description, not the stock question.
1514
1516
 
1515
1517
  ### Twinkle.privateDb
1516
1518
  - async get(key) | scopes: privateDb:read