@lumiastream/ui 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +344 -15
- package/dist/index.js +3098 -179
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
2
|
+
import { ButtonProps } from '@mui/material/Button';
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { ReactNode, CSSProperties, JSX, HTMLAttributes, ComponentType } from 'react';
|
|
5
|
+
import { TextFieldProps } from '@mui/material/TextField';
|
|
5
6
|
import { SelectProps } from '@mui/material/Select';
|
|
6
7
|
import { CheckboxProps } from '@mui/material/Checkbox';
|
|
7
8
|
import { FieldValues, Path } from 'react-hook-form';
|
|
@@ -10,20 +11,32 @@ import { RadioGroupProps } from '@mui/material/RadioGroup';
|
|
|
10
11
|
import { AutocompleteRenderOptionState } from '@mui/material/Autocomplete';
|
|
11
12
|
import { CompletionContext } from '@codemirror/autocomplete';
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
type LSButtonColor = 'inherit' | 'primary' | 'secondary' | 'error' | 'neutral';
|
|
15
|
+
type LSButtonVariant = 'text' | 'outlined' | 'contained';
|
|
16
|
+
type LSButtonProps = Omit<ButtonProps, 'children' | 'color' | 'variant' | 'size'> & {
|
|
17
|
+
/** Compatibility alias for color="primary". */
|
|
15
18
|
primary?: boolean;
|
|
16
|
-
/**
|
|
19
|
+
/** Compatibility style override for the button background. */
|
|
17
20
|
backgroundColor?: string;
|
|
21
|
+
/** Loading state. */
|
|
22
|
+
loading?: boolean;
|
|
23
|
+
/** Color of the button. */
|
|
24
|
+
color?: LSButtonColor;
|
|
18
25
|
/** How large should the button be? */
|
|
19
26
|
size?: 'small' | 'medium' | 'large';
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
/** Element placed before the children. */
|
|
28
|
+
startIcon?: ReactNode;
|
|
29
|
+
/** Element placed after the children. */
|
|
30
|
+
endIcon?: ReactNode;
|
|
31
|
+
/** The variant to use. */
|
|
32
|
+
variant?: LSButtonVariant;
|
|
33
|
+
/** Button contents. */
|
|
34
|
+
label?: ReactNode;
|
|
35
|
+
/** Adds wider horizontal padding. */
|
|
36
|
+
extended?: number;
|
|
37
|
+
style?: CSSProperties;
|
|
38
|
+
};
|
|
39
|
+
declare const LSButton: ({ label, disabled, loading, variant, extended, color, primary, backgroundColor, className, sx, style, ...props }: LSButtonProps) => react_jsx_runtime.JSX.Element;
|
|
27
40
|
|
|
28
41
|
interface LSInputPropsChild$1 {
|
|
29
42
|
id?: string;
|
|
@@ -199,7 +212,7 @@ type LSVariableInputContextValue = {
|
|
|
199
212
|
isOriginConnected?: (origin: string) => boolean;
|
|
200
213
|
translationKeyExists?: (key: string, namespace?: string) => boolean;
|
|
201
214
|
};
|
|
202
|
-
declare const LSVariableInputProvider: ({ children, value }: {
|
|
215
|
+
declare const LSVariableInputProvider: ({ children, value, }: {
|
|
203
216
|
children: ReactNode;
|
|
204
217
|
value: LSVariableInputContextValue;
|
|
205
218
|
}) => react_jsx_runtime.JSX.Element;
|
|
@@ -232,7 +245,7 @@ type LSVariableInputFieldProps = {
|
|
|
232
245
|
value?: string | number | any[];
|
|
233
246
|
placeholder?: string;
|
|
234
247
|
renderOption?: (props: HTMLAttributes<HTMLLIElement>, option: any, state: AutocompleteRenderOptionState) => ReactNode;
|
|
235
|
-
variant?:
|
|
248
|
+
variant?: "standard" | "filled" | "outlined";
|
|
236
249
|
ListboxComponent?: any;
|
|
237
250
|
onScrollListBox?: (e: any) => void;
|
|
238
251
|
onHighlightChange?: (e: any, value: any) => void;
|
|
@@ -472,4 +485,320 @@ declare const resolvePlatformChatterProfileUrl: ({ platform, username, displayna
|
|
|
472
485
|
declare const normalizeHttpUrl: (value: unknown) => string | null;
|
|
473
486
|
declare const resolveChatterProfileUrlWithResolvers: ({ platform, username, displayname, userId, extraSettings, resolvers, }: ResolveChatterProfileUrlWithResolversInput) => Promise<string | null>;
|
|
474
487
|
|
|
475
|
-
|
|
488
|
+
type ModuleType = 'alert' | 'emotebox' | 'emotealert' | 'text' | 'image' | 'audio' | 'video' | 'camera' | 'slideshow' | 'shape' | 'svg' | 'colorbox' | 'goal' | 'timer' | 'gradientlight' | 'browsersource' | 'chatbox' | 'credits' | 'eventlist' | 'eventrotate' | 'hfx' | 'hfxalert' | 'clip' | 'youtube' | 'spinwheel' | 'raffle' | 'viewerqueue' | 'loyaltyleaderboard' | 'viewerprofiles' | 'achievements' | 'poll' | 'tournaments' | 'spotify' | 'nowplaying' | 'youtubemusic' | 'brbscreen' | 'vlc' | 'tts' | 'livecaption' | 'custom' | 'group';
|
|
489
|
+
type OverlayLayerType = 'layer' | 'group';
|
|
490
|
+
type OverlayLayerBounds = {
|
|
491
|
+
height: number;
|
|
492
|
+
width: number;
|
|
493
|
+
x: number;
|
|
494
|
+
y: number;
|
|
495
|
+
scale: [number, number];
|
|
496
|
+
opacity?: number;
|
|
497
|
+
rotate?: number;
|
|
498
|
+
zIndex?: number;
|
|
499
|
+
matrix?: string;
|
|
500
|
+
clipPath?: string;
|
|
501
|
+
};
|
|
502
|
+
type OverlayLayerState = {
|
|
503
|
+
id: string;
|
|
504
|
+
group?: string | null;
|
|
505
|
+
type?: OverlayLayerType;
|
|
506
|
+
state: {
|
|
507
|
+
visible?: boolean;
|
|
508
|
+
locked?: boolean;
|
|
509
|
+
expanded?: boolean;
|
|
510
|
+
};
|
|
511
|
+
bounds: OverlayLayerBounds;
|
|
512
|
+
};
|
|
513
|
+
type OverlayModuleAlertState = {
|
|
514
|
+
events?: Record<string, unknown>;
|
|
515
|
+
[key: string]: unknown;
|
|
516
|
+
};
|
|
517
|
+
type OverlayModuleState = {
|
|
518
|
+
id?: string;
|
|
519
|
+
loaded?: boolean;
|
|
520
|
+
version?: number;
|
|
521
|
+
settings?: {
|
|
522
|
+
title?: string | null;
|
|
523
|
+
type?: ModuleType;
|
|
524
|
+
locked?: boolean;
|
|
525
|
+
};
|
|
526
|
+
lights?: unknown[];
|
|
527
|
+
css?: Record<string, string | number>;
|
|
528
|
+
content?: Record<string, unknown>;
|
|
529
|
+
variables?: Record<string, unknown>;
|
|
530
|
+
events?: Record<string, unknown>;
|
|
531
|
+
alert?: OverlayModuleAlertState;
|
|
532
|
+
};
|
|
533
|
+
type OverlaySettings = {
|
|
534
|
+
layers: OverlayLayerState[];
|
|
535
|
+
metadata: {
|
|
536
|
+
width: number;
|
|
537
|
+
height: number;
|
|
538
|
+
};
|
|
539
|
+
modules: Record<string, OverlayModuleState>;
|
|
540
|
+
};
|
|
541
|
+
type OverlayState = {
|
|
542
|
+
uuid: string;
|
|
543
|
+
listen_id: string;
|
|
544
|
+
name: string;
|
|
545
|
+
description?: string;
|
|
546
|
+
settings: OverlaySettings;
|
|
547
|
+
metadata?: {
|
|
548
|
+
width: number | null;
|
|
549
|
+
height: number | null;
|
|
550
|
+
} | null;
|
|
551
|
+
layers_count?: number | null;
|
|
552
|
+
hasFullSettings?: boolean;
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
interface SEBootstrapResponse {
|
|
556
|
+
overlay: SEOverlay;
|
|
557
|
+
session?: unknown;
|
|
558
|
+
tipping?: unknown;
|
|
559
|
+
channel?: unknown;
|
|
560
|
+
passport?: unknown;
|
|
561
|
+
}
|
|
562
|
+
interface SEOverlay {
|
|
563
|
+
_id: string;
|
|
564
|
+
channel?: string;
|
|
565
|
+
type?: string;
|
|
566
|
+
name?: string;
|
|
567
|
+
settings: {
|
|
568
|
+
width: number;
|
|
569
|
+
height: number;
|
|
570
|
+
name?: string;
|
|
571
|
+
};
|
|
572
|
+
widgets: SEWidget[];
|
|
573
|
+
createdAt?: string;
|
|
574
|
+
updatedAt?: string;
|
|
575
|
+
}
|
|
576
|
+
interface SEWidget {
|
|
577
|
+
id: number | string;
|
|
578
|
+
group?: string | null;
|
|
579
|
+
version?: number | string | null;
|
|
580
|
+
type: string;
|
|
581
|
+
name?: string | null;
|
|
582
|
+
visible?: boolean;
|
|
583
|
+
locked?: boolean;
|
|
584
|
+
listener?: string | null;
|
|
585
|
+
listeners?: Record<string, boolean> | string[] | null;
|
|
586
|
+
provider?: string;
|
|
587
|
+
css: SECss;
|
|
588
|
+
text?: SEText;
|
|
589
|
+
image?: SEImage;
|
|
590
|
+
video?: SEVideo;
|
|
591
|
+
audio?: SEAudio;
|
|
592
|
+
variables?: Record<string, unknown>;
|
|
593
|
+
}
|
|
594
|
+
interface SECss {
|
|
595
|
+
'z-index'?: number;
|
|
596
|
+
width: string | number;
|
|
597
|
+
height: string | number;
|
|
598
|
+
opacity?: number;
|
|
599
|
+
top?: string | number;
|
|
600
|
+
left?: string | number;
|
|
601
|
+
}
|
|
602
|
+
interface SEText {
|
|
603
|
+
type?: string;
|
|
604
|
+
value?: string | null;
|
|
605
|
+
enableShadow?: boolean;
|
|
606
|
+
scrolling?: {
|
|
607
|
+
direction?: string;
|
|
608
|
+
speed?: number;
|
|
609
|
+
};
|
|
610
|
+
css?: Record<string, unknown>;
|
|
611
|
+
}
|
|
612
|
+
interface SEImage {
|
|
613
|
+
type?: string;
|
|
614
|
+
src?: string;
|
|
615
|
+
css?: Record<string, unknown>;
|
|
616
|
+
}
|
|
617
|
+
interface SEVideo {
|
|
618
|
+
type?: string;
|
|
619
|
+
src?: string;
|
|
620
|
+
volume?: number;
|
|
621
|
+
css?: Record<string, unknown>;
|
|
622
|
+
}
|
|
623
|
+
interface SEAudio {
|
|
624
|
+
src?: string;
|
|
625
|
+
volume?: number;
|
|
626
|
+
}
|
|
627
|
+
interface ImportCoverage {
|
|
628
|
+
totalWidgets: number;
|
|
629
|
+
mappings: Array<{
|
|
630
|
+
seType: string;
|
|
631
|
+
lumiaType: string;
|
|
632
|
+
status: 'direct' | 'partial' | 'template' | 'placeholder';
|
|
633
|
+
count: number;
|
|
634
|
+
}>;
|
|
635
|
+
notes: string[];
|
|
636
|
+
}
|
|
637
|
+
interface ReviewItem {
|
|
638
|
+
moduleId: string;
|
|
639
|
+
seWidget: SEWidget;
|
|
640
|
+
status: 'placeholder' | 'template';
|
|
641
|
+
reason: string;
|
|
642
|
+
flaggedOff?: boolean;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/** Distinct SE CDN URLs referenced anywhere inside the overlay's settings. */
|
|
646
|
+
declare function findSEAssetURLs(overlay: OverlayState): string[];
|
|
647
|
+
/** Return a new overlay with every `oldUrl → newUrl` replacement applied. */
|
|
648
|
+
declare function rewriteAssetURLs(overlay: OverlayState, mapping: Record<string, string>): OverlayState;
|
|
649
|
+
interface AssetMirrorProgress {
|
|
650
|
+
url: string;
|
|
651
|
+
state: 'pending' | 'fetching' | 'uploading' | 'done' | 'failed';
|
|
652
|
+
newUrl?: string;
|
|
653
|
+
error?: string;
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Mirror a single SE asset to Lumia storage via the existing `/overlays/assets`
|
|
657
|
+
* upload endpoint. Returns the new Lumia URL on success.
|
|
658
|
+
*
|
|
659
|
+
* The host call site is responsible for retries and progress reporting; this
|
|
660
|
+
* function is intentionally single-shot. Pass `signal` (AbortSignal) to abort
|
|
661
|
+
* the source-side `fetch` when the modal closes mid-mirror — the upload itself
|
|
662
|
+
* is consumer-defined and the caller decides whether/how to short-circuit it.
|
|
663
|
+
*/
|
|
664
|
+
declare function mirrorOneAsset(url: string, upload: (file: File) => Promise<{
|
|
665
|
+
asset: {
|
|
666
|
+
url: string;
|
|
667
|
+
};
|
|
668
|
+
} | {
|
|
669
|
+
url: string;
|
|
670
|
+
} | unknown>, signal?: AbortSignal): Promise<string>;
|
|
671
|
+
declare function filenameFromURL(url: string): string;
|
|
672
|
+
|
|
673
|
+
interface TransplantedUnit {
|
|
674
|
+
layer: OverlayLayerState;
|
|
675
|
+
module: OverlayModuleState;
|
|
676
|
+
extras?: Array<{
|
|
677
|
+
layer: OverlayLayerState;
|
|
678
|
+
module: OverlayModuleState;
|
|
679
|
+
}>;
|
|
680
|
+
}
|
|
681
|
+
declare function transplantLayer(source: OverlayState, sourceLayerId: string): TransplantedUnit | null;
|
|
682
|
+
declare function listTransplantableLayers(source: OverlayState): Array<{
|
|
683
|
+
id: string;
|
|
684
|
+
label: string;
|
|
685
|
+
type: string;
|
|
686
|
+
}>;
|
|
687
|
+
|
|
688
|
+
declare const SE_WIDGET_TO_MARKETPLACE_CANDIDATES: Record<string, number[]>;
|
|
689
|
+
declare function getMarketplaceCandidates(seWidgetType: string): number[];
|
|
690
|
+
declare function hasMarketplaceCandidates(seWidgetType: string): boolean;
|
|
691
|
+
|
|
692
|
+
declare const SE_IMPORT_FLAGS: {
|
|
693
|
+
readonly IMPORT_MERCH_PRODUCTS: false;
|
|
694
|
+
readonly IMPORT_SEASONAL: false;
|
|
695
|
+
};
|
|
696
|
+
declare function isWidgetFlaggedOff(seType: string): boolean;
|
|
697
|
+
declare const FLAG_OFF_REASONS: Record<string, string>;
|
|
698
|
+
|
|
699
|
+
interface SEExistingAsset {
|
|
700
|
+
file_name?: string;
|
|
701
|
+
name?: string;
|
|
702
|
+
url: string;
|
|
703
|
+
}
|
|
704
|
+
interface SECustomEmbedProps {
|
|
705
|
+
codeId: string;
|
|
706
|
+
html: string;
|
|
707
|
+
css: string;
|
|
708
|
+
js: string;
|
|
709
|
+
data: unknown;
|
|
710
|
+
}
|
|
711
|
+
interface SEUploadAssetResult {
|
|
712
|
+
url: string;
|
|
713
|
+
file_name?: string;
|
|
714
|
+
name?: string;
|
|
715
|
+
}
|
|
716
|
+
interface SESaveOverlayBody {
|
|
717
|
+
name: string;
|
|
718
|
+
description?: string;
|
|
719
|
+
settings: OverlayState['settings'];
|
|
720
|
+
}
|
|
721
|
+
interface SEImportBindings {
|
|
722
|
+
fetchMarketplaceOverlay: (id: number) => Promise<OverlayState>;
|
|
723
|
+
generateAICustomCode: (prompt: string) => Promise<AIGeneratedCustomOverlay>;
|
|
724
|
+
uploadAsset: (file: File) => Promise<SEUploadAssetResult>;
|
|
725
|
+
existingAssets: SEExistingAsset[];
|
|
726
|
+
saveOverlay: (body: SESaveOverlayBody) => Promise<{
|
|
727
|
+
uuid: string;
|
|
728
|
+
}>;
|
|
729
|
+
onComplete: (result: {
|
|
730
|
+
overlayId: string;
|
|
731
|
+
}) => void;
|
|
732
|
+
onClose: () => void;
|
|
733
|
+
notify: {
|
|
734
|
+
success: (msg: string) => void;
|
|
735
|
+
error: (msg: string) => void;
|
|
736
|
+
warning: (msg: string) => void;
|
|
737
|
+
};
|
|
738
|
+
CustomEmbed?: ComponentType<SECustomEmbedProps>;
|
|
739
|
+
t?: (key: string) => string | undefined;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
interface SEImportWizardProps {
|
|
743
|
+
bindings: SEImportBindings;
|
|
744
|
+
}
|
|
745
|
+
declare function SEImportWizard({ bindings }: SEImportWizardProps): react_jsx_runtime.JSX.Element;
|
|
746
|
+
|
|
747
|
+
interface MarketplacePickerProps {
|
|
748
|
+
seWidgetType: string;
|
|
749
|
+
fetchMarketplaceOverlay: SEImportBindings['fetchMarketplaceOverlay'];
|
|
750
|
+
CustomEmbed?: SEImportBindings['CustomEmbed'];
|
|
751
|
+
onPick: (transplant: {
|
|
752
|
+
layer: OverlayLayerState;
|
|
753
|
+
module: OverlayModuleState;
|
|
754
|
+
}) => void;
|
|
755
|
+
onCancel: () => void;
|
|
756
|
+
}
|
|
757
|
+
declare function MarketplacePicker({ seWidgetType, fetchMarketplaceOverlay, CustomEmbed, onPick, onCancel }: MarketplacePickerProps): react_jsx_runtime.JSX.Element;
|
|
758
|
+
|
|
759
|
+
interface ImportResult {
|
|
760
|
+
overlay: OverlayState;
|
|
761
|
+
coverage: ImportCoverage;
|
|
762
|
+
reviewItems: ReviewItem[];
|
|
763
|
+
}
|
|
764
|
+
declare function extractSEOverlayId(input: string): string | null;
|
|
765
|
+
declare function extractSEPreviewParts(input: string): {
|
|
766
|
+
overlayId: string;
|
|
767
|
+
apikey: string;
|
|
768
|
+
} | null;
|
|
769
|
+
declare function buildBootstrapUrl(overlayId: string): string;
|
|
770
|
+
declare function fetchSEBootstrap({ overlayId, apikey }: {
|
|
771
|
+
overlayId: string;
|
|
772
|
+
apikey: string;
|
|
773
|
+
}): Promise<SEBootstrapResponse>;
|
|
774
|
+
declare function isSEBootstrap(value: unknown): value is SEBootstrapResponse;
|
|
775
|
+
declare function importSEBootstrap(bootstrap: SEBootstrapResponse): ImportResult;
|
|
776
|
+
interface AIGeneratedCustomOverlay {
|
|
777
|
+
codeId?: string;
|
|
778
|
+
html?: string;
|
|
779
|
+
css?: string;
|
|
780
|
+
js?: string;
|
|
781
|
+
data?: unknown;
|
|
782
|
+
configs?: unknown;
|
|
783
|
+
}
|
|
784
|
+
declare function applyReviewAction(result: ImportResult, moduleId: string, action: 'keep' | 'skip' | 'ai' | 'marketplace', payload?: AIGeneratedCustomOverlay | {
|
|
785
|
+
layer: OverlayLayerState;
|
|
786
|
+
module: OverlayModuleState;
|
|
787
|
+
extras?: Array<{
|
|
788
|
+
layer: OverlayLayerState;
|
|
789
|
+
module: OverlayModuleState;
|
|
790
|
+
}>;
|
|
791
|
+
}): ImportResult;
|
|
792
|
+
declare function getAILandingBounds(widget: SEWidget, canvas?: {
|
|
793
|
+
width: number;
|
|
794
|
+
height: number;
|
|
795
|
+
}): {
|
|
796
|
+
width: number;
|
|
797
|
+
height: number;
|
|
798
|
+
};
|
|
799
|
+
declare function buildAIPromptForSEWidget(widget: SEWidget, canvas?: {
|
|
800
|
+
width: number;
|
|
801
|
+
height: number;
|
|
802
|
+
}): string;
|
|
803
|
+
|
|
804
|
+
export { type AIGeneratedCustomOverlay, type AssetMirrorProgress, type BuildChatMessageContentOptions, type ChatMessageContentItem, type ChatMessageEmoteStore, type ChatMessageToken, type ChatMessageTokenizationOptions, type ChatboxMediaPreview, type ChatboxMediaPreviewKind, type ChatterProfileResolver, type ChatterProfileResolverPayload, type ChatterProfileUrlInput, FLAG_OFF_REASONS, type IVariables, type ImportCoverage, type ImportResult, LSButton, type LSButtonProps, LSCheckbox, LSCheckboxField, LSColorPicker, type LSColorPickerProps, LSFontPicker, LSInput, type LSInputProps$1 as LSInputProps, LSMultiSelect, LSRadio, LSRadioGroup, LSSelect, LSSliderInput, type LSInputProps as LSSliderInputProps, LSTextField, type LSTextFieldProps, type LSVariableInputContextValue, LSVariableInputField, type LSVariableInputFieldProps, LSVariableInputProvider, MEDIA_PREVIEW_USER_LEVEL_VALUES, MarketplacePicker, type MarketplacePickerProps, type MediaPreviewRoleSettings, type MediaPreviewRoleSettingsInput, type MediaPreviewUserLevel, type MessageLinkPart, type ModuleType, type OverlayLayerBounds, type OverlayLayerState, type OverlayLayerType, type OverlayModuleAlertState, type OverlayModuleState, type OverlaySettings, type OverlayState, type ResolveChatterProfileUrlWithResolversInput, type ReviewItem, type SEBootstrapResponse, type SECustomEmbedProps, type SEExistingAsset, type SEImportBindings, SEImportWizard, type SEImportWizardProps, type SESaveOverlayBody, type SEUploadAssetResult, type SEWidget, SE_IMPORT_FLAGS, SE_WIDGET_TO_MARKETPLACE_CANDIDATES, type TransplantedUnit, applyReviewAction, buildAIPromptForSEWidget, buildBootstrapUrl, buildChatMessageContent, codeMirrorlinterOptions, extractSEOverlayId, extractSEPreviewParts, fetchSEBootstrap, filenameFromURL, findSEAssetURLs, getAILandingBounds, getMarketplaceCandidates, getMediaPreviewFromUrl, getNormalizedUserLevels, hasMarketplaceCandidates, importSEBootstrap, isMediaPreviewAllowedForUser, isSEBootstrap, isWidgetFlaggedOff, listTransplantableLayers, mirrorOneAsset, normalizeHttpUrl, normalizeMediaPreviewUserLevels, parseMessageLinks, resolveChatterProfileUrlWithResolvers, resolveMediaPreviewRoleSettings, resolveMediaPreviewSetting, resolvePlatformChatterProfileUrl, rewriteAssetURLs, tokenizeChatMessage, transplantLayer, variableCompletionOptions };
|