🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@shadowob/shared

Package Overview
Dependencies
Maintainers
1
Versions
77
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@shadowob/shared - npm Package Compare versions

Comparing version
1.1.3
to
1.1.4
+524
dist/chunk-44D7I4ZW.js
// src/utils/index.ts
import { customAlphabet } from "nanoid";
// src/utils/avatar-generator.ts
var COLORS = {
bg: [
"transparent",
"#1e1f22",
"#313338",
"#5865F2",
"#23a559",
"#da373c",
"#f472b6",
"#3b82f6",
"#fbbf24",
"#a855f7",
"#1abc9c",
"#f39c12",
"#e74c3c"
],
body: [
"#2d2d30",
"#e8842c",
"#e8e8e8",
"#7a7a80",
"#d4a574",
"#6b8094",
"#f472b6",
"#c8d6e5",
"#3e2723",
"#bdc3c7",
"#ffb8b8"
],
eyes: [
"#f8e71c",
"#00f3ff",
"#4ade80",
"#60a5fa",
"#a855f7",
"#fbbf24",
"#f87171",
"#ffc0cb",
"#1dd1a1",
"#e056fd"
],
pattern: ["#1a1a1c", "#ffffff", "#5a4a46", "#3d3d40", "#9a9aa0", "#d1ccc0", "#2d3436"]
};
function getRandomElement(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
function generateRandomCatConfig() {
return {
bg: getRandomElement(COLORS.bg),
bgPattern: getRandomElement(["none", "dots", "stripes", "grid", "stars"]),
body: getRandomElement(COLORS.body),
pattern: getRandomElement([
"none",
"tabby",
"tuxedo",
"siamese",
"calico",
"bicolor"
]),
patternColor: getRandomElement(COLORS.pattern),
eyeColor: getRandomElement(COLORS.eyes),
expression: getRandomElement([
"smile",
"open",
"flat",
"sad",
"surprised",
"kawaii",
"winking",
"smirk"
]),
decoration: getRandomElement([
"none",
"glasses",
"blush",
"scar",
"flower",
"fish",
"headband"
])
};
}
function renderCatSvg(config) {
const { bg, bgPattern, body, pattern, patternColor, eyeColor, expression, decoration } = config;
const stroke = "#1a1a1c";
let svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">`;
if (bg && bg !== "transparent") {
svg += `<rect width="100" height="100" fill="${bg}" rx="20" />`;
const pColor = `rgba(255,255,255,0.15)`;
const cleanBg = bg.replace("#", "");
if (bgPattern === "dots") {
svg += `<pattern id="p-${cleanBg}-dots" x="0" y="0" width="10" height="10" patternUnits="userSpaceOnUse"><circle cx="2" cy="2" r="2" fill="${pColor}"/></pattern><rect width="100" height="100" fill="url(#p-${cleanBg}-dots)" rx="20" />`;
} else if (bgPattern === "stripes") {
svg += `<pattern id="p-${cleanBg}-str" x="0" y="0" width="12" height="12" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="12" stroke="${pColor}" stroke-width="4"/></pattern><rect width="100" height="100" fill="url(#p-${cleanBg}-str)" rx="20" />`;
} else if (bgPattern === "grid") {
svg += `<pattern id="p-${cleanBg}-grid" width="16" height="16" patternUnits="userSpaceOnUse"><path d="M 16 0 L 0 0 0 16" fill="none" stroke="${pColor}" stroke-width="1"/></pattern><rect width="100" height="100" fill="url(#p-${cleanBg}-grid)" rx="20" />`;
} else if (bgPattern === "stars") {
svg += `<pattern id="p-${cleanBg}-star" width="20" height="20" patternUnits="userSpaceOnUse"><path d="M10,2 L12,8 L18,8 L13,12 L15,18 L10,14 L5,18 L7,12 L2,8 L8,8 Z" fill="${pColor}" transform="scale(0.5) translate(5,5)"/></pattern><rect width="100" height="100" fill="url(#p-${cleanBg}-star)" rx="20" />`;
}
}
svg += `<ellipse cx="50" cy="85" rx="30" ry="6" fill="rgba(0,0,0,0.2)"/>`;
svg += `<path d="M22,45 C15,22 28,18 34,22 C38,25 40,38 40,38" fill="${body}" stroke="${stroke}" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>`;
svg += `<path d="M78,45 C85,22 72,18 66,22 C62,25 60,38 60,38" fill="${body}" stroke="${stroke}" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>`;
svg += `<path d="M26,38 C22,26 29,24 33,26 C35,28 36,34 36,34" fill="#ffb8b8" opacity="0.8"/>`;
svg += `<path d="M74,38 C78,26 71,24 67,26 C65,28 64,34 64,34" fill="#ffb8b8" opacity="0.8"/>`;
svg += `<ellipse cx="50" cy="58" rx="38" ry="30" fill="${body}" stroke="${stroke}" stroke-width="2.5"/>`;
if (pattern === "tabby") {
svg += `<path d="M50,30 L50,40 M42,32 L46,39 M58,32 L54,39" stroke="${patternColor}" stroke-width="3" stroke-linecap="round" opacity="0.7"/>`;
svg += `<path d="M18,55 L26,57 M20,62 L27,62" stroke="${patternColor}" stroke-width="2.5" stroke-linecap="round" opacity="0.7"/>`;
svg += `<path d="M82,55 L74,57 M80,62 L73,62" stroke="${patternColor}" stroke-width="2.5" stroke-linecap="round" opacity="0.7"/>`;
} else if (pattern === "tuxedo") {
svg += `<path d="M50,56 C30,68 28,88 50,88 C72,88 70,68 50,56" fill="${patternColor}" opacity="0.95"/>`;
svg += `<ellipse cx="50" cy="65" rx="16" ry="12" fill="${patternColor}" opacity="0.95"/>`;
} else if (pattern === "siamese") {
svg += `<ellipse cx="50" cy="62" rx="20" ry="16" fill="${patternColor}" opacity="0.6"/>`;
} else if (pattern === "calico") {
svg += `<path d="M25,40 Q35,30 45,45 Q35,55 25,40" fill="${patternColor}" opacity="0.8"/>`;
svg += `<path d="M75,45 Q65,60 55,50 Q65,35 75,45" fill="#e8842c" opacity="0.8"/>`;
} else if (pattern === "bicolor") {
svg += `<path d="M12,58 Q30,30 50,40 Q60,70 50,88 Q12,88 12,58 Z" fill="${patternColor}" opacity="0.8"/>`;
}
if (decoration === "blush") {
svg += `<ellipse cx="28" cy="62" rx="5" ry="3" fill="#ff7675" opacity="0.7"/>`;
svg += `<ellipse cx="72" cy="62" rx="5" ry="3" fill="#ff7675" opacity="0.7"/>`;
}
if (decoration === "scar") {
svg += `<path d="M28,42 L40,52 M30,48 L35,43 M34,51 L39,46 M32,53 L37,48" stroke="#d63031" stroke-width="1.5" stroke-linecap="round"/>`;
}
const drawEye = (cx, cy, lookDir = 0) => {
if (expression === "kawaii") {
return `<path d="M${cx - 8},${cy} Q${cx},${cy - 8} ${cx + 8},${cy} Q${cx},${cy - 3} ${cx - 8},${cy}" fill="${eyeColor}" stroke="${stroke}" stroke-width="1.5"/><circle cx="${cx + lookDir}" cy="${cy - 2}" r="3" fill="white"/><circle cx="${cx - 3 + lookDir}" cy="${cy}" r="1" fill="white"/>`;
} else if (expression === "winking" && cx > 50) {
return `<path d="M${cx - 7},${cy + 2} Q${cx},${cy - 4} ${cx + 7},${cy + 2}" fill="none" stroke="${stroke}" stroke-width="2.5" stroke-linecap="round"/>`;
} else if (expression === "surprised") {
return `<circle cx="${cx}" cy="${cy}" r="8" fill="white" stroke="${stroke}" stroke-width="1.5"/><circle cx="${cx}" cy="${cy}" r="3" fill="${eyeColor}"/>`;
} else {
return `<circle cx="${cx}" cy="${cy}" r="7.5" fill="${eyeColor}" stroke="${stroke}" stroke-width="1.5"/><circle cx="${cx - 2 + lookDir}" cy="${cy - 2}" r="2.5" fill="white"/><circle cx="${cx + 2 + lookDir}" cy="${cy + 1}" r="1" fill="white"/>`;
}
};
if (expression === "smirk") {
svg += `<path d="M26,50 L42,50" stroke="${stroke}" stroke-width="2.5" stroke-linecap="round"/>`;
svg += drawEye(66, 52, 2);
} else {
svg += drawEye(34, 52, 0);
svg += drawEye(66, 52, 0);
}
if (decoration === "glasses") {
svg += `<circle cx="34" cy="52" r="11" fill="rgba(255,255,255,0.2)" stroke="#2d3436" stroke-width="2.5"/>`;
svg += `<circle cx="66" cy="52" r="11" fill="rgba(255,255,255,0.2)" stroke="#2d3436" stroke-width="2.5"/>`;
svg += `<path d="M45,50 Q50,48 55,50" fill="none" stroke="#2d3436" stroke-width="2.5" stroke-linecap="round"/>`;
}
svg += `<path d="M47,62 L53,62 L50,65 Z" fill="#ff9ff3" stroke="${stroke}" stroke-width="1" stroke-linejoin="round"/>`;
if (expression === "smile" || expression === "kawaii" || expression === "winking") {
svg += `<path d="M42,67 Q46,72 50,67 M50,67 Q54,72 58,67" fill="none" stroke="${stroke}" stroke-width="2" stroke-linecap="round"/>`;
} else if (expression === "open") {
svg += `<path d="M46,67 Q50,75 54,67 Z" fill="#ff7675" stroke="${stroke}" stroke-width="1.5" stroke-linejoin="round"/>`;
} else if (expression === "flat") {
svg += `<path d="M47,68 L53,68" stroke="${stroke}" stroke-width="2" stroke-linecap="round"/>`;
} else if (expression === "sad") {
svg += `<path d="M43,70 Q50,64 57,70" fill="none" stroke="${stroke}" stroke-width="2" stroke-linecap="round"/>`;
} else if (expression === "surprised") {
svg += `<circle cx="50" cy="70" r="3" fill="#1a1a1c"/>`;
} else if (expression === "smirk") {
svg += `<path d="M46,67 Q52,69 56,64" fill="none" stroke="${stroke}" stroke-width="2" stroke-linecap="round"/>`;
}
svg += `<path d="M28,62 L15,60 M28,65 L14,66" stroke="${stroke}" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>`;
svg += `<path d="M72,62 L85,60 M72,65 L86,66" stroke="${stroke}" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>`;
if (decoration === "headband") {
svg += `<path d="M16,35 Q50,20 84,35" fill="none" stroke="#e17055" stroke-width="6" stroke-linecap="round"/>`;
svg += `<path d="M50,15 L60,25 L50,28 L40,25 Z" fill="#fab1a0" stroke="#e17055" stroke-width="2"/>`;
} else if (decoration === "flower") {
svg += `<path d="M75,30 Q80,20 85,30 Q95,25 85,35 Q90,45 80,40 Q70,45 75,35 Q65,25 75,30 Z" fill="#ffeaa7" stroke="#fdcb6e" stroke-width="1.5"/>`;
svg += `<circle cx="80" cy="33" r="3" fill="#d63031"/>`;
} else if (decoration === "fish") {
svg += `<path d="M40,82 Q50,75 60,82 L65,78 L65,86 L60,82 Q50,89 40,82 Z" fill="#81ecec" stroke="${stroke}" stroke-width="1.5"/>`;
svg += `<circle cx="45" cy="81" r="1" fill="${stroke}"/>`;
}
svg += `</svg>`;
return `data:image/svg+xml,${encodeURIComponent(svg.replace(/\n\s*/g, ""))}`;
}
// src/utils/message-mentions.ts
function uniqueValues(values) {
return Array.from(new Set(values.filter((value) => !!value)));
}
function canonicalMentionToken(mention) {
if (mention.kind === "channel") return `<#${mention.channelId ?? mention.targetId}>`;
if (mention.kind === "server") return `<@server:${mention.serverId ?? mention.targetId}>`;
if (mention.kind === "app") return `<@app:${mention.appId ?? mention.targetId}>`;
if (mention.kind === "here" || mention.kind === "everyone") {
const scope = mention.serverId ?? mention.targetId;
return scope ? `<!${mention.kind}:${scope}>` : `<!${mention.kind}>`;
}
return `<@${mention.userId ?? mention.targetId}>`;
}
function parseCanonicalMentionToken(token) {
const app = token.match(/^<@app:([^>]+)>$/u);
if (app?.[1]) return { kind: "app", targetId: app[1] };
const user = token.match(/^<@([^>:]+)>$/u);
if (user?.[1]) return { kind: "user", targetId: user[1] };
const server = token.match(/^<@server:([^>]+)>$/u);
if (server?.[1]) return { kind: "server", targetId: server[1] };
const channel = token.match(/^<#([^>]+)>$/u);
if (channel?.[1]) return { kind: "channel", targetId: channel[1] };
const broadcast = token.match(/^<!(here|everyone)(?::([^>]+))?>$/u);
if (broadcast?.[1] === "here" || broadcast?.[1] === "everyone") {
return {
kind: broadcast[1],
...broadcast[2] ? { targetId: broadcast[2] } : {}
};
}
return null;
}
function isCanonicalMentionToken(token) {
return parseCanonicalMentionToken(token) !== null;
}
function matchTextsForMention(mention) {
return uniqueValues([mention.token, mention.sourceToken, canonicalMentionToken(mention)]);
}
function isValidRange(content, mention, range) {
if (!range) return false;
if (!Number.isInteger(range.start) || !Number.isInteger(range.end)) return false;
if (range.start < 0 || range.end <= range.start || range.end > content.length) return false;
const sourceText = content.slice(range.start, range.end);
return matchTextsForMention(mention).includes(sourceText);
}
function overlaps(a, b) {
return a.start < b.end && b.start < a.end;
}
function canUseRange(range, used) {
return !used.some((candidate) => overlaps(candidate, range));
}
function findOccurrences(content, token, used) {
const occurrences = [];
let index = content.indexOf(token);
while (index >= 0) {
const range = { start: index, end: index + token.length };
if (canUseRange(range, used)) occurrences.push(range);
index = content.indexOf(token, index + token.length);
}
return occurrences;
}
function addCandidate(candidates, used, mention, range, order, sourceText) {
if (!canUseRange(range, used)) return;
const matchedText = sourceText ?? mention.sourceToken ?? mention.token;
used.push(range);
candidates.push({
start: range.start,
end: range.end,
order,
sourceText: matchedText,
mention: { ...mention, range }
});
}
function segmentTextByMentions(content, mentions) {
if (!content) return [];
const usableMentions = (mentions ?? []).filter((mention) => mention.token);
if (usableMentions.length === 0) return [{ type: "text", text: content }];
const candidates = [];
const usedRanges = [];
const pendingByText = /* @__PURE__ */ new Map();
usableMentions.forEach((mention, order) => {
if (isValidRange(content, mention, mention.range)) {
addCandidate(
candidates,
usedRanges,
mention,
mention.range,
order,
content.slice(mention.range.start, mention.range.end)
);
return;
}
for (const text of matchTextsForMention(mention)) {
const pending = pendingByText.get(text) ?? [];
pending.push({ mention, order });
pendingByText.set(text, pending);
}
});
const pendingGroups = Array.from(pendingByText.entries()).sort((a, b) => {
const lengthDelta = b[0].length - a[0].length;
if (lengthDelta !== 0) return lengthDelta;
return a[1][0].order - b[1][0].order;
});
for (const [token, pending] of pendingGroups) {
const occurrences = findOccurrences(content, token, usedRanges);
if (occurrences.length === 0) continue;
if (pending.length === 1) {
const { mention, order } = pending[0];
for (const range of occurrences) {
addCandidate(candidates, usedRanges, mention, range, order, token);
}
continue;
}
pending.forEach(({ mention, order }, index) => {
const range = occurrences[index];
if (range) addCandidate(candidates, usedRanges, mention, range, order, token);
});
}
candidates.sort((a, b) => a.start - b.start || b.end - a.end || a.order - b.order);
const segments = [];
let cursor = 0;
for (const candidate of candidates) {
if (candidate.start < cursor) continue;
if (candidate.start > cursor) {
segments.push({ type: "text", text: content.slice(cursor, candidate.start) });
}
const text = content.slice(candidate.start, candidate.end);
segments.push({
type: "mention",
text,
range: { start: candidate.start, end: candidate.end },
mention: { ...candidate.mention, sourceToken: candidate.sourceText }
});
cursor = candidate.end;
}
if (cursor < content.length) {
segments.push({ type: "text", text: content.slice(cursor) });
}
return segments.length > 0 ? segments : [{ type: "text", text: content }];
}
function assignMentionRanges(content, mentions) {
return segmentTextByMentions(content, mentions).filter((segment) => {
return segment.type === "mention";
}).map((segment) => ({
...segment.mention,
token: segment.mention.token || segment.text,
range: segment.range
}));
}
function canonicalizeMentionContent(content, mentions) {
const canonicalMentions = [];
let nextContent = "";
for (const segment of segmentTextByMentions(content, mentions)) {
if (segment.type === "text") {
nextContent += segment.text;
continue;
}
const token = canonicalMentionToken(segment.mention);
const start = nextContent.length;
nextContent += token;
canonicalMentions.push({
...segment.mention,
token,
sourceToken: segment.text === token ? segment.mention.sourceToken : segment.text,
range: { start, end: start + token.length }
});
}
return { content: nextContent, mentions: canonicalMentions };
}
function mentionDisplayText(mention) {
return mention.label || mention.token;
}
function escapeMarkdownLinkLabel(label) {
return label.replace(/\\/g, "\\\\").replace(/\[/g, "\\[").replace(/\]/g, "\\]");
}
function buildMentionMarkdownLinks(content, mentions, hrefForMention) {
const linkedMentions = [];
const markdown = segmentTextByMentions(content, mentions).map((segment) => {
if (segment.type === "text") return segment.text;
const href = hrefForMention(segment.mention, linkedMentions.length);
if (!href) return segment.text;
linkedMentions.push(segment.mention);
return `[${escapeMarkdownLinkLabel(mentionDisplayText(segment.mention))}](${href})`;
}).join("");
return { markdown, mentions: linkedMentions };
}
// src/utils/pixel-cats.ts
var variants = [
// 0: Shadow — Black cat (logo style)
{
body: "#2d2d30",
stroke: "#1a1a1c",
earInner: "#3d3d40",
eyeL: "#f8e71c",
eyeR: "#00f3ff",
nose: "#3a2a26"
},
// 1: Mikan — Orange tabby
{
body: "#e8842c",
stroke: "#1a1a1c",
earInner: "#f5a623",
eyeL: "#4ade80",
eyeR: "#4ade80",
nose: "#d46b1a"
},
// 2: Yuki — White cat
{
body: "#e8e8e8",
stroke: "#a0a0a0",
earInner: "#ffc0cb",
eyeL: "#60a5fa",
eyeR: "#60a5fa",
nose: "#f5a0b0"
},
// 3: Haiiro — Gray cat
{
body: "#7a7a80",
stroke: "#4a4a50",
earInner: "#9a9aa0",
eyeL: "#fbbf24",
eyeR: "#fbbf24",
nose: "#5a4a46"
},
// 4: Tuxedo — Black & white accents
{
body: "#2d2d30",
stroke: "#1a1a1c",
earInner: "#e0e0e0",
eyeL: "#22c55e",
eyeR: "#22c55e",
nose: "#3a2a26"
},
// 5: Mocha — Cream/beige
{
body: "#d4a574",
stroke: "#8b6914",
earInner: "#e8c9a0",
eyeL: "#d97706",
eyeR: "#d97706",
nose: "#a0705a"
},
// 6: Blue — Russian blue
{
body: "#6b8094",
stroke: "#3d5060",
earInner: "#8ba0b4",
eyeL: "#22c55e",
eyeR: "#22c55e",
nose: "#5a6a76"
},
// 7: Sakura — Fantasy pink
{
body: "#f472b6",
stroke: "#be185d",
earInner: "#f9a8d4",
eyeL: "#a855f7",
eyeR: "#c084fc",
nose: "#d44a8c"
}
];
function makeCatSvg(c) {
return [
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">',
`<path d="M22,45 Q15,22 28,22 Q34,22 40,38" fill="${c.body}" stroke="${c.stroke}" stroke-width="2.5" stroke-linecap="round"/>`,
`<path d="M78,45 Q85,22 72,22 Q66,22 60,38" fill="${c.body}" stroke="${c.stroke}" stroke-width="2.5" stroke-linecap="round"/>`,
`<path d="M26,38 Q22,26 29,26 Q33,26 36,34" fill="${c.earInner}" opacity="0.5"/>`,
`<path d="M74,38 Q78,26 71,26 Q67,26 64,34" fill="${c.earInner}" opacity="0.5"/>`,
`<ellipse cx="50" cy="58" rx="36" ry="28" fill="${c.body}" stroke="${c.stroke}" stroke-width="2.5"/>`,
`<circle cx="35" cy="52" r="7" fill="${c.eyeL}" stroke="${c.stroke}" stroke-width="1.5"/>`,
`<circle cx="33" cy="49" r="2.5" fill="white"/>`,
`<circle cx="65" cy="52" r="7" fill="${c.eyeR}" stroke="${c.stroke}" stroke-width="1.5"/>`,
`<circle cx="63" cy="49" r="2.5" fill="white"/>`,
`<ellipse cx="50" cy="62" rx="3.5" ry="2.2" fill="${c.nose}"/>`,
`<path d="M42,67 Q46,72 50,67" fill="none" stroke="${c.stroke}" stroke-width="2" stroke-linecap="round"/>`,
`<path d="M50,67 Q54,72 58,67" fill="none" stroke="${c.stroke}" stroke-width="2" stroke-linecap="round"/>`,
"</svg>"
].join("");
}
var catDataUris = variants.map((v) => {
const svg = makeCatSvg(v);
return `data:image/svg+xml,${encodeURIComponent(svg)}`;
});
function getCatAvatar(index) {
return catDataUris[index % catDataUris.length];
}
function getCatAvatarByUserId(userId) {
let hash = 0;
for (let i = 0; i < userId.length; i++) {
hash = (hash << 5) - hash + userId.charCodeAt(i) | 0;
}
return getCatAvatar(Math.abs(hash) % catDataUris.length);
}
function getAllCatAvatars() {
const names = ["\u5F71\u5B50", "\u871C\u67D1", "\u5C0F\u96EA", "\u7070\u7070", "\u71D5\u5C3E\u670D", "\u6469\u5361", "\u84DD\u84DD", "\u5C0F\u6A31"];
return catDataUris.map((uri, i) => ({ index: i, name: names[i], dataUri: uri }));
}
function getCatSvgString(index) {
return makeCatSvg(variants[index % variants.length]);
}
var CAT_AVATAR_COUNT = variants.length;
// src/utils/index.ts
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var generateInviteCode = customAlphabet(alphabet, 8);
function formatDate(date) {
const d = typeof date === "string" ? new Date(date) : date;
return d.toISOString();
}
function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
function slugify(text) {
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
}
export {
generateRandomCatConfig,
renderCatSvg,
canonicalMentionToken,
parseCanonicalMentionToken,
isCanonicalMentionToken,
segmentTextByMentions,
assignMentionRanges,
canonicalizeMentionContent,
mentionDisplayText,
escapeMarkdownLinkLabel,
buildMentionMarkdownLinks,
getCatAvatar,
getCatAvatarByUserId,
getAllCatAvatars,
getCatSvgString,
CAT_AVATAR_COUNT,
generateInviteCode,
formatDate,
isValidEmail,
slugify
};
interface Message {
id: string;
content: string;
channelId: string;
authorId: string;
threadId: string | null;
replyToId: string | null;
isEdited: boolean;
isPinned: boolean;
createdAt: string;
updatedAt: string;
author?: {
id: string;
username: string;
displayName: string;
avatarUrl: string | null;
isBot: boolean;
};
attachments?: Attachment[];
reactions?: ReactionGroup[];
metadata?: MessageMetadata | null;
}
type MessageMentionKind = 'user' | 'buddy' | 'app' | 'channel' | 'server' | 'here' | 'everyone';
interface MessageMentionRange {
start: number;
end: number;
}
interface MessageMention {
kind: MessageMentionKind;
/** Canonical target id. For users this is userId, for channels channelId, for servers serverId. */
targetId: string;
/** Canonical text persisted in message content, e.g. <@userId>, <#channelId>. */
token: string;
/** Optional display text selected or typed by the sender before canonicalization. */
sourceToken?: string;
/** Human-readable label used by renderers. */
label: string;
/** Optional source range in content. Clients may omit it; servers may recompute later. */
range?: MessageMentionRange;
serverId?: string;
serverSlug?: string | null;
serverName?: string | null;
channelId?: string;
channelName?: string | null;
appId?: string;
appKey?: string;
appName?: string | null;
iconUrl?: string | null;
userId?: string;
username?: string | null;
displayName?: string | null;
avatarUrl?: string | null;
isBot?: boolean;
isPrivate?: boolean;
}
interface MessageMetadata {
mentions?: MessageMention[];
agentChain?: Record<string, unknown>;
interactive?: Record<string, unknown>;
interactiveResponse?: Record<string, unknown>;
interactiveState?: Record<string, unknown>;
commerceCards?: CommerceMessageCard[];
paidFileCards?: PaidFileCard[];
oauthLinkCards?: OAuthLinkCard[];
[key: string]: unknown;
}
interface CommerceOfferCardInput {
id?: string;
kind: 'offer';
offerId: string;
}
type CommerceMessageCard = CommerceProductCard | CommerceOfferCardInput;
interface CommerceProductCard {
id: string;
kind: 'offer' | 'product';
offerId?: string;
shopId: string;
shopScope: {
kind: 'server' | 'user';
id: string;
};
productId: string;
skuId?: string;
snapshot: {
name: string;
summary?: string | null;
imageUrl?: string | null;
price: number;
currency: string;
productType: 'physical' | 'entitlement';
billingMode?: 'one_time' | 'fixed_duration' | 'subscription';
durationSeconds?: number | null;
resourceType?: string;
resourceId?: string;
capability?: string;
};
purchase: {
mode: 'direct' | 'select_sku' | 'open_detail';
};
}
interface PaidFileCard {
id: string;
kind: 'paid_file';
fileId: string;
entitlementId?: string | null;
deliverableId?: string;
snapshot: {
name: string;
summary?: string | null;
mime?: string | null;
sizeBytes?: number | null;
previewUrl?: string | null;
};
action: {
mode: 'open_paid_file';
};
}
interface OAuthLinkCard {
id: string;
kind: 'oauth_link';
appId: string;
clientId?: string | null;
title: string;
description?: string | null;
iconUrl?: string | null;
meta?: {
appName?: string | null;
avatarUrl?: string | null;
iconUrl?: string | null;
coverUrl?: string | null;
homepageUrl?: string | null;
origin?: string | null;
};
url: string;
embedUrl?: string | null;
fallbackUrl?: string | null;
scopes?: string[];
action: {
mode: 'open_iframe' | 'open_external';
};
}
type MentionSuggestionTrigger = '@' | '#';
interface MentionSuggestion {
id: string;
kind: MessageMentionKind;
targetId: string;
token: string;
label: string;
description?: string | null;
serverId?: string;
serverSlug?: string | null;
serverName?: string | null;
channelId?: string;
channelName?: string | null;
appId?: string;
appKey?: string;
appName?: string | null;
iconUrl?: string | null;
userId?: string;
username?: string | null;
displayName?: string | null;
avatarUrl?: string | null;
isBot?: boolean;
isPrivate?: boolean;
}
interface Attachment {
id: string;
messageId: string;
filename: string;
url: string;
contentType: string;
size: number;
width: number | null;
height: number | null;
createdAt: string;
}
interface ReactionGroup {
emoji: string;
count: number;
userIds: string[];
}
interface Thread {
id: string;
name: string;
channelId: string;
parentMessageId: string;
creatorId: string;
isArchived: boolean;
createdAt: string;
updatedAt: string;
}
interface SendMessageRequest {
content: string;
threadId?: string;
replyToId?: string;
mentions?: MessageMention[];
metadata?: MessageMetadata;
}
interface UpdateMessageRequest {
content: string;
}
type NotificationType = 'mention' | 'reply' | 'dm' | 'system';
interface Notification {
id: string;
userId: string;
type: NotificationType;
title: string;
body: string | null;
referenceId: string | null;
referenceType: string | null;
isRead: boolean;
createdAt: string;
}
export type { Attachment as A, CommerceMessageCard as C, MentionSuggestion as M, Notification as N, OAuthLinkCard as O, PaidFileCard as P, ReactionGroup as R, SendMessageRequest as S, Thread as T, UpdateMessageRequest as U, CommerceOfferCardInput as a, CommerceProductCard as b, MentionSuggestionTrigger as c, Message as d, MessageMention as e, MessageMentionKind as f, MessageMentionRange as g, MessageMetadata as h, NotificationType as i };
interface Message {
id: string;
content: string;
channelId: string;
authorId: string;
threadId: string | null;
replyToId: string | null;
isEdited: boolean;
isPinned: boolean;
createdAt: string;
updatedAt: string;
author?: {
id: string;
username: string;
displayName: string;
avatarUrl: string | null;
isBot: boolean;
};
attachments?: Attachment[];
reactions?: ReactionGroup[];
metadata?: MessageMetadata | null;
}
type MessageMentionKind = 'user' | 'buddy' | 'app' | 'channel' | 'server' | 'here' | 'everyone';
interface MessageMentionRange {
start: number;
end: number;
}
interface MessageMention {
kind: MessageMentionKind;
/** Canonical target id. For users this is userId, for channels channelId, for servers serverId. */
targetId: string;
/** Canonical text persisted in message content, e.g. <@userId>, <#channelId>. */
token: string;
/** Optional display text selected or typed by the sender before canonicalization. */
sourceToken?: string;
/** Human-readable label used by renderers. */
label: string;
/** Optional source range in content. Clients may omit it; servers may recompute later. */
range?: MessageMentionRange;
serverId?: string;
serverSlug?: string | null;
serverName?: string | null;
channelId?: string;
channelName?: string | null;
appId?: string;
appKey?: string;
appName?: string | null;
iconUrl?: string | null;
userId?: string;
username?: string | null;
displayName?: string | null;
avatarUrl?: string | null;
isBot?: boolean;
isPrivate?: boolean;
}
interface MessageMetadata {
mentions?: MessageMention[];
agentChain?: Record<string, unknown>;
interactive?: Record<string, unknown>;
interactiveResponse?: Record<string, unknown>;
interactiveState?: Record<string, unknown>;
commerceCards?: CommerceMessageCard[];
paidFileCards?: PaidFileCard[];
oauthLinkCards?: OAuthLinkCard[];
[key: string]: unknown;
}
interface CommerceOfferCardInput {
id?: string;
kind: 'offer';
offerId: string;
}
type CommerceMessageCard = CommerceProductCard | CommerceOfferCardInput;
interface CommerceProductCard {
id: string;
kind: 'offer' | 'product';
offerId?: string;
shopId: string;
shopScope: {
kind: 'server' | 'user';
id: string;
};
productId: string;
skuId?: string;
snapshot: {
name: string;
summary?: string | null;
imageUrl?: string | null;
price: number;
currency: string;
productType: 'physical' | 'entitlement';
billingMode?: 'one_time' | 'fixed_duration' | 'subscription';
durationSeconds?: number | null;
resourceType?: string;
resourceId?: string;
capability?: string;
};
purchase: {
mode: 'direct' | 'select_sku' | 'open_detail';
};
}
interface PaidFileCard {
id: string;
kind: 'paid_file';
fileId: string;
entitlementId?: string | null;
deliverableId?: string;
snapshot: {
name: string;
summary?: string | null;
mime?: string | null;
sizeBytes?: number | null;
previewUrl?: string | null;
};
action: {
mode: 'open_paid_file';
};
}
interface OAuthLinkCard {
id: string;
kind: 'oauth_link';
appId: string;
clientId?: string | null;
title: string;
description?: string | null;
iconUrl?: string | null;
meta?: {
appName?: string | null;
avatarUrl?: string | null;
iconUrl?: string | null;
coverUrl?: string | null;
homepageUrl?: string | null;
origin?: string | null;
};
url: string;
embedUrl?: string | null;
fallbackUrl?: string | null;
scopes?: string[];
action: {
mode: 'open_iframe' | 'open_external';
};
}
type MentionSuggestionTrigger = '@' | '#';
interface MentionSuggestion {
id: string;
kind: MessageMentionKind;
targetId: string;
token: string;
label: string;
description?: string | null;
serverId?: string;
serverSlug?: string | null;
serverName?: string | null;
channelId?: string;
channelName?: string | null;
appId?: string;
appKey?: string;
appName?: string | null;
iconUrl?: string | null;
userId?: string;
username?: string | null;
displayName?: string | null;
avatarUrl?: string | null;
isBot?: boolean;
isPrivate?: boolean;
}
interface Attachment {
id: string;
messageId: string;
filename: string;
url: string;
contentType: string;
size: number;
width: number | null;
height: number | null;
createdAt: string;
}
interface ReactionGroup {
emoji: string;
count: number;
userIds: string[];
}
interface Thread {
id: string;
name: string;
channelId: string;
parentMessageId: string;
creatorId: string;
isArchived: boolean;
createdAt: string;
updatedAt: string;
}
interface SendMessageRequest {
content: string;
threadId?: string;
replyToId?: string;
mentions?: MessageMention[];
metadata?: MessageMetadata;
}
interface UpdateMessageRequest {
content: string;
}
type NotificationType = 'mention' | 'reply' | 'dm' | 'system';
interface Notification {
id: string;
userId: string;
type: NotificationType;
title: string;
body: string | null;
referenceId: string | null;
referenceType: string | null;
isRead: boolean;
createdAt: string;
}
export type { Attachment as A, CommerceMessageCard as C, MentionSuggestion as M, Notification as N, OAuthLinkCard as O, PaidFileCard as P, ReactionGroup as R, SendMessageRequest as S, Thread as T, UpdateMessageRequest as U, CommerceOfferCardInput as a, CommerceProductCard as b, MentionSuggestionTrigger as c, Message as d, MessageMention as e, MessageMentionKind as f, MessageMentionRange as g, MessageMetadata as h, NotificationType as i };
+3
-0

@@ -721,2 +721,3 @@ "use strict";

if (mention.kind === "server") return `<@server:${mention.serverId ?? mention.targetId}>`;
if (mention.kind === "app") return `<@app:${mention.appId ?? mention.targetId}>`;
if (mention.kind === "here" || mention.kind === "everyone") {

@@ -729,2 +730,4 @@ const scope = mention.serverId ?? mention.targetId;

function parseCanonicalMentionToken(token) {
const app = token.match(/^<@app:([^>]+)>$/u);
if (app?.[1]) return { kind: "app", targetId: app[1] };
const user = token.match(/^<@([^>:]+)>$/u);

@@ -731,0 +734,0 @@ if (user?.[1]) return { kind: "user", targetId: user[1] };

+1
-1
export { CLIENT_EVENTS, ClientEvent, LIMITS, SERVER_EVENTS, ServerEvent } from './constants/index.cjs';
export { DEFAULT_HOMEPLAY_CATALOG, SHADOW_PLAY_SERVER_TEMPLATE, ShadowHomePlayCatalogItem, ShadowPlayAction, ShadowPlayAvailability, ShadowPlayServerTemplate, getDefaultHomePlay, getPlayBuddyEmail, getPlayBuddyUsername } from './play-catalog/index.cjs';
export { Agent, AgentCapability, AgentInfo, AgentKernelType, AgentStatus, AuthResponse, Channel, ChannelSortBy, ChannelSortDirection, ChannelSortOptions, ChannelType, CreateAgentRequest, CreateChannelRequest, CreateServerRequest, FriendEntry, FriendSource, Friendship, FriendshipStatus, LoginRequest, Member, MemberRole, RegisterRequest, Server, UpdateChannelRequest, UpdateServerRequest, User, UserMembership, UserMembershipTier, UserProfile, UserStatus } from './types/index.cjs';
export { A as Attachment, C as CommerceMessageCard, a as CommerceOfferCardInput, b as CommerceProductCard, M as MentionSuggestion, c as MentionSuggestionTrigger, d as Message, e as MessageMention, f as MessageMentionKind, g as MessageMentionRange, h as MessageMetadata, N as Notification, i as NotificationType, P as PaidFileCard, R as ReactionGroup, S as SendMessageRequest, T as Thread, U as UpdateMessageRequest } from './message.types-lG4qBSus.cjs';
export { A as Attachment, C as CommerceMessageCard, a as CommerceOfferCardInput, b as CommerceProductCard, M as MentionSuggestion, c as MentionSuggestionTrigger, d as Message, e as MessageMention, f as MessageMentionKind, g as MessageMentionRange, h as MessageMetadata, N as Notification, i as NotificationType, O as OAuthLinkCard, P as PaidFileCard, R as ReactionGroup, S as SendMessageRequest, T as Thread, U as UpdateMessageRequest } from './message.types-Ca8col0f.cjs';
export { BgPattern, CAT_AVATAR_COUNT, CatConfig, CatDecoration, CatExpression, CatPattern, MessageMentionTextSegment, assignMentionRanges, buildMentionMarkdownLinks, canonicalMentionToken, canonicalizeMentionContent, escapeMarkdownLinkLabel, formatDate, generateInviteCode, generateRandomCatConfig, getAllCatAvatars, getCatAvatar, getCatAvatarByUserId, getCatSvgString, isCanonicalMentionToken, isValidEmail, mentionDisplayText, parseCanonicalMentionToken, renderCatSvg, segmentTextByMentions, slugify } from './utils/index.cjs';
export { CLIENT_EVENTS, ClientEvent, LIMITS, SERVER_EVENTS, ServerEvent } from './constants/index.js';
export { DEFAULT_HOMEPLAY_CATALOG, SHADOW_PLAY_SERVER_TEMPLATE, ShadowHomePlayCatalogItem, ShadowPlayAction, ShadowPlayAvailability, ShadowPlayServerTemplate, getDefaultHomePlay, getPlayBuddyEmail, getPlayBuddyUsername } from './play-catalog/index.js';
export { Agent, AgentCapability, AgentInfo, AgentKernelType, AgentStatus, AuthResponse, Channel, ChannelSortBy, ChannelSortDirection, ChannelSortOptions, ChannelType, CreateAgentRequest, CreateChannelRequest, CreateServerRequest, FriendEntry, FriendSource, Friendship, FriendshipStatus, LoginRequest, Member, MemberRole, RegisterRequest, Server, UpdateChannelRequest, UpdateServerRequest, User, UserMembership, UserMembershipTier, UserProfile, UserStatus } from './types/index.js';
export { A as Attachment, C as CommerceMessageCard, a as CommerceOfferCardInput, b as CommerceProductCard, M as MentionSuggestion, c as MentionSuggestionTrigger, d as Message, e as MessageMention, f as MessageMentionKind, g as MessageMentionRange, h as MessageMetadata, N as Notification, i as NotificationType, P as PaidFileCard, R as ReactionGroup, S as SendMessageRequest, T as Thread, U as UpdateMessageRequest } from './message.types-lG4qBSus.js';
export { A as Attachment, C as CommerceMessageCard, a as CommerceOfferCardInput, b as CommerceProductCard, M as MentionSuggestion, c as MentionSuggestionTrigger, d as Message, e as MessageMention, f as MessageMentionKind, g as MessageMentionRange, h as MessageMetadata, N as Notification, i as NotificationType, O as OAuthLinkCard, P as PaidFileCard, R as ReactionGroup, S as SendMessageRequest, T as Thread, U as UpdateMessageRequest } from './message.types-Ca8col0f.js';
export { BgPattern, CAT_AVATAR_COUNT, CatConfig, CatDecoration, CatExpression, CatPattern, MessageMentionTextSegment, assignMentionRanges, buildMentionMarkdownLinks, canonicalMentionToken, canonicalizeMentionContent, escapeMarkdownLinkLabel, formatDate, generateInviteCode, generateRandomCatConfig, getAllCatAvatars, getCatAvatar, getCatAvatarByUserId, getCatSvgString, isCanonicalMentionToken, isValidEmail, mentionDisplayText, parseCanonicalMentionToken, renderCatSvg, segmentTextByMentions, slugify } from './utils/index.js';

@@ -35,3 +35,3 @@ import {

slugify
} from "./chunk-E3UBH4AI.js";
} from "./chunk-44D7I4ZW.js";
export {

@@ -38,0 +38,0 @@ CAT_AVATAR_COUNT,

@@ -1,2 +0,2 @@

export { A as Attachment, C as CommerceMessageCard, a as CommerceOfferCardInput, b as CommerceProductCard, M as MentionSuggestion, c as MentionSuggestionTrigger, d as Message, e as MessageMention, f as MessageMentionKind, g as MessageMentionRange, h as MessageMetadata, N as Notification, i as NotificationType, P as PaidFileCard, R as ReactionGroup, S as SendMessageRequest, T as Thread, U as UpdateMessageRequest } from '../message.types-lG4qBSus.cjs';
export { A as Attachment, C as CommerceMessageCard, a as CommerceOfferCardInput, b as CommerceProductCard, M as MentionSuggestion, c as MentionSuggestionTrigger, d as Message, e as MessageMention, f as MessageMentionKind, g as MessageMentionRange, h as MessageMetadata, N as Notification, i as NotificationType, O as OAuthLinkCard, P as PaidFileCard, R as ReactionGroup, S as SendMessageRequest, T as Thread, U as UpdateMessageRequest } from '../message.types-Ca8col0f.cjs';

@@ -3,0 +3,0 @@ type AgentStatus = 'running' | 'stopped' | 'error';

@@ -1,2 +0,2 @@

export { A as Attachment, C as CommerceMessageCard, a as CommerceOfferCardInput, b as CommerceProductCard, M as MentionSuggestion, c as MentionSuggestionTrigger, d as Message, e as MessageMention, f as MessageMentionKind, g as MessageMentionRange, h as MessageMetadata, N as Notification, i as NotificationType, P as PaidFileCard, R as ReactionGroup, S as SendMessageRequest, T as Thread, U as UpdateMessageRequest } from '../message.types-lG4qBSus.js';
export { A as Attachment, C as CommerceMessageCard, a as CommerceOfferCardInput, b as CommerceProductCard, M as MentionSuggestion, c as MentionSuggestionTrigger, d as Message, e as MessageMention, f as MessageMentionKind, g as MessageMentionRange, h as MessageMetadata, N as Notification, i as NotificationType, O as OAuthLinkCard, P as PaidFileCard, R as ReactionGroup, S as SendMessageRequest, T as Thread, U as UpdateMessageRequest } from '../message.types-Ca8col0f.js';

@@ -3,0 +3,0 @@ type AgentStatus = 'running' | 'stopped' | 'error';

@@ -236,2 +236,3 @@ "use strict";

if (mention.kind === "server") return `<@server:${mention.serverId ?? mention.targetId}>`;
if (mention.kind === "app") return `<@app:${mention.appId ?? mention.targetId}>`;
if (mention.kind === "here" || mention.kind === "everyone") {

@@ -244,2 +245,4 @@ const scope = mention.serverId ?? mention.targetId;

function parseCanonicalMentionToken(token) {
const app = token.match(/^<@app:([^>]+)>$/u);
if (app?.[1]) return { kind: "app", targetId: app[1] };
const user = token.match(/^<@([^>:]+)>$/u);

@@ -246,0 +249,0 @@ if (user?.[1]) return { kind: "user", targetId: user[1] };

@@ -1,2 +0,2 @@

import { g as MessageMentionRange, e as MessageMention } from '../message.types-lG4qBSus.cjs';
import { g as MessageMentionRange, e as MessageMention } from '../message.types-Ca8col0f.cjs';

@@ -34,2 +34,5 @@ type CatPattern = 'none' | 'tabby' | 'tuxedo' | 'siamese' | 'calico' | 'bicolor';

} | {
kind: 'app';
targetId: string;
} | {
kind: 'channel';

@@ -36,0 +39,0 @@ targetId: string;

@@ -1,2 +0,2 @@

import { g as MessageMentionRange, e as MessageMention } from '../message.types-lG4qBSus.js';
import { g as MessageMentionRange, e as MessageMention } from '../message.types-Ca8col0f.js';

@@ -34,2 +34,5 @@ type CatPattern = 'none' | 'tabby' | 'tuxedo' | 'siamese' | 'calico' | 'bicolor';

} | {
kind: 'app';
targetId: string;
} | {
kind: 'channel';

@@ -36,0 +39,0 @@ targetId: string;

@@ -22,3 +22,3 @@ import {

slugify
} from "../chunk-E3UBH4AI.js";
} from "../chunk-44D7I4ZW.js";
export {

@@ -25,0 +25,0 @@ CAT_AVATAR_COUNT,

{
"name": "@shadowob/shared",
"version": "1.1.3",
"version": "1.1.4",
"type": "module",

@@ -5,0 +5,0 @@ "main": "./dist/index.js",

// src/utils/index.ts
import { customAlphabet } from "nanoid";
// src/utils/avatar-generator.ts
var COLORS = {
bg: [
"transparent",
"#1e1f22",
"#313338",
"#5865F2",
"#23a559",
"#da373c",
"#f472b6",
"#3b82f6",
"#fbbf24",
"#a855f7",
"#1abc9c",
"#f39c12",
"#e74c3c"
],
body: [
"#2d2d30",
"#e8842c",
"#e8e8e8",
"#7a7a80",
"#d4a574",
"#6b8094",
"#f472b6",
"#c8d6e5",
"#3e2723",
"#bdc3c7",
"#ffb8b8"
],
eyes: [
"#f8e71c",
"#00f3ff",
"#4ade80",
"#60a5fa",
"#a855f7",
"#fbbf24",
"#f87171",
"#ffc0cb",
"#1dd1a1",
"#e056fd"
],
pattern: ["#1a1a1c", "#ffffff", "#5a4a46", "#3d3d40", "#9a9aa0", "#d1ccc0", "#2d3436"]
};
function getRandomElement(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
function generateRandomCatConfig() {
return {
bg: getRandomElement(COLORS.bg),
bgPattern: getRandomElement(["none", "dots", "stripes", "grid", "stars"]),
body: getRandomElement(COLORS.body),
pattern: getRandomElement([
"none",
"tabby",
"tuxedo",
"siamese",
"calico",
"bicolor"
]),
patternColor: getRandomElement(COLORS.pattern),
eyeColor: getRandomElement(COLORS.eyes),
expression: getRandomElement([
"smile",
"open",
"flat",
"sad",
"surprised",
"kawaii",
"winking",
"smirk"
]),
decoration: getRandomElement([
"none",
"glasses",
"blush",
"scar",
"flower",
"fish",
"headband"
])
};
}
function renderCatSvg(config) {
const { bg, bgPattern, body, pattern, patternColor, eyeColor, expression, decoration } = config;
const stroke = "#1a1a1c";
let svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">`;
if (bg && bg !== "transparent") {
svg += `<rect width="100" height="100" fill="${bg}" rx="20" />`;
const pColor = `rgba(255,255,255,0.15)`;
const cleanBg = bg.replace("#", "");
if (bgPattern === "dots") {
svg += `<pattern id="p-${cleanBg}-dots" x="0" y="0" width="10" height="10" patternUnits="userSpaceOnUse"><circle cx="2" cy="2" r="2" fill="${pColor}"/></pattern><rect width="100" height="100" fill="url(#p-${cleanBg}-dots)" rx="20" />`;
} else if (bgPattern === "stripes") {
svg += `<pattern id="p-${cleanBg}-str" x="0" y="0" width="12" height="12" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="12" stroke="${pColor}" stroke-width="4"/></pattern><rect width="100" height="100" fill="url(#p-${cleanBg}-str)" rx="20" />`;
} else if (bgPattern === "grid") {
svg += `<pattern id="p-${cleanBg}-grid" width="16" height="16" patternUnits="userSpaceOnUse"><path d="M 16 0 L 0 0 0 16" fill="none" stroke="${pColor}" stroke-width="1"/></pattern><rect width="100" height="100" fill="url(#p-${cleanBg}-grid)" rx="20" />`;
} else if (bgPattern === "stars") {
svg += `<pattern id="p-${cleanBg}-star" width="20" height="20" patternUnits="userSpaceOnUse"><path d="M10,2 L12,8 L18,8 L13,12 L15,18 L10,14 L5,18 L7,12 L2,8 L8,8 Z" fill="${pColor}" transform="scale(0.5) translate(5,5)"/></pattern><rect width="100" height="100" fill="url(#p-${cleanBg}-star)" rx="20" />`;
}
}
svg += `<ellipse cx="50" cy="85" rx="30" ry="6" fill="rgba(0,0,0,0.2)"/>`;
svg += `<path d="M22,45 C15,22 28,18 34,22 C38,25 40,38 40,38" fill="${body}" stroke="${stroke}" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>`;
svg += `<path d="M78,45 C85,22 72,18 66,22 C62,25 60,38 60,38" fill="${body}" stroke="${stroke}" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>`;
svg += `<path d="M26,38 C22,26 29,24 33,26 C35,28 36,34 36,34" fill="#ffb8b8" opacity="0.8"/>`;
svg += `<path d="M74,38 C78,26 71,24 67,26 C65,28 64,34 64,34" fill="#ffb8b8" opacity="0.8"/>`;
svg += `<ellipse cx="50" cy="58" rx="38" ry="30" fill="${body}" stroke="${stroke}" stroke-width="2.5"/>`;
if (pattern === "tabby") {
svg += `<path d="M50,30 L50,40 M42,32 L46,39 M58,32 L54,39" stroke="${patternColor}" stroke-width="3" stroke-linecap="round" opacity="0.7"/>`;
svg += `<path d="M18,55 L26,57 M20,62 L27,62" stroke="${patternColor}" stroke-width="2.5" stroke-linecap="round" opacity="0.7"/>`;
svg += `<path d="M82,55 L74,57 M80,62 L73,62" stroke="${patternColor}" stroke-width="2.5" stroke-linecap="round" opacity="0.7"/>`;
} else if (pattern === "tuxedo") {
svg += `<path d="M50,56 C30,68 28,88 50,88 C72,88 70,68 50,56" fill="${patternColor}" opacity="0.95"/>`;
svg += `<ellipse cx="50" cy="65" rx="16" ry="12" fill="${patternColor}" opacity="0.95"/>`;
} else if (pattern === "siamese") {
svg += `<ellipse cx="50" cy="62" rx="20" ry="16" fill="${patternColor}" opacity="0.6"/>`;
} else if (pattern === "calico") {
svg += `<path d="M25,40 Q35,30 45,45 Q35,55 25,40" fill="${patternColor}" opacity="0.8"/>`;
svg += `<path d="M75,45 Q65,60 55,50 Q65,35 75,45" fill="#e8842c" opacity="0.8"/>`;
} else if (pattern === "bicolor") {
svg += `<path d="M12,58 Q30,30 50,40 Q60,70 50,88 Q12,88 12,58 Z" fill="${patternColor}" opacity="0.8"/>`;
}
if (decoration === "blush") {
svg += `<ellipse cx="28" cy="62" rx="5" ry="3" fill="#ff7675" opacity="0.7"/>`;
svg += `<ellipse cx="72" cy="62" rx="5" ry="3" fill="#ff7675" opacity="0.7"/>`;
}
if (decoration === "scar") {
svg += `<path d="M28,42 L40,52 M30,48 L35,43 M34,51 L39,46 M32,53 L37,48" stroke="#d63031" stroke-width="1.5" stroke-linecap="round"/>`;
}
const drawEye = (cx, cy, lookDir = 0) => {
if (expression === "kawaii") {
return `<path d="M${cx - 8},${cy} Q${cx},${cy - 8} ${cx + 8},${cy} Q${cx},${cy - 3} ${cx - 8},${cy}" fill="${eyeColor}" stroke="${stroke}" stroke-width="1.5"/><circle cx="${cx + lookDir}" cy="${cy - 2}" r="3" fill="white"/><circle cx="${cx - 3 + lookDir}" cy="${cy}" r="1" fill="white"/>`;
} else if (expression === "winking" && cx > 50) {
return `<path d="M${cx - 7},${cy + 2} Q${cx},${cy - 4} ${cx + 7},${cy + 2}" fill="none" stroke="${stroke}" stroke-width="2.5" stroke-linecap="round"/>`;
} else if (expression === "surprised") {
return `<circle cx="${cx}" cy="${cy}" r="8" fill="white" stroke="${stroke}" stroke-width="1.5"/><circle cx="${cx}" cy="${cy}" r="3" fill="${eyeColor}"/>`;
} else {
return `<circle cx="${cx}" cy="${cy}" r="7.5" fill="${eyeColor}" stroke="${stroke}" stroke-width="1.5"/><circle cx="${cx - 2 + lookDir}" cy="${cy - 2}" r="2.5" fill="white"/><circle cx="${cx + 2 + lookDir}" cy="${cy + 1}" r="1" fill="white"/>`;
}
};
if (expression === "smirk") {
svg += `<path d="M26,50 L42,50" stroke="${stroke}" stroke-width="2.5" stroke-linecap="round"/>`;
svg += drawEye(66, 52, 2);
} else {
svg += drawEye(34, 52, 0);
svg += drawEye(66, 52, 0);
}
if (decoration === "glasses") {
svg += `<circle cx="34" cy="52" r="11" fill="rgba(255,255,255,0.2)" stroke="#2d3436" stroke-width="2.5"/>`;
svg += `<circle cx="66" cy="52" r="11" fill="rgba(255,255,255,0.2)" stroke="#2d3436" stroke-width="2.5"/>`;
svg += `<path d="M45,50 Q50,48 55,50" fill="none" stroke="#2d3436" stroke-width="2.5" stroke-linecap="round"/>`;
}
svg += `<path d="M47,62 L53,62 L50,65 Z" fill="#ff9ff3" stroke="${stroke}" stroke-width="1" stroke-linejoin="round"/>`;
if (expression === "smile" || expression === "kawaii" || expression === "winking") {
svg += `<path d="M42,67 Q46,72 50,67 M50,67 Q54,72 58,67" fill="none" stroke="${stroke}" stroke-width="2" stroke-linecap="round"/>`;
} else if (expression === "open") {
svg += `<path d="M46,67 Q50,75 54,67 Z" fill="#ff7675" stroke="${stroke}" stroke-width="1.5" stroke-linejoin="round"/>`;
} else if (expression === "flat") {
svg += `<path d="M47,68 L53,68" stroke="${stroke}" stroke-width="2" stroke-linecap="round"/>`;
} else if (expression === "sad") {
svg += `<path d="M43,70 Q50,64 57,70" fill="none" stroke="${stroke}" stroke-width="2" stroke-linecap="round"/>`;
} else if (expression === "surprised") {
svg += `<circle cx="50" cy="70" r="3" fill="#1a1a1c"/>`;
} else if (expression === "smirk") {
svg += `<path d="M46,67 Q52,69 56,64" fill="none" stroke="${stroke}" stroke-width="2" stroke-linecap="round"/>`;
}
svg += `<path d="M28,62 L15,60 M28,65 L14,66" stroke="${stroke}" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>`;
svg += `<path d="M72,62 L85,60 M72,65 L86,66" stroke="${stroke}" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>`;
if (decoration === "headband") {
svg += `<path d="M16,35 Q50,20 84,35" fill="none" stroke="#e17055" stroke-width="6" stroke-linecap="round"/>`;
svg += `<path d="M50,15 L60,25 L50,28 L40,25 Z" fill="#fab1a0" stroke="#e17055" stroke-width="2"/>`;
} else if (decoration === "flower") {
svg += `<path d="M75,30 Q80,20 85,30 Q95,25 85,35 Q90,45 80,40 Q70,45 75,35 Q65,25 75,30 Z" fill="#ffeaa7" stroke="#fdcb6e" stroke-width="1.5"/>`;
svg += `<circle cx="80" cy="33" r="3" fill="#d63031"/>`;
} else if (decoration === "fish") {
svg += `<path d="M40,82 Q50,75 60,82 L65,78 L65,86 L60,82 Q50,89 40,82 Z" fill="#81ecec" stroke="${stroke}" stroke-width="1.5"/>`;
svg += `<circle cx="45" cy="81" r="1" fill="${stroke}"/>`;
}
svg += `</svg>`;
return `data:image/svg+xml,${encodeURIComponent(svg.replace(/\n\s*/g, ""))}`;
}
// src/utils/message-mentions.ts
function uniqueValues(values) {
return Array.from(new Set(values.filter((value) => !!value)));
}
function canonicalMentionToken(mention) {
if (mention.kind === "channel") return `<#${mention.channelId ?? mention.targetId}>`;
if (mention.kind === "server") return `<@server:${mention.serverId ?? mention.targetId}>`;
if (mention.kind === "here" || mention.kind === "everyone") {
const scope = mention.serverId ?? mention.targetId;
return scope ? `<!${mention.kind}:${scope}>` : `<!${mention.kind}>`;
}
return `<@${mention.userId ?? mention.targetId}>`;
}
function parseCanonicalMentionToken(token) {
const user = token.match(/^<@([^>:]+)>$/u);
if (user?.[1]) return { kind: "user", targetId: user[1] };
const server = token.match(/^<@server:([^>]+)>$/u);
if (server?.[1]) return { kind: "server", targetId: server[1] };
const channel = token.match(/^<#([^>]+)>$/u);
if (channel?.[1]) return { kind: "channel", targetId: channel[1] };
const broadcast = token.match(/^<!(here|everyone)(?::([^>]+))?>$/u);
if (broadcast?.[1] === "here" || broadcast?.[1] === "everyone") {
return {
kind: broadcast[1],
...broadcast[2] ? { targetId: broadcast[2] } : {}
};
}
return null;
}
function isCanonicalMentionToken(token) {
return parseCanonicalMentionToken(token) !== null;
}
function matchTextsForMention(mention) {
return uniqueValues([mention.token, mention.sourceToken, canonicalMentionToken(mention)]);
}
function isValidRange(content, mention, range) {
if (!range) return false;
if (!Number.isInteger(range.start) || !Number.isInteger(range.end)) return false;
if (range.start < 0 || range.end <= range.start || range.end > content.length) return false;
const sourceText = content.slice(range.start, range.end);
return matchTextsForMention(mention).includes(sourceText);
}
function overlaps(a, b) {
return a.start < b.end && b.start < a.end;
}
function canUseRange(range, used) {
return !used.some((candidate) => overlaps(candidate, range));
}
function findOccurrences(content, token, used) {
const occurrences = [];
let index = content.indexOf(token);
while (index >= 0) {
const range = { start: index, end: index + token.length };
if (canUseRange(range, used)) occurrences.push(range);
index = content.indexOf(token, index + token.length);
}
return occurrences;
}
function addCandidate(candidates, used, mention, range, order, sourceText) {
if (!canUseRange(range, used)) return;
const matchedText = sourceText ?? mention.sourceToken ?? mention.token;
used.push(range);
candidates.push({
start: range.start,
end: range.end,
order,
sourceText: matchedText,
mention: { ...mention, range }
});
}
function segmentTextByMentions(content, mentions) {
if (!content) return [];
const usableMentions = (mentions ?? []).filter((mention) => mention.token);
if (usableMentions.length === 0) return [{ type: "text", text: content }];
const candidates = [];
const usedRanges = [];
const pendingByText = /* @__PURE__ */ new Map();
usableMentions.forEach((mention, order) => {
if (isValidRange(content, mention, mention.range)) {
addCandidate(
candidates,
usedRanges,
mention,
mention.range,
order,
content.slice(mention.range.start, mention.range.end)
);
return;
}
for (const text of matchTextsForMention(mention)) {
const pending = pendingByText.get(text) ?? [];
pending.push({ mention, order });
pendingByText.set(text, pending);
}
});
const pendingGroups = Array.from(pendingByText.entries()).sort((a, b) => {
const lengthDelta = b[0].length - a[0].length;
if (lengthDelta !== 0) return lengthDelta;
return a[1][0].order - b[1][0].order;
});
for (const [token, pending] of pendingGroups) {
const occurrences = findOccurrences(content, token, usedRanges);
if (occurrences.length === 0) continue;
if (pending.length === 1) {
const { mention, order } = pending[0];
for (const range of occurrences) {
addCandidate(candidates, usedRanges, mention, range, order, token);
}
continue;
}
pending.forEach(({ mention, order }, index) => {
const range = occurrences[index];
if (range) addCandidate(candidates, usedRanges, mention, range, order, token);
});
}
candidates.sort((a, b) => a.start - b.start || b.end - a.end || a.order - b.order);
const segments = [];
let cursor = 0;
for (const candidate of candidates) {
if (candidate.start < cursor) continue;
if (candidate.start > cursor) {
segments.push({ type: "text", text: content.slice(cursor, candidate.start) });
}
const text = content.slice(candidate.start, candidate.end);
segments.push({
type: "mention",
text,
range: { start: candidate.start, end: candidate.end },
mention: { ...candidate.mention, sourceToken: candidate.sourceText }
});
cursor = candidate.end;
}
if (cursor < content.length) {
segments.push({ type: "text", text: content.slice(cursor) });
}
return segments.length > 0 ? segments : [{ type: "text", text: content }];
}
function assignMentionRanges(content, mentions) {
return segmentTextByMentions(content, mentions).filter((segment) => {
return segment.type === "mention";
}).map((segment) => ({
...segment.mention,
token: segment.mention.token || segment.text,
range: segment.range
}));
}
function canonicalizeMentionContent(content, mentions) {
const canonicalMentions = [];
let nextContent = "";
for (const segment of segmentTextByMentions(content, mentions)) {
if (segment.type === "text") {
nextContent += segment.text;
continue;
}
const token = canonicalMentionToken(segment.mention);
const start = nextContent.length;
nextContent += token;
canonicalMentions.push({
...segment.mention,
token,
sourceToken: segment.text === token ? segment.mention.sourceToken : segment.text,
range: { start, end: start + token.length }
});
}
return { content: nextContent, mentions: canonicalMentions };
}
function mentionDisplayText(mention) {
return mention.label || mention.token;
}
function escapeMarkdownLinkLabel(label) {
return label.replace(/\\/g, "\\\\").replace(/\[/g, "\\[").replace(/\]/g, "\\]");
}
function buildMentionMarkdownLinks(content, mentions, hrefForMention) {
const linkedMentions = [];
const markdown = segmentTextByMentions(content, mentions).map((segment) => {
if (segment.type === "text") return segment.text;
const href = hrefForMention(segment.mention, linkedMentions.length);
if (!href) return segment.text;
linkedMentions.push(segment.mention);
return `[${escapeMarkdownLinkLabel(mentionDisplayText(segment.mention))}](${href})`;
}).join("");
return { markdown, mentions: linkedMentions };
}
// src/utils/pixel-cats.ts
var variants = [
// 0: Shadow — Black cat (logo style)
{
body: "#2d2d30",
stroke: "#1a1a1c",
earInner: "#3d3d40",
eyeL: "#f8e71c",
eyeR: "#00f3ff",
nose: "#3a2a26"
},
// 1: Mikan — Orange tabby
{
body: "#e8842c",
stroke: "#1a1a1c",
earInner: "#f5a623",
eyeL: "#4ade80",
eyeR: "#4ade80",
nose: "#d46b1a"
},
// 2: Yuki — White cat
{
body: "#e8e8e8",
stroke: "#a0a0a0",
earInner: "#ffc0cb",
eyeL: "#60a5fa",
eyeR: "#60a5fa",
nose: "#f5a0b0"
},
// 3: Haiiro — Gray cat
{
body: "#7a7a80",
stroke: "#4a4a50",
earInner: "#9a9aa0",
eyeL: "#fbbf24",
eyeR: "#fbbf24",
nose: "#5a4a46"
},
// 4: Tuxedo — Black & white accents
{
body: "#2d2d30",
stroke: "#1a1a1c",
earInner: "#e0e0e0",
eyeL: "#22c55e",
eyeR: "#22c55e",
nose: "#3a2a26"
},
// 5: Mocha — Cream/beige
{
body: "#d4a574",
stroke: "#8b6914",
earInner: "#e8c9a0",
eyeL: "#d97706",
eyeR: "#d97706",
nose: "#a0705a"
},
// 6: Blue — Russian blue
{
body: "#6b8094",
stroke: "#3d5060",
earInner: "#8ba0b4",
eyeL: "#22c55e",
eyeR: "#22c55e",
nose: "#5a6a76"
},
// 7: Sakura — Fantasy pink
{
body: "#f472b6",
stroke: "#be185d",
earInner: "#f9a8d4",
eyeL: "#a855f7",
eyeR: "#c084fc",
nose: "#d44a8c"
}
];
function makeCatSvg(c) {
return [
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">',
`<path d="M22,45 Q15,22 28,22 Q34,22 40,38" fill="${c.body}" stroke="${c.stroke}" stroke-width="2.5" stroke-linecap="round"/>`,
`<path d="M78,45 Q85,22 72,22 Q66,22 60,38" fill="${c.body}" stroke="${c.stroke}" stroke-width="2.5" stroke-linecap="round"/>`,
`<path d="M26,38 Q22,26 29,26 Q33,26 36,34" fill="${c.earInner}" opacity="0.5"/>`,
`<path d="M74,38 Q78,26 71,26 Q67,26 64,34" fill="${c.earInner}" opacity="0.5"/>`,
`<ellipse cx="50" cy="58" rx="36" ry="28" fill="${c.body}" stroke="${c.stroke}" stroke-width="2.5"/>`,
`<circle cx="35" cy="52" r="7" fill="${c.eyeL}" stroke="${c.stroke}" stroke-width="1.5"/>`,
`<circle cx="33" cy="49" r="2.5" fill="white"/>`,
`<circle cx="65" cy="52" r="7" fill="${c.eyeR}" stroke="${c.stroke}" stroke-width="1.5"/>`,
`<circle cx="63" cy="49" r="2.5" fill="white"/>`,
`<ellipse cx="50" cy="62" rx="3.5" ry="2.2" fill="${c.nose}"/>`,
`<path d="M42,67 Q46,72 50,67" fill="none" stroke="${c.stroke}" stroke-width="2" stroke-linecap="round"/>`,
`<path d="M50,67 Q54,72 58,67" fill="none" stroke="${c.stroke}" stroke-width="2" stroke-linecap="round"/>`,
"</svg>"
].join("");
}
var catDataUris = variants.map((v) => {
const svg = makeCatSvg(v);
return `data:image/svg+xml,${encodeURIComponent(svg)}`;
});
function getCatAvatar(index) {
return catDataUris[index % catDataUris.length];
}
function getCatAvatarByUserId(userId) {
let hash = 0;
for (let i = 0; i < userId.length; i++) {
hash = (hash << 5) - hash + userId.charCodeAt(i) | 0;
}
return getCatAvatar(Math.abs(hash) % catDataUris.length);
}
function getAllCatAvatars() {
const names = ["\u5F71\u5B50", "\u871C\u67D1", "\u5C0F\u96EA", "\u7070\u7070", "\u71D5\u5C3E\u670D", "\u6469\u5361", "\u84DD\u84DD", "\u5C0F\u6A31"];
return catDataUris.map((uri, i) => ({ index: i, name: names[i], dataUri: uri }));
}
function getCatSvgString(index) {
return makeCatSvg(variants[index % variants.length]);
}
var CAT_AVATAR_COUNT = variants.length;
// src/utils/index.ts
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var generateInviteCode = customAlphabet(alphabet, 8);
function formatDate(date) {
const d = typeof date === "string" ? new Date(date) : date;
return d.toISOString();
}
function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
function slugify(text) {
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
}
export {
generateRandomCatConfig,
renderCatSvg,
canonicalMentionToken,
parseCanonicalMentionToken,
isCanonicalMentionToken,
segmentTextByMentions,
assignMentionRanges,
canonicalizeMentionContent,
mentionDisplayText,
escapeMarkdownLinkLabel,
buildMentionMarkdownLinks,
getCatAvatar,
getCatAvatarByUserId,
getAllCatAvatars,
getCatSvgString,
CAT_AVATAR_COUNT,
generateInviteCode,
formatDate,
isValidEmail,
slugify
};
interface Message {
id: string;
content: string;
channelId: string;
authorId: string;
threadId: string | null;
replyToId: string | null;
isEdited: boolean;
isPinned: boolean;
createdAt: string;
updatedAt: string;
author?: {
id: string;
username: string;
displayName: string;
avatarUrl: string | null;
isBot: boolean;
};
attachments?: Attachment[];
reactions?: ReactionGroup[];
metadata?: MessageMetadata | null;
}
type MessageMentionKind = 'user' | 'buddy' | 'channel' | 'server' | 'here' | 'everyone';
interface MessageMentionRange {
start: number;
end: number;
}
interface MessageMention {
kind: MessageMentionKind;
/** Canonical target id. For users this is userId, for channels channelId, for servers serverId. */
targetId: string;
/** Canonical text persisted in message content, e.g. <@userId>, <#channelId>. */
token: string;
/** Optional display text selected or typed by the sender before canonicalization. */
sourceToken?: string;
/** Human-readable label used by renderers. */
label: string;
/** Optional source range in content. Clients may omit it; servers may recompute later. */
range?: MessageMentionRange;
serverId?: string;
serverSlug?: string | null;
serverName?: string | null;
channelId?: string;
channelName?: string | null;
userId?: string;
username?: string | null;
displayName?: string | null;
avatarUrl?: string | null;
isBot?: boolean;
isPrivate?: boolean;
}
interface MessageMetadata {
mentions?: MessageMention[];
agentChain?: Record<string, unknown>;
interactive?: Record<string, unknown>;
interactiveResponse?: Record<string, unknown>;
interactiveState?: Record<string, unknown>;
commerceCards?: CommerceMessageCard[];
paidFileCards?: PaidFileCard[];
[key: string]: unknown;
}
interface CommerceOfferCardInput {
id?: string;
kind: 'offer';
offerId: string;
}
type CommerceMessageCard = CommerceProductCard | CommerceOfferCardInput;
interface CommerceProductCard {
id: string;
kind: 'offer' | 'product';
offerId?: string;
shopId: string;
shopScope: {
kind: 'server' | 'user';
id: string;
};
productId: string;
skuId?: string;
snapshot: {
name: string;
summary?: string | null;
imageUrl?: string | null;
price: number;
currency: string;
productType: 'physical' | 'entitlement';
billingMode?: 'one_time' | 'fixed_duration' | 'subscription';
durationSeconds?: number | null;
resourceType?: string;
resourceId?: string;
capability?: string;
};
purchase: {
mode: 'direct' | 'select_sku' | 'open_detail';
};
}
interface PaidFileCard {
id: string;
kind: 'paid_file';
fileId: string;
entitlementId?: string | null;
deliverableId?: string;
snapshot: {
name: string;
summary?: string | null;
mime?: string | null;
sizeBytes?: number | null;
previewUrl?: string | null;
};
action: {
mode: 'open_paid_file';
};
}
type MentionSuggestionTrigger = '@' | '#';
interface MentionSuggestion {
id: string;
kind: MessageMentionKind;
targetId: string;
token: string;
label: string;
description?: string | null;
serverId?: string;
serverSlug?: string | null;
serverName?: string | null;
channelId?: string;
channelName?: string | null;
userId?: string;
username?: string | null;
displayName?: string | null;
avatarUrl?: string | null;
isBot?: boolean;
isPrivate?: boolean;
}
interface Attachment {
id: string;
messageId: string;
filename: string;
url: string;
contentType: string;
size: number;
width: number | null;
height: number | null;
createdAt: string;
}
interface ReactionGroup {
emoji: string;
count: number;
userIds: string[];
}
interface Thread {
id: string;
name: string;
channelId: string;
parentMessageId: string;
creatorId: string;
isArchived: boolean;
createdAt: string;
updatedAt: string;
}
interface SendMessageRequest {
content: string;
threadId?: string;
replyToId?: string;
mentions?: MessageMention[];
metadata?: MessageMetadata;
}
interface UpdateMessageRequest {
content: string;
}
type NotificationType = 'mention' | 'reply' | 'dm' | 'system';
interface Notification {
id: string;
userId: string;
type: NotificationType;
title: string;
body: string | null;
referenceId: string | null;
referenceType: string | null;
isRead: boolean;
createdAt: string;
}
export type { Attachment as A, CommerceMessageCard as C, MentionSuggestion as M, Notification as N, PaidFileCard as P, ReactionGroup as R, SendMessageRequest as S, Thread as T, UpdateMessageRequest as U, CommerceOfferCardInput as a, CommerceProductCard as b, MentionSuggestionTrigger as c, Message as d, MessageMention as e, MessageMentionKind as f, MessageMentionRange as g, MessageMetadata as h, NotificationType as i };
interface Message {
id: string;
content: string;
channelId: string;
authorId: string;
threadId: string | null;
replyToId: string | null;
isEdited: boolean;
isPinned: boolean;
createdAt: string;
updatedAt: string;
author?: {
id: string;
username: string;
displayName: string;
avatarUrl: string | null;
isBot: boolean;
};
attachments?: Attachment[];
reactions?: ReactionGroup[];
metadata?: MessageMetadata | null;
}
type MessageMentionKind = 'user' | 'buddy' | 'channel' | 'server' | 'here' | 'everyone';
interface MessageMentionRange {
start: number;
end: number;
}
interface MessageMention {
kind: MessageMentionKind;
/** Canonical target id. For users this is userId, for channels channelId, for servers serverId. */
targetId: string;
/** Canonical text persisted in message content, e.g. <@userId>, <#channelId>. */
token: string;
/** Optional display text selected or typed by the sender before canonicalization. */
sourceToken?: string;
/** Human-readable label used by renderers. */
label: string;
/** Optional source range in content. Clients may omit it; servers may recompute later. */
range?: MessageMentionRange;
serverId?: string;
serverSlug?: string | null;
serverName?: string | null;
channelId?: string;
channelName?: string | null;
userId?: string;
username?: string | null;
displayName?: string | null;
avatarUrl?: string | null;
isBot?: boolean;
isPrivate?: boolean;
}
interface MessageMetadata {
mentions?: MessageMention[];
agentChain?: Record<string, unknown>;
interactive?: Record<string, unknown>;
interactiveResponse?: Record<string, unknown>;
interactiveState?: Record<string, unknown>;
commerceCards?: CommerceMessageCard[];
paidFileCards?: PaidFileCard[];
[key: string]: unknown;
}
interface CommerceOfferCardInput {
id?: string;
kind: 'offer';
offerId: string;
}
type CommerceMessageCard = CommerceProductCard | CommerceOfferCardInput;
interface CommerceProductCard {
id: string;
kind: 'offer' | 'product';
offerId?: string;
shopId: string;
shopScope: {
kind: 'server' | 'user';
id: string;
};
productId: string;
skuId?: string;
snapshot: {
name: string;
summary?: string | null;
imageUrl?: string | null;
price: number;
currency: string;
productType: 'physical' | 'entitlement';
billingMode?: 'one_time' | 'fixed_duration' | 'subscription';
durationSeconds?: number | null;
resourceType?: string;
resourceId?: string;
capability?: string;
};
purchase: {
mode: 'direct' | 'select_sku' | 'open_detail';
};
}
interface PaidFileCard {
id: string;
kind: 'paid_file';
fileId: string;
entitlementId?: string | null;
deliverableId?: string;
snapshot: {
name: string;
summary?: string | null;
mime?: string | null;
sizeBytes?: number | null;
previewUrl?: string | null;
};
action: {
mode: 'open_paid_file';
};
}
type MentionSuggestionTrigger = '@' | '#';
interface MentionSuggestion {
id: string;
kind: MessageMentionKind;
targetId: string;
token: string;
label: string;
description?: string | null;
serverId?: string;
serverSlug?: string | null;
serverName?: string | null;
channelId?: string;
channelName?: string | null;
userId?: string;
username?: string | null;
displayName?: string | null;
avatarUrl?: string | null;
isBot?: boolean;
isPrivate?: boolean;
}
interface Attachment {
id: string;
messageId: string;
filename: string;
url: string;
contentType: string;
size: number;
width: number | null;
height: number | null;
createdAt: string;
}
interface ReactionGroup {
emoji: string;
count: number;
userIds: string[];
}
interface Thread {
id: string;
name: string;
channelId: string;
parentMessageId: string;
creatorId: string;
isArchived: boolean;
createdAt: string;
updatedAt: string;
}
interface SendMessageRequest {
content: string;
threadId?: string;
replyToId?: string;
mentions?: MessageMention[];
metadata?: MessageMetadata;
}
interface UpdateMessageRequest {
content: string;
}
type NotificationType = 'mention' | 'reply' | 'dm' | 'system';
interface Notification {
id: string;
userId: string;
type: NotificationType;
title: string;
body: string | null;
referenceId: string | null;
referenceType: string | null;
isRead: boolean;
createdAt: string;
}
export type { Attachment as A, CommerceMessageCard as C, MentionSuggestion as M, Notification as N, PaidFileCard as P, ReactionGroup as R, SendMessageRequest as S, Thread as T, UpdateMessageRequest as U, CommerceOfferCardInput as a, CommerceProductCard as b, MentionSuggestionTrigger as c, Message as d, MessageMention as e, MessageMentionKind as f, MessageMentionRange as g, MessageMetadata as h, NotificationType as i };