@minesa-org/mini-interaction 0.0.6 → 0.0.8
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/builders/FileUploadBuilder.d.ts +37 -0
- package/dist/builders/FileUploadBuilder.js +59 -0
- package/dist/builders/LabelBuilder.d.ts +35 -0
- package/dist/builders/LabelBuilder.js +55 -0
- package/dist/builders/ModalBuilder.d.ts +2 -2
- package/dist/builders/ModalBuilder.js +27 -1
- package/dist/builders/TextInputBuilder.js +2 -3
- package/dist/builders/index.d.ts +4 -0
- package/dist/builders/index.js +2 -0
- package/dist/clients/MiniInteraction.d.ts +1 -0
- package/dist/clients/MiniInteraction.js +18 -2
- package/package.json +1 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type APIFileUploadComponent } from "discord-api-types/v10";
|
|
2
|
+
import type { JSONEncodable } from "./shared.js";
|
|
3
|
+
/** Shape describing initial file upload data accepted by the builder. */
|
|
4
|
+
export type FileUploadBuilderData = {
|
|
5
|
+
customId?: string;
|
|
6
|
+
minValues?: number;
|
|
7
|
+
maxValues?: number;
|
|
8
|
+
required?: boolean;
|
|
9
|
+
};
|
|
10
|
+
/** Builder for Discord file upload components used in modals. */
|
|
11
|
+
export declare class FileUploadBuilder implements JSONEncodable<APIFileUploadComponent> {
|
|
12
|
+
private data;
|
|
13
|
+
/**
|
|
14
|
+
* Creates a new file upload builder with optional seed data.
|
|
15
|
+
*/
|
|
16
|
+
constructor(data?: FileUploadBuilderData);
|
|
17
|
+
/**
|
|
18
|
+
* Sets the custom identifier for this file upload component.
|
|
19
|
+
*/
|
|
20
|
+
setCustomId(customId: string): this;
|
|
21
|
+
/**
|
|
22
|
+
* Sets the minimum number of files that must be uploaded (min 0, max 10).
|
|
23
|
+
*/
|
|
24
|
+
setMinValues(minValues: number): this;
|
|
25
|
+
/**
|
|
26
|
+
* Sets the maximum number of files that can be uploaded (max 10).
|
|
27
|
+
*/
|
|
28
|
+
setMaxValues(maxValues: number): this;
|
|
29
|
+
/**
|
|
30
|
+
* Sets whether files are required before submitting the modal.
|
|
31
|
+
*/
|
|
32
|
+
setRequired(required: boolean): this;
|
|
33
|
+
/**
|
|
34
|
+
* Serialises the builder into an API compatible file upload component payload.
|
|
35
|
+
*/
|
|
36
|
+
toJSON(): APIFileUploadComponent;
|
|
37
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ComponentType, } from "discord-api-types/v10";
|
|
2
|
+
/** Builder for Discord file upload components used in modals. */
|
|
3
|
+
export class FileUploadBuilder {
|
|
4
|
+
data;
|
|
5
|
+
/**
|
|
6
|
+
* Creates a new file upload builder with optional seed data.
|
|
7
|
+
*/
|
|
8
|
+
constructor(data = {}) {
|
|
9
|
+
this.data = {
|
|
10
|
+
customId: data.customId,
|
|
11
|
+
minValues: data.minValues,
|
|
12
|
+
maxValues: data.maxValues,
|
|
13
|
+
required: data.required,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Sets the custom identifier for this file upload component.
|
|
18
|
+
*/
|
|
19
|
+
setCustomId(customId) {
|
|
20
|
+
this.data.customId = customId;
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Sets the minimum number of files that must be uploaded (min 0, max 10).
|
|
25
|
+
*/
|
|
26
|
+
setMinValues(minValues) {
|
|
27
|
+
this.data.minValues = minValues;
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Sets the maximum number of files that can be uploaded (max 10).
|
|
32
|
+
*/
|
|
33
|
+
setMaxValues(maxValues) {
|
|
34
|
+
this.data.maxValues = maxValues;
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Sets whether files are required before submitting the modal.
|
|
39
|
+
*/
|
|
40
|
+
setRequired(required) {
|
|
41
|
+
this.data.required = required;
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Serialises the builder into an API compatible file upload component payload.
|
|
46
|
+
*/
|
|
47
|
+
toJSON() {
|
|
48
|
+
if (!this.data.customId) {
|
|
49
|
+
throw new Error("[FileUploadBuilder] custom_id is required.");
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
type: ComponentType.FileUpload,
|
|
53
|
+
custom_id: this.data.customId,
|
|
54
|
+
min_values: this.data.minValues,
|
|
55
|
+
max_values: this.data.maxValues,
|
|
56
|
+
required: this.data.required,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type APILabelComponent, type APIComponentInLabel } from "discord-api-types/v10";
|
|
2
|
+
import type { JSONEncodable } from "./shared.js";
|
|
3
|
+
/** Values accepted when composing label components. */
|
|
4
|
+
export type LabelComponentLike = JSONEncodable<APIComponentInLabel> | APIComponentInLabel;
|
|
5
|
+
/** Shape describing initial label data accepted by the builder. */
|
|
6
|
+
export type LabelBuilderData = {
|
|
7
|
+
label?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
component?: LabelComponentLike;
|
|
10
|
+
};
|
|
11
|
+
/** Builder for Discord label components used in modals. */
|
|
12
|
+
export declare class LabelBuilder implements JSONEncodable<APILabelComponent> {
|
|
13
|
+
private data;
|
|
14
|
+
/**
|
|
15
|
+
* Creates a new label builder with optional seed data.
|
|
16
|
+
*/
|
|
17
|
+
constructor(data?: LabelBuilderData);
|
|
18
|
+
/**
|
|
19
|
+
* Sets the label text (max 45 characters).
|
|
20
|
+
*/
|
|
21
|
+
setLabel(label: string): this;
|
|
22
|
+
/**
|
|
23
|
+
* Sets the optional description text (max 100 characters).
|
|
24
|
+
*/
|
|
25
|
+
setDescription(description: string): this;
|
|
26
|
+
/**
|
|
27
|
+
* Sets the component within the label.
|
|
28
|
+
* Can be a TextInput, SelectMenu, or FileUpload component.
|
|
29
|
+
*/
|
|
30
|
+
setComponent(component: LabelComponentLike): this;
|
|
31
|
+
/**
|
|
32
|
+
* Serialises the builder into an API compatible label component payload.
|
|
33
|
+
*/
|
|
34
|
+
toJSON(): APILabelComponent;
|
|
35
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ComponentType, } from "discord-api-types/v10";
|
|
2
|
+
import { resolveJSONEncodable } from "./shared.js";
|
|
3
|
+
/** Builder for Discord label components used in modals. */
|
|
4
|
+
export class LabelBuilder {
|
|
5
|
+
data;
|
|
6
|
+
/**
|
|
7
|
+
* Creates a new label builder with optional seed data.
|
|
8
|
+
*/
|
|
9
|
+
constructor(data = {}) {
|
|
10
|
+
this.data = {
|
|
11
|
+
label: data.label,
|
|
12
|
+
description: data.description,
|
|
13
|
+
component: data.component,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Sets the label text (max 45 characters).
|
|
18
|
+
*/
|
|
19
|
+
setLabel(label) {
|
|
20
|
+
this.data.label = label;
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Sets the optional description text (max 100 characters).
|
|
25
|
+
*/
|
|
26
|
+
setDescription(description) {
|
|
27
|
+
this.data.description = description;
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Sets the component within the label.
|
|
32
|
+
* Can be a TextInput, SelectMenu, or FileUpload component.
|
|
33
|
+
*/
|
|
34
|
+
setComponent(component) {
|
|
35
|
+
this.data.component = component;
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Serialises the builder into an API compatible label component payload.
|
|
40
|
+
*/
|
|
41
|
+
toJSON() {
|
|
42
|
+
if (!this.data.label) {
|
|
43
|
+
throw new Error("[LabelBuilder] label is required.");
|
|
44
|
+
}
|
|
45
|
+
if (!this.data.component) {
|
|
46
|
+
throw new Error("[LabelBuilder] component is required.");
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
type: ComponentType.Label,
|
|
50
|
+
label: this.data.label,
|
|
51
|
+
description: this.data.description,
|
|
52
|
+
component: resolveJSONEncodable(this.data.component),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { APIModalInteractionResponseCallbackComponent, APIModalInteractionResponseCallbackData } from "discord-api-types/v10";
|
|
1
|
+
import type { APIModalInteractionResponseCallbackComponent, APIModalInteractionResponseCallbackData, APITextInputComponent, APIComponentInLabel, APISelectMenuComponent, APIFileUploadComponent } from "discord-api-types/v10";
|
|
2
2
|
import type { JSONEncodable } from "./shared.js";
|
|
3
3
|
/** Values accepted when composing modal component rows. */
|
|
4
|
-
export type ModalComponentLike = JSONEncodable<APIModalInteractionResponseCallbackComponent> | APIModalInteractionResponseCallbackComponent;
|
|
4
|
+
export type ModalComponentLike = JSONEncodable<APIModalInteractionResponseCallbackComponent> | APIModalInteractionResponseCallbackComponent | JSONEncodable<APITextInputComponent> | APITextInputComponent | JSONEncodable<APIComponentInLabel> | APIComponentInLabel | JSONEncodable<APISelectMenuComponent> | APISelectMenuComponent | JSONEncodable<APIFileUploadComponent> | APIFileUploadComponent;
|
|
5
5
|
/** Shape describing initial modal data accepted by the builder. */
|
|
6
6
|
export type ModalBuilderData = {
|
|
7
7
|
customId?: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ComponentType } from "discord-api-types/v10";
|
|
1
2
|
import { resolveJSONEncodable } from "./shared.js";
|
|
2
3
|
/** Builder for Discord modal interaction responses. */
|
|
3
4
|
export class ModalBuilder {
|
|
@@ -56,10 +57,35 @@ export class ModalBuilder {
|
|
|
56
57
|
if (this.components.length > 5) {
|
|
57
58
|
throw new Error("[ModalBuilder] no more than 5 components can be provided.");
|
|
58
59
|
}
|
|
60
|
+
// Auto-wrap components that need wrapping
|
|
61
|
+
const normalizedComponents = this.components.map((component) => {
|
|
62
|
+
const resolved = resolveJSONEncodable(component);
|
|
63
|
+
if (!resolved ||
|
|
64
|
+
typeof resolved !== "object" ||
|
|
65
|
+
!("type" in resolved)) {
|
|
66
|
+
return resolved;
|
|
67
|
+
}
|
|
68
|
+
const componentType = resolved.type;
|
|
69
|
+
// If it's a TextInput, SelectMenu, or FileUpload component, wrap it in an ActionRow
|
|
70
|
+
// (ActionRows in modals are deprecated, but still supported for backwards compatibility)
|
|
71
|
+
if (componentType === ComponentType.TextInput ||
|
|
72
|
+
componentType === ComponentType.StringSelect ||
|
|
73
|
+
componentType === ComponentType.UserSelect ||
|
|
74
|
+
componentType === ComponentType.RoleSelect ||
|
|
75
|
+
componentType === ComponentType.MentionableSelect ||
|
|
76
|
+
componentType === ComponentType.ChannelSelect ||
|
|
77
|
+
componentType === ComponentType.FileUpload) {
|
|
78
|
+
return {
|
|
79
|
+
type: ComponentType.ActionRow,
|
|
80
|
+
components: [resolved],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return resolved;
|
|
84
|
+
});
|
|
59
85
|
return {
|
|
60
86
|
custom_id: this.customId,
|
|
61
87
|
title: this.title,
|
|
62
|
-
components:
|
|
88
|
+
components: normalizedComponents,
|
|
63
89
|
};
|
|
64
90
|
}
|
|
65
91
|
}
|
|
@@ -80,9 +80,8 @@ export class TextInputBuilder {
|
|
|
80
80
|
if (!this.data.customId) {
|
|
81
81
|
throw new Error("[TextInputBuilder] custom_id is required.");
|
|
82
82
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
83
|
+
// Note: label is optional when used inside a LabelComponent
|
|
84
|
+
// but required when used standalone in an ActionRow
|
|
86
85
|
return {
|
|
87
86
|
type: ComponentType.TextInput,
|
|
88
87
|
custom_id: this.data.customId,
|
package/dist/builders/index.d.ts
CHANGED
|
@@ -13,6 +13,10 @@ export { ModalBuilder } from "./ModalBuilder.js";
|
|
|
13
13
|
export type { ModalBuilderData, ModalComponentLike } from "./ModalBuilder.js";
|
|
14
14
|
export { TextInputBuilder } from "./TextInputBuilder.js";
|
|
15
15
|
export type { TextInputBuilderData } from "./TextInputBuilder.js";
|
|
16
|
+
export { LabelBuilder } from "./LabelBuilder.js";
|
|
17
|
+
export type { LabelBuilderData, LabelComponentLike } from "./LabelBuilder.js";
|
|
18
|
+
export { FileUploadBuilder } from "./FileUploadBuilder.js";
|
|
19
|
+
export type { FileUploadBuilderData } from "./FileUploadBuilder.js";
|
|
16
20
|
export { AutomodRuleBuilder } from "./AutomodRuleBuilder.js";
|
|
17
21
|
export type { AutomodRuleBuilderData } from "./AutomodRuleBuilder.js";
|
|
18
22
|
export { EmbedBuilder } from "./EmbedBuilder.js";
|
package/dist/builders/index.js
CHANGED
|
@@ -6,6 +6,8 @@ export { RoleSelectMenuBuilder } from "./RoleSelectMenuBuilder.js";
|
|
|
6
6
|
export { ChannelSelectMenuBuilder } from "./ChannelSelectMenuBuilder.js";
|
|
7
7
|
export { ModalBuilder } from "./ModalBuilder.js";
|
|
8
8
|
export { TextInputBuilder } from "./TextInputBuilder.js";
|
|
9
|
+
export { LabelBuilder } from "./LabelBuilder.js";
|
|
10
|
+
export { FileUploadBuilder } from "./FileUploadBuilder.js";
|
|
9
11
|
export { AutomodRuleBuilder } from "./AutomodRuleBuilder.js";
|
|
10
12
|
export { EmbedBuilder } from "./EmbedBuilder.js";
|
|
11
13
|
export { ContainerBuilder, SectionBuilder, TextDisplayBuilder, SeparatorBuilder, GalleryBuilder, GalleryItemBuilder, ThumbnailBuilder, } from "./MiniContainerBuilder.js";
|
|
@@ -185,6 +185,7 @@ export declare class MiniInteraction {
|
|
|
185
185
|
private importCommandModule;
|
|
186
186
|
/**
|
|
187
187
|
* Dynamically imports and validates a component module from disk.
|
|
188
|
+
* Also handles modal components if they're in a "modals" subdirectory.
|
|
188
189
|
*/
|
|
189
190
|
private importComponentModule;
|
|
190
191
|
/**
|
|
@@ -545,6 +545,7 @@ export class MiniInteraction {
|
|
|
545
545
|
}
|
|
546
546
|
/**
|
|
547
547
|
* Dynamically imports and validates a component module from disk.
|
|
548
|
+
* Also handles modal components if they're in a "modals" subdirectory.
|
|
548
549
|
*/
|
|
549
550
|
async importComponentModule(absolutePath) {
|
|
550
551
|
try {
|
|
@@ -554,11 +555,16 @@ export class MiniInteraction {
|
|
|
554
555
|
imported.component ??
|
|
555
556
|
imported.components ??
|
|
556
557
|
imported.componentDefinition ??
|
|
558
|
+
imported.modal ??
|
|
559
|
+
imported.modals ??
|
|
557
560
|
imported;
|
|
558
561
|
const candidates = Array.isArray(candidate)
|
|
559
562
|
? candidate
|
|
560
563
|
: [candidate];
|
|
561
564
|
const components = [];
|
|
565
|
+
// Check if this file is in a "modals" subdirectory
|
|
566
|
+
const isModalFile = absolutePath.includes(path.sep + "modals" + path.sep) ||
|
|
567
|
+
absolutePath.includes("/modals/");
|
|
562
568
|
for (const item of candidates) {
|
|
563
569
|
if (!item || typeof item !== "object") {
|
|
564
570
|
continue;
|
|
@@ -572,9 +578,18 @@ export class MiniInteraction {
|
|
|
572
578
|
console.warn(`[MiniInteraction] Component module "${absolutePath}" is missing a "handler" function. Skipping.`);
|
|
573
579
|
continue;
|
|
574
580
|
}
|
|
575
|
-
|
|
581
|
+
// If it's in a modals directory, register it as a modal
|
|
582
|
+
if (isModalFile) {
|
|
583
|
+
this.useModal({
|
|
584
|
+
customId,
|
|
585
|
+
handler: handler,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
components.push({ customId, handler });
|
|
590
|
+
}
|
|
576
591
|
}
|
|
577
|
-
if (components.length === 0) {
|
|
592
|
+
if (components.length === 0 && !isModalFile) {
|
|
578
593
|
console.warn(`[MiniInteraction] Component module "${absolutePath}" did not export any valid components. Skipping.`);
|
|
579
594
|
}
|
|
580
595
|
return components;
|
|
@@ -755,6 +770,7 @@ export class MiniInteraction {
|
|
|
755
770
|
},
|
|
756
771
|
};
|
|
757
772
|
}
|
|
773
|
+
await this.ensureComponentsLoaded();
|
|
758
774
|
const handler = this.modalHandlers.get(customId);
|
|
759
775
|
if (!handler) {
|
|
760
776
|
return {
|
package/package.json
CHANGED