@jchat/addon-sdk 1.0.0-alpha.20 → 1.0.0-alpha.21
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/CHANGELOG.md +20 -0
- package/dist/contracts.d.mts +23 -2
- package/dist/contracts.mjs +5 -3
- package/dist/index.mjs +27 -2
- package/dist/index2.d.mts +151 -28
- package/dist/vite.mjs +14 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @jchat/addon-sdk
|
|
2
2
|
|
|
3
|
+
## 1.0.0-alpha.21
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add message lifecycle interception hooks and chat message card mount point support
|
|
8
|
+
|
|
9
|
+
- Add TypeScript typings for available editor commands in Addon SDK
|
|
10
|
+
|
|
11
|
+
- Add verifyAddonEntitlement runtime helper and AddonServerContext method
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Remove redundant boilerplate test files
|
|
16
|
+
|
|
17
|
+
- Enhance add-on CSS compilation to ensure consistent styling across dialogs and portals
|
|
18
|
+
|
|
19
|
+
- Bumped `@jchat/*` dependencies:
|
|
20
|
+
- [`icon@1.0.0-alpha.21`](https://github.com/mjjabarullah/jchat/releases/tag/icon@v1.0.0-alpha.21)
|
|
21
|
+
- [`ui@1.0.0-alpha.21`](https://github.com/mjjabarullah/jchat/releases/tag/ui@v1.0.0-alpha.21)
|
|
22
|
+
|
|
3
23
|
## 1.0.0-alpha.20
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
package/dist/contracts.d.mts
CHANGED
|
@@ -36,15 +36,34 @@ export declare const MOUNT_POINTS: {
|
|
|
36
36
|
readonly EMOJI_PICKER_GIF_TAB: "emoji-picker-gif-tab";
|
|
37
37
|
/** Custom tab in the emoji/sticker selection panel */
|
|
38
38
|
readonly EMOJI_PICKER_STICKER_TAB: "emoji-picker-sticker-tab";
|
|
39
|
-
/** Full-screen or modal dialog specifically for TubeScan */
|
|
40
|
-
readonly TUBESCAN_DIALOG: "tubescan-dialog";
|
|
41
39
|
/** Dedicated settings panel rendered in the Owner Dashboard */
|
|
42
40
|
readonly OWNER_SETTINGS: "owner-settings";
|
|
43
41
|
/** Dedicated settings panel rendered in the Site Admin Dashboard */
|
|
44
42
|
readonly ADDON_SETTINGS: "addon-settings";
|
|
43
|
+
/** Custom interactive card rendered inside an enriched chat message */
|
|
44
|
+
readonly CHAT_MESSAGE_CARD: "chat-message-card";
|
|
45
|
+
/** Custom badge, audio equalizer, or particle aura rendered on a chat bubble */
|
|
46
|
+
readonly CHAT_MESSAGE_FLAIR: "chat-message-flair";
|
|
45
47
|
};
|
|
46
48
|
/** Union type of all valid addon mount points. */
|
|
47
49
|
export type MountPoint = (typeof MOUNT_POINTS)[keyof typeof MOUNT_POINTS];
|
|
50
|
+
/** Parameter specification for an addon slash command. */
|
|
51
|
+
export interface AddonCommandParameter {
|
|
52
|
+
name: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
required?: boolean;
|
|
55
|
+
}
|
|
56
|
+
/** Declaration of an addon slash command available in the chat composer. */
|
|
57
|
+
export interface AddonCommandDefinition {
|
|
58
|
+
/** The command keyword without leading slash (e.g. 'yt', 'roll', 'poll') */
|
|
59
|
+
name: string;
|
|
60
|
+
/** Short summary of what the command does, displayed in autocomplete */
|
|
61
|
+
description: string;
|
|
62
|
+
/** Usage syntax displayed as a hint (e.g. '<query | url>') */
|
|
63
|
+
syntax?: string;
|
|
64
|
+
/** Parameters accepted by this command */
|
|
65
|
+
parameters?: AddonCommandParameter[];
|
|
66
|
+
}
|
|
48
67
|
/**
|
|
49
68
|
* The `manifest.json` schema required for every Jchat addon. This file describes how the addon is
|
|
50
69
|
* identified and mounted.
|
|
@@ -117,5 +136,7 @@ export interface AddonManifest {
|
|
|
117
136
|
permissions?: string[];
|
|
118
137
|
/** Custom permissions that should be registered with the host application on installation */
|
|
119
138
|
permissionDefinitions?: AddonPermissionDefinition[];
|
|
139
|
+
/** Interactive slash commands exposed in the chat composer */
|
|
140
|
+
commands?: AddonCommandDefinition[];
|
|
120
141
|
}
|
|
121
142
|
//#endregion
|
package/dist/contracts.mjs
CHANGED
|
@@ -27,12 +27,14 @@ const MOUNT_POINTS = {
|
|
|
27
27
|
EMOJI_PICKER_GIF_TAB: "emoji-picker-gif-tab",
|
|
28
28
|
/** Custom tab in the emoji/sticker selection panel */
|
|
29
29
|
EMOJI_PICKER_STICKER_TAB: "emoji-picker-sticker-tab",
|
|
30
|
-
/** Full-screen or modal dialog specifically for TubeScan */
|
|
31
|
-
TUBESCAN_DIALOG: "tubescan-dialog",
|
|
32
30
|
/** Dedicated settings panel rendered in the Owner Dashboard */
|
|
33
31
|
OWNER_SETTINGS: "owner-settings",
|
|
34
32
|
/** Dedicated settings panel rendered in the Site Admin Dashboard */
|
|
35
|
-
ADDON_SETTINGS: "addon-settings"
|
|
33
|
+
ADDON_SETTINGS: "addon-settings",
|
|
34
|
+
/** Custom interactive card rendered inside an enriched chat message */
|
|
35
|
+
CHAT_MESSAGE_CARD: "chat-message-card",
|
|
36
|
+
/** Custom badge, audio equalizer, or particle aura rendered on a chat bubble */
|
|
37
|
+
CHAT_MESSAGE_FLAIR: "chat-message-flair"
|
|
36
38
|
};
|
|
37
39
|
//#endregion
|
|
38
40
|
export { MOUNT_POINTS };
|
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { MOUNT_POINTS } from "./contracts.mjs";
|
|
|
2
2
|
import { a as useTranslation, i as useLocale, n as LocaleContext, r as createTranslator, t as HOST_LANGUAGES } from "./i18n.mjs";
|
|
3
3
|
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
4
|
//#region package.json
|
|
5
|
-
var version = "1.0.0-alpha.
|
|
5
|
+
var version = "1.0.0-alpha.21";
|
|
6
6
|
//#endregion
|
|
7
7
|
//#region src/query.ts
|
|
8
8
|
/**
|
|
@@ -145,5 +145,30 @@ function useAddonSDK() {
|
|
|
145
145
|
function getAddonSdkVersion() {
|
|
146
146
|
return version;
|
|
147
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Verifies purchase entitlement and identity integrity for commercial addons. Throws an error if
|
|
150
|
+
* the addon fails entitlement verification or if its manifest ID has been tampered with to
|
|
151
|
+
* impersonate another addon.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ;```ts
|
|
155
|
+
* export default defineAddonServer((context, app) => {
|
|
156
|
+
* verifyAddonEntitlement("tubescanpro", context)
|
|
157
|
+
* return app.get("/trending", async () => { ... })
|
|
158
|
+
* })
|
|
159
|
+
* ```
|
|
160
|
+
*
|
|
161
|
+
* @param addonId - The canonical addon ID defined by the publisher (e.g. 'tubescanpro')
|
|
162
|
+
* @param context - The AddonServerContext passed to the addon's server factory
|
|
163
|
+
* @returns True if valid
|
|
164
|
+
* @throws Error if entitlement verification fails
|
|
165
|
+
*/
|
|
166
|
+
function verifyAddonEntitlement(addonId, context) {
|
|
167
|
+
if (context?.verifyEntitlement) {
|
|
168
|
+
if (!context.verifyEntitlement(addonId)) throw new Error(`addon_entitlement_verification_failed:${addonId}`);
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
148
173
|
//#endregion
|
|
149
|
-
export { HOST_LANGUAGES, LocaleContext, MOUNT_POINTS, createTranslator, defineAddonManifest, defineAddonServer, getAddonSdkVersion, useAddonInfiniteQuery, useAddonMutation, useAddonQuery, useAddonSDK, useLocale, useQueryClient, useTranslation };
|
|
174
|
+
export { HOST_LANGUAGES, LocaleContext, MOUNT_POINTS, createTranslator, defineAddonManifest, defineAddonServer, getAddonSdkVersion, useAddonInfiniteQuery, useAddonMutation, useAddonQuery, useAddonSDK, useLocale, useQueryClient, useTranslation, verifyAddonEntitlement };
|
package/dist/index2.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as IconPack, n as IconComponent, r as IconName } from "./types.mjs";
|
|
2
|
-
import { AddonManifest, AddonPermissionDefinition, MOUNT_POINTS, MountPoint } from "./contracts.mjs";
|
|
2
|
+
import { AddonCommandDefinition, AddonCommandParameter, AddonManifest, AddonPermissionDefinition, MOUNT_POINTS, MountPoint } from "./contracts.mjs";
|
|
3
3
|
import { InfiniteData, MutationFunction, QueryFunction, QueryFunctionContext, QueryKey, UndefinedInitialDataInfiniteOptions, UndefinedInitialDataOptions, UseInfiniteQueryResult, UseMutationOptions, UseMutationResult, UseQueryResult, useQueryClient } from "@tanstack/react-query";
|
|
4
4
|
import { Elysia, t } from "elysia";
|
|
5
5
|
import { ColumnType, Generated, Insertable, JSONColumnType, Kysely, Selectable, Updateable } from "kysely";
|
|
@@ -209,6 +209,61 @@ export interface AddonServerEmitTarget {
|
|
|
209
209
|
/** Target a specific user */
|
|
210
210
|
userId?: string;
|
|
211
211
|
}
|
|
212
|
+
export interface AddonMessageMutableData {
|
|
213
|
+
/** Message content (can be Slate JSON structure or string) */
|
|
214
|
+
content: unknown;
|
|
215
|
+
/** Optional message attachments */
|
|
216
|
+
attachments?: unknown[] | null;
|
|
217
|
+
/**
|
|
218
|
+
* Namespaced metadata bucket for this addon. Keyed as `metadata["addon:<addonId>"]`. Storing data
|
|
219
|
+
* here automatically attaches it to the message broadcast and database record.
|
|
220
|
+
*/
|
|
221
|
+
metadata: Record<string, unknown>;
|
|
222
|
+
}
|
|
223
|
+
export interface AddonMessageContext {
|
|
224
|
+
/** The site ID where the message is being sent */
|
|
225
|
+
siteId: string;
|
|
226
|
+
/** The chat room ID */
|
|
227
|
+
roomId: string;
|
|
228
|
+
/** The authenticated user sending the message (read-only) */
|
|
229
|
+
user: AddonRequestUser;
|
|
230
|
+
/** Mutable message payload */
|
|
231
|
+
message: AddonMessageMutableData;
|
|
232
|
+
/** Convenience method to set metadata under this addon's namespace */
|
|
233
|
+
setMetadata: (key: string, value: unknown) => void;
|
|
234
|
+
/**
|
|
235
|
+
* Abort the message sending pipeline with a user-facing error code and reason. Only active during
|
|
236
|
+
* `beforeValidate` and `transformContent`.
|
|
237
|
+
*/
|
|
238
|
+
abort: (error: {
|
|
239
|
+
code: string;
|
|
240
|
+
reason?: string;
|
|
241
|
+
}) => never;
|
|
242
|
+
}
|
|
243
|
+
export interface AddonMessageAfterSaveContext {
|
|
244
|
+
siteId: string;
|
|
245
|
+
roomId: string;
|
|
246
|
+
user: AddonRequestUser;
|
|
247
|
+
message: {
|
|
248
|
+
id: string;
|
|
249
|
+
content: unknown;
|
|
250
|
+
attachments?: unknown[] | null;
|
|
251
|
+
metadata?: Record<string, unknown> | null;
|
|
252
|
+
type: string;
|
|
253
|
+
createdAt: Date | string;
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
export interface AddonPipelineHooks {
|
|
257
|
+
/**
|
|
258
|
+
* Run before validation to inspect or guard actions (rate-limits, permission gates). Throw or
|
|
259
|
+
* call `context.abort()` to reject the message.
|
|
260
|
+
*/
|
|
261
|
+
beforeValidate?: (context: AddonMessageContext) => Promise<void> | void;
|
|
262
|
+
/** Run before persistence to mutate text, parse slash commands, or enrich metadata. */
|
|
263
|
+
transformContent?: (context: AddonMessageContext) => Promise<void> | void;
|
|
264
|
+
/** Run asynchronously after database save (fire-and-forget observers). */
|
|
265
|
+
afterSave?: (context: AddonMessageAfterSaveContext) => Promise<void> | void;
|
|
266
|
+
}
|
|
212
267
|
/**
|
|
213
268
|
* Context provided to an addon's server-side factory. Parameterize it with the addon's table
|
|
214
269
|
* interface: `AddonServerContext<MyAddonDatabase>`.
|
|
@@ -249,6 +304,21 @@ export interface AddonServerContext<TDatabase = Record<string, Record<string, un
|
|
|
249
304
|
* `sdk.events.on('addon:<addonId>:<event>', callback)` or `sdk.events.on(event, callback)`.
|
|
250
305
|
*/
|
|
251
306
|
emit: (event: string, payload: unknown, target?: AddonServerEmitTarget) => Promise<void>;
|
|
307
|
+
/**
|
|
308
|
+
* Register lifecycle interception hooks for chat messages. Hooks run in the core chat execution
|
|
309
|
+
* path with strict 50ms timeout guards and fail-open fault tolerance.
|
|
310
|
+
*/
|
|
311
|
+
pipeline?: {
|
|
312
|
+
register: (hooks: AddonPipelineHooks) => void;
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* Cryptographically verify that this addon instance holds a valid host license entitlement.
|
|
316
|
+
* Commercial paid addons should invoke this or call `verifyAddonEntitlement(addonId, context)`.
|
|
317
|
+
*
|
|
318
|
+
* @param expectedAddonId - The canonical addon ID defined by the publisher
|
|
319
|
+
* @returns True if valid, or false if entitlement is missing or invalid
|
|
320
|
+
*/
|
|
321
|
+
verifyEntitlement?: (expectedAddonId?: string) => boolean;
|
|
252
322
|
}
|
|
253
323
|
/**
|
|
254
324
|
* Elysia app instance provided to addon server endpoints with Jchat macro metadata (e.g.
|
|
@@ -309,6 +379,10 @@ export interface AddonProps {
|
|
|
309
379
|
locale?: string;
|
|
310
380
|
/** Text direction for the current locale */
|
|
311
381
|
dir?: "ltr" | "rtl";
|
|
382
|
+
/** The message object when rendered inside 'chat-message-card' or 'chat-message-flair' */
|
|
383
|
+
message?: unknown;
|
|
384
|
+
/** The addon's specific payload extracted from message.metadata */
|
|
385
|
+
addonData?: unknown;
|
|
312
386
|
}
|
|
313
387
|
export interface AddonApi {
|
|
314
388
|
get: <TResponse = unknown>(path: string) => Promise<TResponse>;
|
|
@@ -337,6 +411,59 @@ export interface AddonFont {
|
|
|
337
411
|
/** Relative size multiplier — apply this to base font sizes to match the host's rendering */
|
|
338
412
|
scale: number;
|
|
339
413
|
}
|
|
414
|
+
export interface AddonEditorInsertImagePayload {
|
|
415
|
+
src: string;
|
|
416
|
+
altText: string;
|
|
417
|
+
emoji?: {
|
|
418
|
+
col: number;
|
|
419
|
+
row: number;
|
|
420
|
+
size: number;
|
|
421
|
+
};
|
|
422
|
+
className?: string;
|
|
423
|
+
}
|
|
424
|
+
export interface AddonEditorInsertEmojiPayload {
|
|
425
|
+
col: number;
|
|
426
|
+
row: number;
|
|
427
|
+
unicode: string;
|
|
428
|
+
shortcode?: string;
|
|
429
|
+
}
|
|
430
|
+
export interface AddonEditorCommandMap {
|
|
431
|
+
INSERT_YOUTUBE_COMMAND: string;
|
|
432
|
+
INSERT_TEXT: string;
|
|
433
|
+
INSERT_TEXT_COMMAND: string;
|
|
434
|
+
INSERT_MENTION_COMMAND: string;
|
|
435
|
+
INSERT_IMAGE_COMMAND: AddonEditorInsertImagePayload;
|
|
436
|
+
INSERT_EMOJI_COMMAND: AddonEditorInsertEmojiPayload;
|
|
437
|
+
CLEAR_EDITOR: void;
|
|
438
|
+
CLEAR_EDITOR_COMMAND: void;
|
|
439
|
+
}
|
|
440
|
+
export type AddonEditorCommand = keyof AddonEditorCommandMap;
|
|
441
|
+
export interface AddonEditorApi {
|
|
442
|
+
/**
|
|
443
|
+
* Trigger a specialized editor command defined by the host.
|
|
444
|
+
*
|
|
445
|
+
* @example
|
|
446
|
+
* sdk.editor.dispatchCommand("INSERT_YOUTUBE_COMMAND", "dQw4w9WgXcQ")
|
|
447
|
+
*/
|
|
448
|
+
dispatchCommand: <C extends AddonEditorCommand>(command: C, ...args: AddonEditorCommandMap[C] extends void ? [payload?: undefined] : [payload: AddonEditorCommandMap[C]]) => void;
|
|
449
|
+
/**
|
|
450
|
+
* Insert a low-level Slate node directly at the user's cursor position.
|
|
451
|
+
*
|
|
452
|
+
* @param node - A valid Slate Descendant or Fragment object
|
|
453
|
+
*/
|
|
454
|
+
insertNode: (node: unknown) => void;
|
|
455
|
+
/**
|
|
456
|
+
* Upload and attach a file to the current message without inserting it inline. Users will see the
|
|
457
|
+
* file in the attachment bar below the editor.
|
|
458
|
+
*/
|
|
459
|
+
addAttachment: (attachment: {
|
|
460
|
+
url: string;
|
|
461
|
+
name: string;
|
|
462
|
+
type: string;
|
|
463
|
+
}) => void;
|
|
464
|
+
/** Force the browser focus back to the editor input */
|
|
465
|
+
focus: () => void;
|
|
466
|
+
}
|
|
340
467
|
/**
|
|
341
468
|
* The bridge between your addon and the Jchat host application. Use the `useAddonSDK()` hook to
|
|
342
469
|
* access this interface in your components.
|
|
@@ -370,32 +497,7 @@ export interface AddonSDK {
|
|
|
370
497
|
* Interaction utilities for the active Slate message editor. Only available if your addon is
|
|
371
498
|
* mounted near the chat input.
|
|
372
499
|
*/
|
|
373
|
-
editor:
|
|
374
|
-
/**
|
|
375
|
-
* Trigger a specialized editor command defined by the host.
|
|
376
|
-
*
|
|
377
|
-
* @example
|
|
378
|
-
* sdk.editor.dispatchCommand("INSERT_YOUTUBE_COMMAND", "dQw4w9WgXcQ")
|
|
379
|
-
*/
|
|
380
|
-
dispatchCommand: (command: string, payload?: unknown) => void;
|
|
381
|
-
/**
|
|
382
|
-
* Insert a low-level Slate node directly at the user's cursor position.
|
|
383
|
-
*
|
|
384
|
-
* @param node - A valid Slate Descendant or Fragment object
|
|
385
|
-
*/
|
|
386
|
-
insertNode: (node: unknown) => void;
|
|
387
|
-
/**
|
|
388
|
-
* Upload and attach a file to the current message without inserting it inline. Users will see
|
|
389
|
-
* the file in the attachment bar below the editor.
|
|
390
|
-
*/
|
|
391
|
-
addAttachment: (attachment: {
|
|
392
|
-
url: string;
|
|
393
|
-
name: string;
|
|
394
|
-
type: string;
|
|
395
|
-
}) => void;
|
|
396
|
-
/** Force the browser focus back to the editor input */
|
|
397
|
-
focus: () => void;
|
|
398
|
-
};
|
|
500
|
+
editor: AddonEditorApi;
|
|
399
501
|
/** Event Bus for reacting to real-time application state changes. */
|
|
400
502
|
events: {
|
|
401
503
|
/**
|
|
@@ -474,5 +576,26 @@ export declare function useAddonSDK(): AddonSDK;
|
|
|
474
576
|
* @returns
|
|
475
577
|
*/
|
|
476
578
|
export declare function getAddonSdkVersion(): string;
|
|
579
|
+
/**
|
|
580
|
+
* Verifies purchase entitlement and identity integrity for commercial addons. Throws an error if
|
|
581
|
+
* the addon fails entitlement verification or if its manifest ID has been tampered with to
|
|
582
|
+
* impersonate another addon.
|
|
583
|
+
*
|
|
584
|
+
* @example
|
|
585
|
+
* ;```ts
|
|
586
|
+
* export default defineAddonServer((context, app) => {
|
|
587
|
+
* verifyAddonEntitlement("tubescanpro", context)
|
|
588
|
+
* return app.get("/trending", async () => { ... })
|
|
589
|
+
* })
|
|
590
|
+
* ```
|
|
591
|
+
*
|
|
592
|
+
* @param addonId - The canonical addon ID defined by the publisher (e.g. 'tubescanpro')
|
|
593
|
+
* @param context - The AddonServerContext passed to the addon's server factory
|
|
594
|
+
* @returns True if valid
|
|
595
|
+
* @throws Error if entitlement verification fails
|
|
596
|
+
*/
|
|
597
|
+
export declare function verifyAddonEntitlement(addonId: string, context?: {
|
|
598
|
+
verifyEntitlement?: (expectedAddonId?: string) => boolean;
|
|
599
|
+
}): boolean;
|
|
477
600
|
//#endregion
|
|
478
|
-
export { type AddonManifest, type AddonPermissionDefinition, type ColumnType, type Generated, type IconComponent, type IconName, type IconPack, type InfiniteData, type Insertable, type JSONColumnType, MOUNT_POINTS, type MountPoint, type QueryKey, type Selectable, type Updateable, useQueryClient };
|
|
601
|
+
export { type AddonCommandDefinition, type AddonCommandParameter, type AddonManifest, type AddonPermissionDefinition, type ColumnType, type Generated, type IconComponent, type IconName, type IconPack, type InfiniteData, type Insertable, type JSONColumnType, MOUNT_POINTS, type MountPoint, type QueryKey, type Selectable, type Updateable, useQueryClient };
|
package/dist/vite.mjs
CHANGED
|
@@ -82,7 +82,7 @@ function jchatAddon(options) {
|
|
|
82
82
|
if (!fs.existsSync(buildDir)) return;
|
|
83
83
|
const bundleFile = fs.readdirSync(buildDir).find((f) => f.includes("ui") && f.endsWith(".js"));
|
|
84
84
|
if (!bundleFile) return;
|
|
85
|
-
if (options.css !== false) embedCssIntoBundle(buildDir
|
|
85
|
+
if (options.css !== false) embedCssIntoBundle(buildDir);
|
|
86
86
|
let manifest = {
|
|
87
87
|
...manifestData,
|
|
88
88
|
version: pkg.version,
|
|
@@ -243,7 +243,7 @@ function requireAddonSigningKey(signingPrivateKey, developmentBuild) {
|
|
|
243
243
|
if (developmentBuild) return void 0;
|
|
244
244
|
throw new Error("Database-bearing addon builds require JCHAT_ADDON_SIGNING_PRIVATE_KEY");
|
|
245
245
|
}
|
|
246
|
-
function embedCssIntoBundle(buildDir
|
|
246
|
+
function embedCssIntoBundle(buildDir) {
|
|
247
247
|
const files = fs.readdirSync(buildDir);
|
|
248
248
|
const cssFile = files.find((f) => f.endsWith(".css"));
|
|
249
249
|
const jsFile = files.find((f) => f.includes("ui") && f.endsWith(".js"));
|
|
@@ -274,7 +274,7 @@ function embedCssIntoBundle(buildDir, addonId) {
|
|
|
274
274
|
css = css.replace(/\.user-border\s*\{[^}]+\}/g, "");
|
|
275
275
|
css = css.replace(/\.scrollbar-hide[^{]*\{[^}]+\}/g, "");
|
|
276
276
|
css = css.replace(/@layer\s+components(?:\s*\{[^}]*\}|;)/g, "");
|
|
277
|
-
css = minifyCss(
|
|
277
|
+
css = minifyCss(css);
|
|
278
278
|
}
|
|
279
279
|
if (css.trim().length > 0) {
|
|
280
280
|
const injection = `
|
|
@@ -294,12 +294,14 @@ function embedCssIntoBundle(buildDir, addonId) {
|
|
|
294
294
|
} catch {}
|
|
295
295
|
}
|
|
296
296
|
function stripLayer(css, layerName) {
|
|
297
|
-
const
|
|
298
|
-
let
|
|
299
|
-
while (
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
297
|
+
const regex = new RegExp(`@layer\\s+${layerName}\\s*\\{`);
|
|
298
|
+
let match = regex.exec(css);
|
|
299
|
+
while (match) {
|
|
300
|
+
const startIdx = match.index;
|
|
301
|
+
const openBraceIdx = startIdx + match[0].length - 1;
|
|
302
|
+
let braceCount = 1;
|
|
303
|
+
let endIdx = -1;
|
|
304
|
+
for (let i = openBraceIdx + 1; i < css.length; i++) if (css[i] === "{") braceCount++;
|
|
303
305
|
else if (css[i] === "}") {
|
|
304
306
|
braceCount--;
|
|
305
307
|
if (braceCount === 0) {
|
|
@@ -307,8 +309,9 @@ function stripLayer(css, layerName) {
|
|
|
307
309
|
break;
|
|
308
310
|
}
|
|
309
311
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
+
if (endIdx === -1) break;
|
|
313
|
+
css = css.slice(0, startIdx) + css.slice(endIdx + 1);
|
|
314
|
+
match = regex.exec(css);
|
|
312
315
|
}
|
|
313
316
|
return css;
|
|
314
317
|
}
|