@openmarket/rooms-client 0.1.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/LICENSE +201 -0
- package/README.md +25 -0
- package/dist/agent/armed-mode.d.ts +187 -0
- package/dist/agent/armed-mode.js +306 -0
- package/dist/agent/clients/common.d.ts +70 -0
- package/dist/agent/clients/common.js +46 -0
- package/dist/agent/lane-draft.d.ts +15 -0
- package/dist/agent/lane-draft.js +43 -0
- package/dist/agent/lane-prompts.d.ts +102 -0
- package/dist/agent/lane-prompts.js +120 -0
- package/dist/agent/library-context.d.ts +38 -0
- package/dist/agent/library-context.js +57 -0
- package/dist/agent/library-model.d.ts +41 -0
- package/dist/agent/library-model.js +173 -0
- package/dist/agent/room-context.d.ts +151 -0
- package/dist/agent/room-context.js +251 -0
- package/dist/agent/sidebar-model.d.ts +86 -0
- package/dist/agent/sidebar-model.js +162 -0
- package/dist/agent/topic-inbox.d.ts +119 -0
- package/dist/agent/topic-inbox.js +266 -0
- package/dist/agent/topic-view-batcher.d.ts +54 -0
- package/dist/agent/topic-view-batcher.js +115 -0
- package/dist/client.d.ts +421 -0
- package/dist/client.js +1428 -0
- package/dist/doc-reconcile.d.ts +100 -0
- package/dist/doc-reconcile.js +110 -0
- package/dist/doc-uri.d.ts +29 -0
- package/dist/doc-uri.js +55 -0
- package/dist/endpoints.d.ts +9 -0
- package/dist/endpoints.js +13 -0
- package/dist/jwt.d.ts +10 -0
- package/dist/jwt.js +51 -0
- package/dist/library-publisher.d.ts +52 -0
- package/dist/library-publisher.js +49 -0
- package/dist/merge3.d.ts +50 -0
- package/dist/merge3.js +280 -0
- package/dist/names.d.ts +6 -0
- package/dist/names.js +35 -0
- package/dist/shared/emoji-data.d.ts +22 -0
- package/dist/shared/emoji-data.js +8834 -0
- package/dist/shared/mentions.d.ts +6 -0
- package/dist/shared/mentions.js +32 -0
- package/dist/shared/rooms-protocol.d.ts +1183 -0
- package/dist/shared/rooms-protocol.js +160 -0
- package/dist/social-client.d.ts +361 -0
- package/dist/social-client.js +686 -0
- package/dist/social-types.d.ts +338 -0
- package/dist/social-types.js +1 -0
- package/dist/suggestion-attribution.d.ts +10 -0
- package/dist/suggestion-attribution.js +56 -0
- package/dist/topic-uri.d.ts +26 -0
- package/dist/topic-uri.js +40 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.js +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/dist/ws-client.d.ts +115 -0
- package/dist/ws-client.js +491 -0
- package/package.json +180 -0
- package/src/agent/armed-mode.ts +368 -0
- package/src/agent/clients/common.ts +91 -0
- package/src/agent/lane-draft.ts +47 -0
- package/src/agent/lane-prompts.ts +267 -0
- package/src/agent/library-context.ts +97 -0
- package/src/agent/library-model.ts +210 -0
- package/src/agent/room-context.ts +351 -0
- package/src/agent/sidebar-model.ts +235 -0
- package/src/agent/topic-inbox.ts +297 -0
- package/src/agent/topic-view-batcher.ts +134 -0
- package/src/client.ts +2331 -0
- package/src/doc-reconcile.ts +160 -0
- package/src/doc-uri.ts +83 -0
- package/src/endpoints.ts +14 -0
- package/src/jwt.ts +59 -0
- package/src/library-publisher.ts +93 -0
- package/src/merge3.ts +326 -0
- package/src/names.ts +44 -0
- package/src/shared/emoji-data.ts +8868 -0
- package/src/shared/mentions.ts +32 -0
- package/src/shared/rooms-protocol.ts +1339 -0
- package/src/social-client.ts +1287 -0
- package/src/social-types.ts +376 -0
- package/src/suggestion-attribution.ts +83 -0
- package/src/topic-uri.ts +64 -0
- package/src/types.ts +83 -0
- package/src/version.ts +2 -0
- package/src/ws-client.ts +611 -0
package/dist/merge3.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Line-based three-way merge (diff3 semantics) for the library mirror's
|
|
3
|
+
* agent-mediated conflict resolution. Pure, no deps, byte-faithful: content
|
|
4
|
+
* is split on "\n" only and re-joined identically, so CRLF and no-trailing-
|
|
5
|
+
* newline round-trip exactly.
|
|
6
|
+
*
|
|
7
|
+
* The contract callers rely on:
|
|
8
|
+
* - `clean: true` means every collision region was one-sided or identical on
|
|
9
|
+
* both sides; `merged` is the full merged document.
|
|
10
|
+
* - `clean: false` means at least one region genuinely diverged (or an input
|
|
11
|
+
* bound tripped); `merged` is "" so no caller can write a half-merge, and
|
|
12
|
+
* `regions` carries the diverging chunks for a mediator to look at.
|
|
13
|
+
* - Identical changes on BOTH sides merge clean. This is load-bearing: a
|
|
14
|
+
* crash between the merged-file write and the manifest save re-runs the
|
|
15
|
+
* merge with mine already containing theirs' lines, and it must converge.
|
|
16
|
+
* - Touching-or-overlapping changes from the two sides conflict (git's
|
|
17
|
+
* conservative rule); insertions at the same anchor conflict.
|
|
18
|
+
*/
|
|
19
|
+
/** Reassemble a skeleton with one resolved text per region ("" = the region
|
|
20
|
+
* resolves to nothing). The counterpart of a region-scoped mediation. */
|
|
21
|
+
export function spliceSkeleton(skeleton, resolved) {
|
|
22
|
+
const lines = [];
|
|
23
|
+
for (const part of skeleton) {
|
|
24
|
+
if (part.kind === "lines") {
|
|
25
|
+
lines.push(...part.lines);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
const text = resolved[part.index];
|
|
29
|
+
if (text === undefined)
|
|
30
|
+
throw new Error(`missing resolution for region ${part.index}`);
|
|
31
|
+
if (text !== "")
|
|
32
|
+
lines.push(...text.split("\n"));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return lines.join("\n");
|
|
36
|
+
}
|
|
37
|
+
const DEFAULT_MAX_LINES = 30_000;
|
|
38
|
+
const DEFAULT_MAX_EDIT_DISTANCE = 2_048;
|
|
39
|
+
/**
|
|
40
|
+
* Myers O(ND) shortest-edit-script diff at line granularity, returning
|
|
41
|
+
* base-anchored replacement hunks. Returns null when the edit distance
|
|
42
|
+
* exceeds maxD (caller bails to not-clean, which degrades to the existing
|
|
43
|
+
* theirs-file behavior).
|
|
44
|
+
*/
|
|
45
|
+
function diffLines(a, b, maxD) {
|
|
46
|
+
// Trim the common prefix/suffix first: cheap, and it keeps the Myers band
|
|
47
|
+
// proportional to the actual change, not the file.
|
|
48
|
+
let start = 0;
|
|
49
|
+
const minLen = Math.min(a.length, b.length);
|
|
50
|
+
while (start < minLen && a[start] === b[start])
|
|
51
|
+
start++;
|
|
52
|
+
let endA = a.length;
|
|
53
|
+
let endB = b.length;
|
|
54
|
+
while (endA > start && endB > start && a[endA - 1] === b[endB - 1]) {
|
|
55
|
+
endA--;
|
|
56
|
+
endB--;
|
|
57
|
+
}
|
|
58
|
+
const n = endA - start;
|
|
59
|
+
const m = endB - start;
|
|
60
|
+
if (n === 0 && m === 0)
|
|
61
|
+
return [];
|
|
62
|
+
if (n === 0)
|
|
63
|
+
return [{ aStart: start, aEnd: start, bStart: start, bEnd: endB }];
|
|
64
|
+
if (m === 0)
|
|
65
|
+
return [{ aStart: start, aEnd: endA, bStart: start, bEnd: start }];
|
|
66
|
+
const max = Math.min(n + m, maxD);
|
|
67
|
+
const offset = max;
|
|
68
|
+
const width = 2 * max + 1;
|
|
69
|
+
let v = new Int32Array(width);
|
|
70
|
+
const trace = [];
|
|
71
|
+
let foundD = -1;
|
|
72
|
+
outer: for (let d = 0; d <= max; d++) {
|
|
73
|
+
trace.push(v.slice());
|
|
74
|
+
const next = v.slice();
|
|
75
|
+
for (let k = -d; k <= d; k += 2) {
|
|
76
|
+
if (k < -max || k > max)
|
|
77
|
+
continue;
|
|
78
|
+
let x;
|
|
79
|
+
if (k === -d || (k !== d && (v[k - 1 + offset] ?? 0) < (v[k + 1 + offset] ?? 0))) {
|
|
80
|
+
x = v[k + 1 + offset] ?? 0;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
x = (v[k - 1 + offset] ?? 0) + 1;
|
|
84
|
+
}
|
|
85
|
+
let y = x - k;
|
|
86
|
+
while (x < n && y < m && a[start + x] === b[start + y]) {
|
|
87
|
+
x++;
|
|
88
|
+
y++;
|
|
89
|
+
}
|
|
90
|
+
next[k + offset] = x;
|
|
91
|
+
if (x >= n && y >= m) {
|
|
92
|
+
v = next;
|
|
93
|
+
foundD = d;
|
|
94
|
+
break outer;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
v = next;
|
|
98
|
+
}
|
|
99
|
+
if (foundD < 0)
|
|
100
|
+
return null;
|
|
101
|
+
const edits = [];
|
|
102
|
+
let x = n;
|
|
103
|
+
let y = m;
|
|
104
|
+
for (let d = foundD; d > 0; d--) {
|
|
105
|
+
const prev = trace[d];
|
|
106
|
+
if (!prev)
|
|
107
|
+
return null;
|
|
108
|
+
const k = x - y;
|
|
109
|
+
let prevK;
|
|
110
|
+
if (k === -d || (k !== d && (prev[k - 1 + offset] ?? 0) < (prev[k + 1 + offset] ?? 0))) {
|
|
111
|
+
prevK = k + 1;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
prevK = k - 1;
|
|
115
|
+
}
|
|
116
|
+
const prevX = prev[prevK + offset] ?? 0;
|
|
117
|
+
const prevY = prevX - prevK;
|
|
118
|
+
if (prevK === k + 1) {
|
|
119
|
+
// Vertical move: b[prevY] was inserted at base position prevX.
|
|
120
|
+
edits.push({ type: "ins", x: prevX, y: prevY });
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Horizontal move: a[prevX] was deleted.
|
|
124
|
+
edits.push({ type: "del", x: prevX, y: prevY });
|
|
125
|
+
}
|
|
126
|
+
x = prevX;
|
|
127
|
+
y = prevY;
|
|
128
|
+
}
|
|
129
|
+
edits.reverse();
|
|
130
|
+
const hunks = [];
|
|
131
|
+
let current = null;
|
|
132
|
+
let pathX = -1;
|
|
133
|
+
let pathY = -1;
|
|
134
|
+
for (const edit of edits) {
|
|
135
|
+
const contiguous = current !== null && edit.x === pathX && edit.y === pathY;
|
|
136
|
+
if (!contiguous) {
|
|
137
|
+
if (current)
|
|
138
|
+
hunks.push(current);
|
|
139
|
+
current = { aStart: edit.x, aEnd: edit.x, bStart: edit.y, bEnd: edit.y };
|
|
140
|
+
}
|
|
141
|
+
if (!current)
|
|
142
|
+
continue;
|
|
143
|
+
if (edit.type === "del") {
|
|
144
|
+
current.aEnd = edit.x + 1;
|
|
145
|
+
pathX = edit.x + 1;
|
|
146
|
+
pathY = edit.y;
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
current.bEnd = edit.y + 1;
|
|
150
|
+
pathX = edit.x;
|
|
151
|
+
pathY = edit.y + 1;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (current)
|
|
155
|
+
hunks.push(current);
|
|
156
|
+
// Re-anchor to full coordinates (the prefix trim shifted everything).
|
|
157
|
+
return hunks.map((hunk) => ({
|
|
158
|
+
aStart: hunk.aStart + start,
|
|
159
|
+
aEnd: hunk.aEnd + start,
|
|
160
|
+
bStart: hunk.bStart + start,
|
|
161
|
+
bEnd: hunk.bEnd + start,
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
164
|
+
/** A side's replacement lines for base range [aStart,aEnd), given the side's
|
|
165
|
+
* cluster hunks (linear offset mapping holds between hunks). */
|
|
166
|
+
function sideSlice(sideLines, baseLines, hunks, aStart, aEnd) {
|
|
167
|
+
if (hunks.length === 0)
|
|
168
|
+
return baseLines.slice(aStart, aEnd);
|
|
169
|
+
const first = hunks[0];
|
|
170
|
+
const last = hunks[hunks.length - 1];
|
|
171
|
+
const sStart = first.bStart - (first.aStart - aStart);
|
|
172
|
+
const sEnd = last.bEnd + (aEnd - last.aEnd);
|
|
173
|
+
return sideLines.slice(sStart, sEnd);
|
|
174
|
+
}
|
|
175
|
+
function sameLines(a, b) {
|
|
176
|
+
if (a.length !== b.length)
|
|
177
|
+
return false;
|
|
178
|
+
for (let i = 0; i < a.length; i++) {
|
|
179
|
+
if (a[i] !== b[i])
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
function notClean(base, mine, theirs) {
|
|
185
|
+
return {
|
|
186
|
+
clean: false,
|
|
187
|
+
merged: "",
|
|
188
|
+
regions: [{ base, mine, theirs }],
|
|
189
|
+
skeleton: [{ kind: "region", index: 0 }],
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
export function merge3(base, mine, theirs, options = {}) {
|
|
193
|
+
if (mine === theirs)
|
|
194
|
+
return { clean: true, merged: mine, regions: [] };
|
|
195
|
+
if (base === mine)
|
|
196
|
+
return { clean: true, merged: theirs, regions: [] };
|
|
197
|
+
if (base === theirs)
|
|
198
|
+
return { clean: true, merged: mine, regions: [] };
|
|
199
|
+
const maxLines = options.maxLines ?? DEFAULT_MAX_LINES;
|
|
200
|
+
const maxD = options.maxEditDistance ?? DEFAULT_MAX_EDIT_DISTANCE;
|
|
201
|
+
const baseLines = base.split("\n");
|
|
202
|
+
const mineLines = mine.split("\n");
|
|
203
|
+
const theirsLines = theirs.split("\n");
|
|
204
|
+
if (baseLines.length > maxLines || mineLines.length > maxLines || theirsLines.length > maxLines) {
|
|
205
|
+
return notClean(baseLines, mineLines, theirsLines);
|
|
206
|
+
}
|
|
207
|
+
const mineDiff = diffLines(baseLines, mineLines, maxD);
|
|
208
|
+
const theirsDiff = diffLines(baseLines, theirsLines, maxD);
|
|
209
|
+
if (!mineDiff || !theirsDiff)
|
|
210
|
+
return notClean(baseLines, mineLines, theirsLines);
|
|
211
|
+
const out = [];
|
|
212
|
+
const regions = [];
|
|
213
|
+
const skeleton = [];
|
|
214
|
+
const emitLines = (lines) => {
|
|
215
|
+
if (lines.length === 0)
|
|
216
|
+
return;
|
|
217
|
+
out.push(...lines);
|
|
218
|
+
const last = skeleton[skeleton.length - 1];
|
|
219
|
+
if (last?.kind === "lines")
|
|
220
|
+
last.lines.push(...lines);
|
|
221
|
+
else
|
|
222
|
+
skeleton.push({ kind: "lines", lines: [...lines] });
|
|
223
|
+
};
|
|
224
|
+
let cursor = 0;
|
|
225
|
+
let mi = 0;
|
|
226
|
+
let ti = 0;
|
|
227
|
+
while (mi < mineDiff.length || ti < theirsDiff.length) {
|
|
228
|
+
const nextMine = mineDiff[mi];
|
|
229
|
+
const nextTheirs = theirsDiff[ti];
|
|
230
|
+
const clusterStart = Math.min(nextMine ? nextMine.aStart : Number.POSITIVE_INFINITY, nextTheirs ? nextTheirs.aStart : Number.POSITIVE_INFINITY);
|
|
231
|
+
// Copy the untouched base span before the next change.
|
|
232
|
+
emitLines(baseLines.slice(cursor, clusterStart));
|
|
233
|
+
cursor = clusterStart;
|
|
234
|
+
// Grow the cluster while hunks from either side touch it. Touching (not
|
|
235
|
+
// just overlapping) is deliberate: adjacent edits conflict, like git.
|
|
236
|
+
let clusterEnd = clusterStart;
|
|
237
|
+
const mineFirst = mi;
|
|
238
|
+
const theirsFirst = ti;
|
|
239
|
+
let grew = true;
|
|
240
|
+
while (grew) {
|
|
241
|
+
grew = false;
|
|
242
|
+
while (mi < mineDiff.length && mineDiff[mi].aStart <= clusterEnd) {
|
|
243
|
+
clusterEnd = Math.max(clusterEnd, mineDiff[mi].aEnd);
|
|
244
|
+
mi++;
|
|
245
|
+
grew = true;
|
|
246
|
+
}
|
|
247
|
+
while (ti < theirsDiff.length && theirsDiff[ti].aStart <= clusterEnd) {
|
|
248
|
+
clusterEnd = Math.max(clusterEnd, theirsDiff[ti].aEnd);
|
|
249
|
+
ti++;
|
|
250
|
+
grew = true;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const mineHunks = mineDiff.slice(mineFirst, mi);
|
|
254
|
+
const theirsHunks = theirsDiff.slice(theirsFirst, ti);
|
|
255
|
+
const mineChunk = sideSlice(mineLines, baseLines, mineHunks, clusterStart, clusterEnd);
|
|
256
|
+
const theirsChunk = sideSlice(theirsLines, baseLines, theirsHunks, clusterStart, clusterEnd);
|
|
257
|
+
if (theirsHunks.length === 0) {
|
|
258
|
+
emitLines(mineChunk);
|
|
259
|
+
}
|
|
260
|
+
else if (mineHunks.length === 0) {
|
|
261
|
+
emitLines(theirsChunk);
|
|
262
|
+
}
|
|
263
|
+
else if (sameLines(mineChunk, theirsChunk)) {
|
|
264
|
+
emitLines(mineChunk);
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
skeleton.push({ kind: "region", index: regions.length });
|
|
268
|
+
regions.push({
|
|
269
|
+
base: baseLines.slice(clusterStart, clusterEnd),
|
|
270
|
+
mine: mineChunk,
|
|
271
|
+
theirs: theirsChunk,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
cursor = clusterEnd;
|
|
275
|
+
}
|
|
276
|
+
emitLines(baseLines.slice(cursor));
|
|
277
|
+
if (regions.length > 0)
|
|
278
|
+
return { clean: false, merged: "", regions, skeleton };
|
|
279
|
+
return { clean: true, merged: out.join("\n"), regions: [] };
|
|
280
|
+
}
|
package/dist/names.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const RESERVED_ROOM_PREFIXES: string[];
|
|
2
|
+
export declare function normalizeRoomNameInput(raw: string): string;
|
|
3
|
+
export declare function normalizeCommunityRoomName(raw: string): string;
|
|
4
|
+
export declare function assertCommunityRoomName(name: string): void;
|
|
5
|
+
export declare function normalizeWorkspaceId(raw: string): string;
|
|
6
|
+
export declare function workspaceRoomName(workspaceId: string): string;
|
package/dist/names.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const RESERVED_ROOM_PREFIXES = ["workspace-"];
|
|
2
|
+
const ROOM_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,78}[a-z0-9])?$/;
|
|
3
|
+
const WORKSPACE_ID_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,126}[A-Za-z0-9])?$/;
|
|
4
|
+
export function normalizeRoomNameInput(raw) {
|
|
5
|
+
return raw.trim().replace(/^#/, "").toLowerCase();
|
|
6
|
+
}
|
|
7
|
+
export function normalizeCommunityRoomName(raw) {
|
|
8
|
+
const name = normalizeRoomNameInput(raw);
|
|
9
|
+
assertCommunityRoomName(name);
|
|
10
|
+
return name;
|
|
11
|
+
}
|
|
12
|
+
export function assertCommunityRoomName(name) {
|
|
13
|
+
assertRoomSlug(name, "Room name");
|
|
14
|
+
if (RESERVED_ROOM_PREFIXES.some((prefix) => name.startsWith(prefix))) {
|
|
15
|
+
throw new Error("Room names cannot use the reserved workspace- prefix");
|
|
16
|
+
}
|
|
17
|
+
if (/^room-[a-f0-9]{12}$/.test(name)) {
|
|
18
|
+
throw new Error("Room names cannot use the reserved generated room id format");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function normalizeWorkspaceId(raw) {
|
|
22
|
+
const id = raw.trim();
|
|
23
|
+
if (!WORKSPACE_ID_PATTERN.test(id)) {
|
|
24
|
+
throw new Error("Workspace ID must contain only letters, numbers, and hyphens");
|
|
25
|
+
}
|
|
26
|
+
return id;
|
|
27
|
+
}
|
|
28
|
+
export function workspaceRoomName(workspaceId) {
|
|
29
|
+
return `workspace-${normalizeWorkspaceId(workspaceId).toLowerCase()}`;
|
|
30
|
+
}
|
|
31
|
+
function assertRoomSlug(value, label) {
|
|
32
|
+
if (!ROOM_NAME_PATTERN.test(value)) {
|
|
33
|
+
throw new Error(`${label} must be 1-80 characters, start and end with a letter or number, and contain only lowercase letters, numbers, and hyphens`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type EmojiCategory = "smileys" | "people" | "nature" | "food" | "travel" | "activities" | "objects" | "symbols" | "flags";
|
|
2
|
+
export interface EmojiEntry {
|
|
3
|
+
/** The native glyph, e.g. "🔥" */
|
|
4
|
+
glyph: string;
|
|
5
|
+
/** Canonical shortcode without colons, e.g. "fire" */
|
|
6
|
+
shortcode: string;
|
|
7
|
+
/** Search keywords (lowercase), including the shortcode */
|
|
8
|
+
keywords: string[];
|
|
9
|
+
/** One of the 9 category keys in EMOJI_CATEGORIES */
|
|
10
|
+
category: EmojiCategory;
|
|
11
|
+
}
|
|
12
|
+
/** Picker display order for categories; matches the dataset's sort order. */
|
|
13
|
+
export declare const EMOJI_CATEGORIES: readonly EmojiCategory[];
|
|
14
|
+
export declare const EMOJI: readonly EmojiEntry[];
|
|
15
|
+
/** The hover-toolbar reaction trio; every glyph exists in EMOJI. */
|
|
16
|
+
export declare const QUICK_REACTIONS: readonly string[];
|
|
17
|
+
/**
|
|
18
|
+
* Prefix + substring search over shortcodes and keywords. Shortcode-prefix
|
|
19
|
+
* matches rank first (shortest shortcode wins ties), then keyword-prefix,
|
|
20
|
+
* then shortcode/keyword substring matches.
|
|
21
|
+
*/
|
|
22
|
+
export declare function searchEmoji(query: string, limit?: number): EmojiEntry[];
|