@mostfeatured/dbi 0.2.16 → 0.2.18

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.
Files changed (81) hide show
  1. package/dist/src/types/Components/HTMLComponentsV2/index.d.ts +4 -0
  2. package/dist/src/types/Components/HTMLComponentsV2/index.d.ts.map +1 -1
  3. package/dist/src/types/Components/HTMLComponentsV2/index.js +40 -5
  4. package/dist/src/types/Components/HTMLComponentsV2/index.js.map +1 -1
  5. package/dist/src/types/Event.d.ts +21 -13
  6. package/dist/src/types/Event.d.ts.map +1 -1
  7. package/dist/src/types/Event.js.map +1 -1
  8. package/dist/test/index.js +1 -1
  9. package/dist/test/index.js.map +1 -1
  10. package/generated/namespaceData.d.ts +3 -1
  11. package/package.json +6 -2
  12. package/.gitattributes +0 -2
  13. package/.hintrc +0 -8
  14. package/.vscode/settings.json +0 -3
  15. package/docs/ADVANCED_FEATURES.md +0 -840
  16. package/docs/API_REFERENCE.md +0 -929
  17. package/docs/CHAT_INPUT.md +0 -811
  18. package/docs/COMPONENTS.md +0 -1039
  19. package/docs/EVENTS.md +0 -568
  20. package/docs/GETTING_STARTED.md +0 -398
  21. package/docs/LOCALIZATION.md +0 -777
  22. package/docs/README.md +0 -345
  23. package/docs/SVELTE_COMPONENTS.md +0 -1111
  24. package/docs/llm/ADVANCED_FEATURES.txt +0 -521
  25. package/docs/llm/API_REFERENCE.txt +0 -659
  26. package/docs/llm/CHAT_INPUT.txt +0 -514
  27. package/docs/llm/COMPONENTS.txt +0 -595
  28. package/docs/llm/EVENTS.txt +0 -449
  29. package/docs/llm/GETTING_STARTED.txt +0 -296
  30. package/docs/llm/LOCALIZATION.txt +0 -501
  31. package/docs/llm/README.txt +0 -193
  32. package/docs/llm/SVELTE_COMPONENTS.txt +0 -566
  33. package/src/DBI.ts +0 -1007
  34. package/src/Events.ts +0 -189
  35. package/src/data/eventMap.json +0 -248
  36. package/src/index.ts +0 -23
  37. package/src/methods/handleMessageCommands.ts +0 -482
  38. package/src/methods/hookEventListeners.ts +0 -119
  39. package/src/methods/hookInteractionListeners.ts +0 -314
  40. package/src/methods/publishInteractions.ts +0 -256
  41. package/src/types/ApplicationRoleConnectionMetadata.ts +0 -19
  42. package/src/types/Builders/ButtonBuilder.ts +0 -53
  43. package/src/types/Builders/ChannelSelectMenuBuilder.ts +0 -53
  44. package/src/types/Builders/MentionableSelectMenuBuilder.ts +0 -53
  45. package/src/types/Builders/ModalBuilder.ts +0 -53
  46. package/src/types/Builders/RoleSelectMenuBuilder.ts +0 -53
  47. package/src/types/Builders/StringSelectMenuBuilder.ts +0 -53
  48. package/src/types/Builders/UserSelectMenuBuilder.ts +0 -53
  49. package/src/types/ChatInput/ChatInput.ts +0 -28
  50. package/src/types/ChatInput/ChatInputOptions.ts +0 -388
  51. package/src/types/Components/Button.ts +0 -39
  52. package/src/types/Components/ChannelSelectMenu.ts +0 -43
  53. package/src/types/Components/HTMLComponentsV2/HTMLComponentsV2Handlers.ts +0 -78
  54. package/src/types/Components/HTMLComponentsV2/index.ts +0 -761
  55. package/src/types/Components/HTMLComponentsV2/parser.ts +0 -649
  56. package/src/types/Components/HTMLComponentsV2/svelteParser.ts +0 -1503
  57. package/src/types/Components/HTMLComponentsV2/svelteRenderer.ts +0 -416
  58. package/src/types/Components/MentionableSelectMenu.ts +0 -43
  59. package/src/types/Components/Modal.ts +0 -46
  60. package/src/types/Components/RoleSelectMenu.ts +0 -43
  61. package/src/types/Components/StringSelectMenu.ts +0 -43
  62. package/src/types/Components/UserSelectMenu.ts +0 -43
  63. package/src/types/Event.ts +0 -145
  64. package/src/types/Interaction.ts +0 -100
  65. package/src/types/other/CustomEvent.ts +0 -19
  66. package/src/types/other/FakeMessageInteraction.ts +0 -408
  67. package/src/types/other/InteractionLocale.ts +0 -34
  68. package/src/types/other/Locale.ts +0 -70
  69. package/src/types/other/MessageContextMenu.ts +0 -27
  70. package/src/types/other/UserContextMenu.ts +0 -25
  71. package/src/utils/MemoryStore.ts +0 -28
  72. package/src/utils/UtilTypes.ts +0 -11
  73. package/src/utils/customId.ts +0 -49
  74. package/src/utils/permissions.ts +0 -5
  75. package/src/utils/recursiveImport.ts +0 -35
  76. package/src/utils/recursiveUnload.ts +0 -25
  77. package/src/utils/unloadModule.ts +0 -7
  78. package/test/index.ts +0 -176
  79. package/test/product-showcase.svelte +0 -558
  80. package/test/test.ts +0 -3
  81. package/tsconfig.json +0 -51
@@ -1,1111 +0,0 @@
1
- # DBI Svelte Components System
2
-
3
- DBI provides a powerful Svelte 5-based component system for building interactive Discord UI with **Components V2**. This system brings modern reactive programming patterns to Discord bot development, allowing you to create dynamic, stateful interfaces with minimal boilerplate.
4
-
5
- ## Table of Contents
6
-
7
- - [Overview](#overview)
8
- - [Quick Start](#quick-start)
9
- - [Component Structure](#component-structure)
10
- - [Props and Reactivity](#props-and-reactivity)
11
- - [Lifecycle Hooks](#lifecycle-hooks)
12
- - [Render Helpers](#render-helpers)
13
- - [HTML Elements Reference](#html-elements-reference)
14
- - [Handler Functions](#handler-functions)
15
- - [Using External Modules](#using-external-modules)
16
- - [Type Definitions](#type-definitions)
17
- - [Complete Example](#complete-example)
18
-
19
- ---
20
-
21
- ## Overview
22
-
23
- The DBI Svelte component system provides:
24
-
25
- - **Svelte 5 Syntax** - Use modern `$props()` runes and reactive patterns
26
- - **Auto-Reactivity** - UI automatically updates when data changes
27
- - **Lifecycle Hooks** - `onMount` and `onDestroy` for managing timers, intervals, cleanup
28
- - **Throttled Rendering** - Built-in rate limiting to prevent Discord API abuse
29
- - **Type Safety** - Full TypeScript support with autocomplete for Discord components
30
-
31
- ---
32
-
33
- ## Quick Start
34
-
35
- ### 1. Register a Svelte Component
36
-
37
- ```typescript
38
- import { createDBI } from "@mostfeatured/dbi";
39
- import path from "path";
40
-
41
- const dbi = createDBI("my-bot", { /* config */ });
42
-
43
- dbi.register(({ HTMLComponentsV2 }) => {
44
- HTMLComponentsV2({
45
- name: "my-component",
46
- mode: "svelte",
47
- file: path.join(__dirname, "my-component.svelte"),
48
- });
49
- });
50
- ```
51
-
52
- ### 2. Create the Svelte File
53
-
54
- ```svelte
55
- <script>
56
- let { count = 0 } = $props();
57
-
58
- function increment() {
59
- data.count++;
60
- }
61
- </script>
62
-
63
- <components>
64
- <text-display>Count: {count}</text-display>
65
- <action-row>
66
- <button style="Primary" handler={increment}>+1</button>
67
- </action-row>
68
- </components>
69
- ```
70
-
71
- ### 3. Send the Component
72
-
73
- ```typescript
74
- ChatInput({
75
- name: "counter",
76
- description: "Interactive counter",
77
- async onExecute({ interaction, dbi }) {
78
- const component = dbi.interaction("my-component");
79
-
80
- await component.send(interaction, {
81
- data: { count: 0 }
82
- });
83
- }
84
- });
85
- ```
86
-
87
- ---
88
-
89
- ## Component Structure
90
-
91
- A DBI Svelte component consists of two main parts:
92
-
93
- ### Script Block
94
-
95
- Contains your reactive state, handler functions, and lifecycle hooks.
96
-
97
- ```svelte
98
- <script>
99
- // Type reference for IDE support
100
- /// <reference types="@mostfeatured/dbi/svelte" />
101
-
102
- // Import external modules
103
- import stuffs from "stuffs";
104
-
105
- // Declare props with Svelte 5 $props() rune
106
- let {
107
- products = [],
108
- currentIndex = 0,
109
- cart = [],
110
- view = "browse",
111
- } = $props();
112
-
113
- // Handler functions (automatically bound to buttons/selects)
114
- function nextProduct() {
115
- data.currentIndex = (currentIndex + 1) % products.length;
116
- }
117
-
118
- function addToCart(ctx) {
119
- const product = products[currentIndex];
120
- data.cart = [...cart, product];
121
- ctx.interaction.reply({
122
- content: "Added to cart!",
123
- flags: ["Ephemeral"],
124
- });
125
- }
126
-
127
- // Lifecycle
128
- onMount(() => {
129
- const interval = setInterval(() => {
130
- data.elapsedTime += 1;
131
- }, 1000);
132
-
133
- return () => clearInterval(interval); // Cleanup
134
- });
135
- </script>
136
- ```
137
-
138
- ### Template Block
139
-
140
- Contains your Discord UI components wrapped in `<components>`.
141
-
142
- ```svelte
143
- <components>
144
- <container accent-color="5865F2">
145
- <components>
146
- <text-display>## Welcome!</text-display>
147
- <action-row>
148
- <button style="Primary" handler={nextProduct}>Next</button>
149
- </action-row>
150
- </components>
151
- </container>
152
- </components>
153
- ```
154
-
155
- ---
156
-
157
- ## Props and Reactivity
158
-
159
- ### Declaring Props
160
-
161
- Use Svelte 5's `$props()` rune to declare component props with default values:
162
-
163
- ```svelte
164
- <script>
165
- let {
166
- count = 0,
167
- items = [],
168
- settings = { theme: "dark" },
169
- } = $props();
170
- </script>
171
- ```
172
-
173
- ### The `data` Object
174
-
175
- Inside handlers, use the global `data` object to update state. Changes automatically trigger re-renders:
176
-
177
- ```svelte
178
- <script>
179
- let { count = 0 } = $props();
180
-
181
- function increment() {
182
- // Use 'data' to update - triggers auto-render
183
- data.count++;
184
- }
185
-
186
- function reset() {
187
- data.count = 0;
188
- }
189
- </script>
190
- ```
191
-
192
- ### Reactive Updates
193
-
194
- The `data` object is wrapped in a Proxy that:
195
-
196
- 1. Detects property changes
197
- 2. Automatically re-renders the component
198
- 3. Throttles updates (default: 250ms minimum between renders)
199
-
200
- ```svelte
201
- <script>
202
- let { items = [] } = $props();
203
-
204
- function addItem(ctx) {
205
- // Arrays must be reassigned to trigger reactivity
206
- data.items = [...items, { name: "New Item" }];
207
- }
208
- </script>
209
- ```
210
-
211
- ---
212
-
213
- ## Lifecycle Hooks
214
-
215
- ### onMount
216
-
217
- Runs when the component is first sent. Perfect for setting up timers, intervals, or fetching data.
218
-
219
- ```svelte
220
- <script>
221
- let { seconds = 0 } = $props();
222
-
223
- onMount(() => {
224
- console.log("Component mounted!");
225
-
226
- // Start a timer
227
- const interval = setInterval(() => {
228
- data.seconds++;
229
- }, 1000);
230
-
231
- // Return cleanup function (optional)
232
- return () => {
233
- clearInterval(interval);
234
- console.log("Timer cleared!");
235
- };
236
- });
237
- </script>
238
- ```
239
-
240
- ### onDestroy
241
-
242
- Runs when the component is destroyed (via `$unRef` or manual `destroy()` call).
243
-
244
- ```svelte
245
- <script>
246
- let timer;
247
-
248
- onMount(() => {
249
- timer = setInterval(() => data.count++, 1000);
250
- });
251
-
252
- onDestroy(() => {
253
- clearInterval(timer);
254
- console.log("Component destroyed, cleanup complete!");
255
- });
256
- </script>
257
- ```
258
-
259
- ### Manual Destruction
260
-
261
- Call `destroy()` from any handler to manually clean up:
262
-
263
- ```svelte
264
- <script>
265
- function handleClose() {
266
- destroy(); // Runs onDestroy callbacks, removes ref
267
- }
268
- </script>
269
-
270
- <components>
271
- <action-row>
272
- <button style="Danger" handler={handleClose}>Close</button>
273
- </action-row>
274
- </components>
275
- ```
276
-
277
- ---
278
-
279
- ## Render Helpers
280
-
281
- ### render()
282
-
283
- Force an immediate render of the component:
284
-
285
- ```svelte
286
- <script>
287
- function forceUpdate() {
288
- data.value = computeExpensiveValue();
289
- render(); // Force immediate render
290
- }
291
- </script>
292
- ```
293
-
294
- ### update()
295
-
296
- Update the message using `interaction.update()`. Best for button clicks:
297
-
298
- ```svelte
299
- <script>
300
- async function handleButton() {
301
- data.count++;
302
- await update(); // Uses interaction.update()
303
- }
304
- </script>
305
- ```
306
-
307
- ### rerender()
308
-
309
- Re-render using `message.edit()`. Use after `reply()` or `followUp()`:
310
-
311
- ```svelte
312
- <script>
313
- async function processData(ctx) {
314
- await ctx.interaction.reply({ content: "Processing..." });
315
-
316
- data.result = await fetchData();
317
- await rerender(); // Uses message.edit()
318
- }
319
- </script>
320
- ```
321
-
322
- ### noRender()
323
-
324
- Disable auto-render for the current handler:
325
-
326
- ```svelte
327
- <script>
328
- function backgroundTask() {
329
- noRender(); // Don't update UI
330
- data.internalState = calculate(); // Won't trigger render
331
- }
332
- </script>
333
- ```
334
-
335
- ### setThrottle(ms)
336
-
337
- Set minimum interval between renders:
338
-
339
- ```svelte
340
- <script>
341
- // For a timer that updates every second
342
- setThrottle(1000);
343
-
344
- onMount(() => {
345
- setInterval(() => data.seconds++, 1000);
346
- });
347
- </script>
348
- ```
349
-
350
- ### lowPriorityUpdate(callback)
351
-
352
- Low-priority update for background tasks. If a user interaction handler is running, the callback executes but rendering is skipped (the handler's render will include the changes).
353
-
354
- Use this to prevent interval/timeout updates from conflicting with button clicks:
355
-
356
- ```svelte
357
- <script>
358
- onMount(() => {
359
- const interval = setInterval(() => {
360
- // If user clicks a button during this interval tick,
361
- // this update won't trigger a conflicting render
362
- lowPriorityUpdate(() => {
363
- data.elapsedTime += 1;
364
- });
365
- }, 1000);
366
-
367
- return () => clearInterval(interval);
368
- });
369
- </script>
370
- ```
371
-
372
- ---
373
-
374
- ## HTML Elements Reference
375
-
376
- ### Layout Components
377
-
378
- #### `<components>`
379
- Root wrapper for all Discord components.
380
-
381
- ```svelte
382
- <components>
383
- <!-- Your components here -->
384
- </components>
385
- ```
386
-
387
- #### `<action-row>`
388
- Container for buttons (max 5) or a single select menu.
389
-
390
- ```svelte
391
- <action-row>
392
- <button style="Primary">Click Me</button>
393
- <button style="Secondary">Or Me</button>
394
- </action-row>
395
- ```
396
-
397
- #### `<container>`
398
- Colored container with optional accent color.
399
-
400
- ```svelte
401
- <container accent-color="5865F2" spoiler>
402
- <components>
403
- <!-- Content -->
404
- </components>
405
- </container>
406
- ```
407
-
408
- | Attribute | Type | Description |
409
- |-----------|------|-------------|
410
- | `accent-color` | string | Hex color (e.g., "5865F2", "#FF0000") |
411
- | `spoiler` | boolean | Hide content behind spoiler |
412
-
413
- #### `<section>`
414
- Section with components and optional accessory (thumbnail/button).
415
-
416
- ```svelte
417
- <section>
418
- <components>
419
- <text-display>Main content</text-display>
420
- </components>
421
- <thumbnail url="https://example.com/image.png"></thumbnail>
422
- </section>
423
- ```
424
-
425
- #### `<separator>`
426
- Visual divider between components.
427
-
428
- ```svelte
429
- <separator divider spacing="2"></separator>
430
- ```
431
-
432
- | Attribute | Type | Description |
433
- |-----------|------|-------------|
434
- | `divider` | boolean | Show divider line |
435
- | `spacing` | number | Spacing size (1-3) |
436
-
437
- ---
438
-
439
- ### Interactive Components
440
-
441
- #### `<button>`
442
- Discord button with various styles.
443
-
444
- ```svelte
445
- <button
446
- style="Primary"
447
- emoji="🚀"
448
- handler={handleClick}
449
- disabled
450
- >
451
- Click Me
452
- </button>
453
- ```
454
-
455
- | Attribute | Type | Description |
456
- |-----------|------|-------------|
457
- | `style` | "Primary" \| "Secondary" \| "Success" \| "Danger" \| "Link" \| "Premium" | Button style |
458
- | `handler` | function | Handler function reference |
459
- | `emoji` | string | Emoji to display |
460
- | `disabled` | boolean | Disable the button |
461
- | `url` | string | URL for Link style |
462
- | `sku-id` | string | SKU ID for Premium style |
463
-
464
- **Note:** You can also use `onclick` as an alias for `handler`.
465
-
466
- #### `<string-select>`
467
- Dropdown menu with custom options.
468
-
469
- ```svelte
470
- <string-select
471
- placeholder="Choose an option..."
472
- min-values="1"
473
- max-values="3"
474
- handler={handleSelect}
475
- >
476
- <option value="a" description="First option" emoji="1️⃣" default>
477
- Option A
478
- </option>
479
- <option value="b" description="Second option">
480
- Option B
481
- </option>
482
- </string-select>
483
- ```
484
-
485
- | Attribute | Type | Description |
486
- |-----------|------|-------------|
487
- | `placeholder` | string | Placeholder text |
488
- | `min-values` | number | Minimum selections |
489
- | `max-values` | number | Maximum selections |
490
- | `handler` | function | Handler function |
491
- | `disabled` | boolean | Disable the menu |
492
-
493
- #### `<option>`
494
- Option for select menus.
495
-
496
- | Attribute | Type | Description |
497
- |-----------|------|-------------|
498
- | `value` | string | Value sent when selected |
499
- | `description` | string | Description below label |
500
- | `emoji` | string | Emoji to display |
501
- | `default` | boolean | Selected by default |
502
-
503
- #### Other Select Menus
504
-
505
- ```svelte
506
- <user-select placeholder="Select users..." handler={handleUsers}></user-select>
507
- <role-select placeholder="Select roles..." handler={handleRoles}></role-select>
508
- <channel-select placeholder="Select channels..." handler={handleChannels}></channel-select>
509
- <mentionable-select placeholder="Select users/roles..." handler={handleMentionables}></mentionable-select>
510
- ```
511
-
512
- ---
513
-
514
- ### Display Components
515
-
516
- #### `<text-display>`
517
- Renders markdown text.
518
-
519
- ```svelte
520
- <text-display>
521
- ## Heading
522
- **Bold** and *italic* text
523
-
524
- - List item 1
525
- - List item 2
526
- </text-display>
527
- ```
528
-
529
- #### `<thumbnail>`
530
- Thumbnail image for sections.
531
-
532
- ```svelte
533
- <thumbnail url="https://example.com/image.png"></thumbnail>
534
- <!-- or -->
535
- <thumbnail media="https://example.com/image.png"></thumbnail>
536
- ```
537
-
538
- #### `<media-gallery>`
539
- Gallery of images.
540
-
541
- ```svelte
542
- <media-gallery>
543
- <item url="https://example.com/1.png" description="Image 1"></item>
544
- <item url="https://example.com/2.png" spoiler></item>
545
- </media-gallery>
546
- ```
547
-
548
- #### `<file>`
549
- File attachment display.
550
-
551
- ```svelte
552
- <file url="attachment://document.pdf" spoiler></file>
553
- ```
554
-
555
- ---
556
-
557
- ### Modal Components
558
-
559
- Discord modals now support many interactive components beyond text inputs. Use the `<field>` wrapper component for the new structure.
560
-
561
- #### `<components type="modal">`
562
-
563
- Define a modal form that can be shown to users.
564
-
565
- ```svelte
566
- <components
567
- type="modal"
568
- id="feedback-modal"
569
- title="Submit Feedback"
570
- >
571
- <!-- New Field wrapper structure (recommended) -->
572
- <field label="Rating" description="How would you rate our service?">
573
- <string-select id="rating" placeholder="Select rating">
574
- <option value="5">⭐⭐⭐⭐⭐ Excellent</option>
575
- <option value="4">⭐⭐⭐⭐ Great</option>
576
- <option value="3">⭐⭐⭐ Good</option>
577
- <option value="2">⭐⭐ Fair</option>
578
- <option value="1">⭐ Poor</option>
579
- </string-select>
580
- </field>
581
- <field label="Comments" description="Tell us more about your experience">
582
- <text-input id="comments" style="Paragraph" placeholder="Your feedback..." />
583
- </field>
584
- </components>
585
- ```
586
-
587
- #### `<field>`
588
-
589
- Wrapper component for modal inputs (Discord's Label component, type 18). **Required for new modal structure.**
590
-
591
- | Attribute | Type | Description |
592
- |-----------|------|-------------|
593
- | `label` | string | Label text shown above component |
594
- | `description` | string | Optional description text |
595
-
596
- #### `<text-input>`
597
-
598
- Text input for modal forms.
599
-
600
- ```svelte
601
- <field label="Username" description="Enter your display name">
602
- <text-input
603
- id="username"
604
- placeholder="Enter your username"
605
- style="Short"
606
- min-length="3"
607
- max-length="32"
608
- required
609
- />
610
- </field>
611
-
612
- <!-- Legacy format still supported -->
613
- <text-input
614
- id="username"
615
- label="Username"
616
- placeholder="Enter your username"
617
- style="Short"
618
- />
619
- ```
620
-
621
- | Attribute | Type | Description |
622
- |-----------|------|-------------|
623
- | `id` / `custom-id` / `name` | string | Input identifier |
624
- | `label` | string | Label (legacy format only) |
625
- | `placeholder` | string | Placeholder text |
626
- | `style` | "Short" \| "Paragraph" | Input style |
627
- | `min-length` | number | Minimum characters |
628
- | `max-length` | number | Maximum characters |
629
- | `required` | boolean | Is required |
630
- | `value` | string | Default value |
631
-
632
- #### `<string-select>` (in modals)
633
-
634
- Dropdown select menu in modals. Returns an **array** of selected values.
635
-
636
- ```svelte
637
- <field label="Choose Bug Type" description="Select the bug category">
638
- <string-select id="bug-type" placeholder="Select bug type">
639
- <option value="ant" emoji="🐜">Ant</option>
640
- <option value="beetle" emoji="🪲">Beetle</option>
641
- <option value="spider" emoji="🕷️">Spider</option>
642
- </string-select>
643
- </field>
644
- ```
645
-
646
- #### `<user-select>`, `<role-select>`, `<mentionable-select>`, `<channel-select>` (in modals)
647
-
648
- Auto-populated select menus for modals. Returns an **array** of IDs.
649
-
650
- ```svelte
651
- <field label="Assign To" description="Select team members">
652
- <user-select id="assignees" placeholder="Choose users" max-values="3" />
653
- </field>
654
-
655
- <field label="Notification Channel" description="Where to post updates">
656
- <channel-select id="channel" placeholder="Select channel" channel-types="0,5" />
657
- </field>
658
- ```
659
-
660
- #### `<file-upload>` (in modals)
661
-
662
- File upload component for modals. Returns attachment objects.
663
-
664
- ```svelte
665
- <field label="Attachments" description="Upload relevant files">
666
- <file-upload id="files" min-values="0" max-values="5" />
667
- </field>
668
- ```
669
-
670
- #### `<text-display>` (in modals)
671
-
672
- Show static text content in modals.
673
-
674
- ```svelte
675
- <text-display>Please fill out all required fields below.</text-display>
676
- ```
677
-
678
- ### Using Modals with `showModal()`
679
-
680
- The `showModal()` function opens a modal and returns a Promise with the submitted values:
681
-
682
- ```svelte
683
- <script>
684
- async function openFeedbackModal(ctx) {
685
- // Show modal and wait for submission
686
- const { fields, interaction } = await showModal("feedback-modal");
687
-
688
- // Extract values
689
- const rating = fields.rating[0]; // string-select returns array
690
- const comments = fields.comments; // text-input returns string
691
-
692
- // Respond to the submission
693
- interaction.reply({
694
- content: `Thanks for your ${rating}-star feedback!`,
695
- flags: ["Ephemeral"]
696
- });
697
- }
698
- </script>
699
- ```
700
-
701
- ### Modal Field Types
702
-
703
- Different modal components return different value types:
704
-
705
- | Component | Return Type | Example |
706
- |-----------|-------------|---------|
707
- | `text-input` | `string` | `"Hello world"` |
708
- | `string-select` | `string[]` | `["option1", "option2"]` |
709
- | `user-select` | `string[]` | `["123456789"]` (user IDs) |
710
- | `role-select` | `string[]` | `["987654321"]` (role IDs) |
711
- | `channel-select` | `string[]` | `["111222333"]` (channel IDs) |
712
- | `mentionable-select` | `{ values, users, roles }` | IDs with separated types |
713
- | `file-upload` | `Attachment[]` | Uploaded file objects |
714
-
715
- ---
716
-
717
- ## Handler Functions
718
-
719
- Handler functions receive a `ctx` object with the interaction context:
720
-
721
- ```svelte
722
- <script>
723
- function handleButton(ctx) {
724
- // Access the Discord interaction
725
- const { interaction } = ctx;
726
-
727
- // Reply to the user
728
- ctx.interaction.reply({
729
- content: "Button clicked!",
730
- flags: ["Ephemeral"],
731
- });
732
-
733
- // Access DBI instance
734
- const { dbi } = ctx;
735
-
736
- // Access locale helpers
737
- const { locale } = ctx;
738
- const text = locale.user("greeting");
739
- }
740
-
741
- // Handler without ctx - just updates data
742
- function simpleHandler() {
743
- data.count++;
744
- // Auto-renders after handler completes
745
- }
746
- </script>
747
-
748
- <components>
749
- <action-row>
750
- <button handler={handleButton}>With Context</button>
751
- <button handler={simpleHandler}>Simple</button>
752
- </action-row>
753
- </components>
754
- ```
755
-
756
- ### Context Object
757
-
758
- | Property | Type | Description |
759
- |----------|------|-------------|
760
- | `interaction` | ButtonInteraction / SelectMenuInteraction | Discord.js interaction |
761
- | `dbi` | DBI | DBI instance |
762
- | `locale` | object | Locale helpers (`user()`, `guild()`) |
763
-
764
- ---
765
-
766
- ## Using External Modules
767
-
768
- You can import external modules in your Svelte scripts:
769
-
770
- ```svelte
771
- <script>
772
- import stuffs from "stuffs";
773
- import lodash from "lodash";
774
- import { someUtil } from "./utils";
775
-
776
- function formatTime(seconds) {
777
- return stuffs.formatSeconds(seconds);
778
- }
779
-
780
- function sortItems() {
781
- data.items = lodash.sortBy(items, "name");
782
- }
783
- </script>
784
- ```
785
-
786
- Modules are loaded via `require()` at runtime, so they must be installed in your project.
787
-
788
- ---
789
-
790
- ## Type Definitions
791
-
792
- For full IDE support with autocomplete, add the type reference at the top of your script:
793
-
794
- ```svelte
795
- <script>
796
- /// <reference types="@mostfeatured/dbi/svelte" />
797
-
798
- // Now you get autocomplete for:
799
- // - render(), update(), rerender(), noRender(), setThrottle()
800
- // - onMount(), onDestroy(), destroy()
801
- // - ctx, data
802
- // - All HTML elements (text-display, button, etc.)
803
- </script>
804
- ```
805
-
806
- ---
807
-
808
- ## Complete Example
809
-
810
- Here's a complete example of a product showcase component:
811
-
812
- ### Registration (index.ts)
813
-
814
- ```typescript
815
- import { createDBI } from "@mostfeatured/dbi";
816
- import path from "path";
817
-
818
- const dbi = createDBI("shop-bot", {
819
- discord: {
820
- token: process.env.DISCORD_TOKEN,
821
- options: {
822
- intents: ["GuildMessages", "Guilds"],
823
- }
824
- },
825
- references: {
826
- autoClear: {
827
- ttl: 60000 * 60, // 60 minutes
828
- check: 60000 // Check every 60 seconds
829
- }
830
- }
831
- });
832
-
833
- dbi.register(({ ChatInput, HTMLComponentsV2 }) => {
834
- // Register Svelte component
835
- HTMLComponentsV2({
836
- name: "product-showcase",
837
- mode: "svelte",
838
- file: path.join(__dirname, "product-showcase.svelte"),
839
- });
840
-
841
- // Command to show the component
842
- ChatInput({
843
- name: "shop",
844
- description: "Browse our product catalog",
845
- async onExecute({ interaction, dbi }) {
846
- const showcase = dbi.interaction("product-showcase");
847
-
848
- await showcase.send(interaction, {
849
- data: {
850
- products: [
851
- { name: "Keyboard", price: 149, image: "https://..." },
852
- { name: "Mouse", price: 79, image: "https://..." },
853
- ],
854
- currentIndex: 0,
855
- cart: [],
856
- view: "browse",
857
- elapsedTime: 0,
858
- }
859
- });
860
- }
861
- });
862
- });
863
-
864
- dbi.load().then(() => console.log("Bot ready!"));
865
- ```
866
-
867
- ### Component (product-showcase.svelte)
868
-
869
- ```svelte
870
- <script>
871
- /// <reference types="@mostfeatured/dbi/svelte" />
872
- import stuffs from "stuffs";
873
-
874
- let {
875
- products = [],
876
- currentIndex = 0,
877
- cart = [],
878
- view = "browse",
879
- elapsedTime = 0,
880
- } = $props();
881
-
882
- // Format elapsed time
883
- function formatTime(seconds) {
884
- return stuffs.formatSeconds(seconds);
885
- }
886
-
887
- // Navigation
888
- function nextProduct() {
889
- data.currentIndex = (currentIndex + 1) % products.length;
890
- }
891
-
892
- function prevProduct() {
893
- data.currentIndex = (currentIndex - 1 + products.length) % products.length;
894
- }
895
-
896
- // Cart actions
897
- function addToCart(ctx) {
898
- const product = products[currentIndex];
899
- data.cart = [...cart, product];
900
- ctx.interaction.reply({
901
- content: `✅ Added **${product.name}** to cart!`,
902
- flags: ["Ephemeral"],
903
- });
904
- }
905
-
906
- function clearCart(ctx) {
907
- data.cart = [];
908
- ctx.interaction.reply({
909
- content: "🗑️ Cart cleared!",
910
- flags: ["Ephemeral"],
911
- });
912
- }
913
-
914
- function checkout(ctx) {
915
- if (cart.length === 0) {
916
- ctx.interaction.reply({
917
- content: "Cart is empty!",
918
- flags: ["Ephemeral"],
919
- });
920
- noRender(); // Don't update UI
921
- return;
922
- }
923
-
924
- const total = cart.reduce((sum, p) => sum + p.price, 0);
925
- ctx.interaction.reply({
926
- content: `💳 **Order Placed!**\nTotal: $${total}`,
927
- flags: ["Ephemeral"],
928
- });
929
-
930
- data.cart = [];
931
- data.view = "browse";
932
- }
933
-
934
- // View switching
935
- function showCart() { data.view = "cart"; }
936
- function showBrowse() { data.view = "browse"; }
937
- function showDetails() { data.view = "details"; }
938
-
939
- // Lifecycle - start timer on mount
940
- onMount(() => {
941
- data.elapsedTime = 0;
942
- const interval = setInterval(() => {
943
- data.elapsedTime += 1;
944
- }, 1000);
945
-
946
- // Cleanup on destroy
947
- return () => clearInterval(interval);
948
- });
949
- </script>
950
-
951
- <components>
952
- {#if view === "browse"}
953
- <container accent-color="5865F2">
954
- <components>
955
- <section>
956
- <components>
957
- <text-display>## 🛍️ Product Showcase</text-display>
958
- <text-display>
959
- **{products[currentIndex]?.name}**
960
- {products[currentIndex]?.description}
961
- </text-display>
962
- <text-display>💰 **${products[currentIndex]?.price}**</text-display>
963
- </components>
964
- <thumbnail media={products[currentIndex]?.image}></thumbnail>
965
- </section>
966
-
967
- <separator></separator>
968
-
969
- <text-display>
970
- 📦 Product {currentIndex + 1} of {products.length} | 🛒 Cart: {cart.length} items
971
- </text-display>
972
-
973
- <action-row>
974
- <button style="Secondary" handler={prevProduct}>◀️ Prev</button>
975
- <button style="Secondary" handler={nextProduct}>Next ▶️</button>
976
- <button style="Success" handler={addToCart}>🛒 Add to Cart</button>
977
- <button style="Primary" handler={showDetails}>📋 Details</button>
978
- <button style="Primary" handler={showCart}>🛒 View Cart ({cart.length})</button>
979
- </action-row>
980
-
981
- <separator></separator>
982
- <text-display>⏱️ Session: {formatTime(elapsedTime)}</text-display>
983
- </components>
984
- </container>
985
-
986
- {:else if view === "cart"}
987
- <container accent-color="57F287">
988
- <components>
989
- <text-display>## 🛒 Your Cart</text-display>
990
-
991
- {#if cart.length === 0}
992
- <text-display>*Your cart is empty*</text-display>
993
- {:else}
994
- {#each cart as item, i}
995
- <text-display>• **{item.name}** - ${item.price}</text-display>
996
- {/each}
997
- <separator></separator>
998
- <text-display>
999
- **Total: ${cart.reduce((sum, p) => sum + p.price, 0)}**
1000
- </text-display>
1001
- {/if}
1002
-
1003
- <action-row>
1004
- <button style="Secondary" handler={showBrowse}>◀️ Back</button>
1005
- <button style="Danger" handler={clearCart}>🗑️ Clear</button>
1006
- <button style="Success" handler={checkout}>💳 Checkout</button>
1007
- </action-row>
1008
-
1009
- <separator></separator>
1010
- <text-display>⏱️ Session: {formatTime(elapsedTime)}</text-display>
1011
- </components>
1012
- </container>
1013
-
1014
- {:else if view === "details"}
1015
- <container accent-color="FEE75C">
1016
- <components>
1017
- <section>
1018
- <components>
1019
- <text-display>## 📋 Product Details</text-display>
1020
- <text-display>**{products[currentIndex]?.name}**</text-display>
1021
- </components>
1022
- <thumbnail media={products[currentIndex]?.image}></thumbnail>
1023
- </section>
1024
-
1025
- <separator></separator>
1026
- <text-display>{products[currentIndex]?.description}</text-display>
1027
-
1028
- <action-row>
1029
- <button style="Secondary" handler={showBrowse}>◀️ Back</button>
1030
- <button style="Success" handler={addToCart}>🛒 Add to Cart</button>
1031
- </action-row>
1032
-
1033
- <separator></separator>
1034
- <text-display>⏱️ Session: {formatTime(elapsedTime)}</text-display>
1035
- </components>
1036
- </container>
1037
- {/if}
1038
- </components>
1039
- ```
1040
-
1041
- ---
1042
-
1043
- ## API Reference
1044
-
1045
- ### Component Class Methods
1046
-
1047
- ```typescript
1048
- const component = dbi.interaction("my-component");
1049
-
1050
- // Send to interaction or channel
1051
- await component.send(interaction, { data: { count: 0 } });
1052
- await component.send(channel, { data: { count: 0 } });
1053
-
1054
- // Manually destroy a specific instance by ref
1055
- component.destroy(refId);
1056
-
1057
- // Destroy all active instances
1058
- component.destroyAll();
1059
-
1060
- // Get JSON representation
1061
- const json = component.toJSON({ data: { count: 0 } });
1062
- ```
1063
-
1064
- ### Send Options
1065
-
1066
- ```typescript
1067
- interface SendOptions {
1068
- data?: Record<string, any>; // Initial data
1069
- flags?: string[]; // Message flags
1070
- content?: string; // Text content
1071
- ephemeral?: boolean; // Ephemeral message
1072
- reply?: boolean; // Force reply
1073
- followUp?: boolean; // Use followUp instead
1074
- }
1075
- ```
1076
-
1077
- ---
1078
-
1079
- ## Best Practices
1080
-
1081
- 1. **Use `$props()` for initial state** - Destructure with defaults for clean code
1082
- 2. **Mutate `data` for updates** - Don't reassign the entire data object
1083
- 3. **Return cleanup from `onMount`** - Prevents memory leaks
1084
- 4. **Use `noRender()` for background tasks** - Avoid unnecessary renders
1085
- 5. **Set appropriate throttle** - Match your update frequency
1086
- 6. **Use `destroy()` for cleanup** - Clean up timers when done
1087
- 7. **Add type reference** - Get full IDE support
1088
-
1089
- ---
1090
-
1091
- ## Troubleshooting
1092
-
1093
- ### Component not updating?
1094
- - Make sure you're using `data.property = value`, not reassigning `data`
1095
- - Check that `noRender()` wasn't called earlier
1096
-
1097
- ### Timer keeps running after message deleted?
1098
- - Return a cleanup function from `onMount`
1099
- - Or use `onDestroy` to clear intervals
1100
-
1101
- ### Rate limited by Discord?
1102
- - Increase throttle with `setThrottle(500)` or higher
1103
- - The system automatically retries on rate limits (max 3 retries)
1104
-
1105
- ### IDE not showing autocomplete?
1106
- - Add `/// <reference types="@mostfeatured/dbi/svelte" />` at top of script
1107
- - Make sure `@mostfeatured/dbi` is installed
1108
-
1109
- ---
1110
-
1111
- > 📄 **LLM-optimized version:** [llm/SVELTE_COMPONENTS.txt](./llm/SVELTE_COMPONENTS.txt)