discordcn 0.0.1a2__py3-none-any.whl → 0.0.1a4__py3-none-any.whl

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.
@@ -0,0 +1,386 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright: 2026 plun1331, NiceBots.xyz, DiscordCN Contributors
3
+ # Source (modified): https://gist.github.com/plun1331/d8f375922ee7a98c2961530f946ae8ef
4
+
5
+ """Paginator interface module."""
6
+
7
+ # pyright: reportUnknownMemberType=false
8
+
9
+ from abc import ABC, abstractmethod
10
+ from collections.abc import Sequence
11
+ from typing import Any, Generic, TypeVar
12
+
13
+ import discord
14
+ from discord import Interaction
15
+ from typing_extensions import Self, override
16
+
17
+ from discordcn.pycord._utils import maybe_awaitable
18
+
19
+ from ._types import EmojiType
20
+
21
+ T = TypeVar("T", bound="PaginatorInterfaceBase")
22
+
23
+
24
+ class PaginatorInterfaceBase(ABC, discord.ui.DesignerView):
25
+ """ABC defining the interface for paginator views.
26
+
27
+ This ABC specifies the minimum requirements for a view to be compatible
28
+ with paginator controls, ensuring consistent behavior across different
29
+ paginator implementations.
30
+
31
+ Attributes:
32
+ page (int): The current page index (0-based).
33
+ """
34
+
35
+ page: int
36
+
37
+ @property
38
+ @abstractmethod
39
+ def max_page(self) -> int:
40
+ """The maximum page index (0-based)."""
41
+ ...
42
+
43
+ @abstractmethod
44
+ def update(self) -> None:
45
+ """Update the view to reflect the current page state."""
46
+ ...
47
+
48
+
49
+ class PaginatorControlsBase(ABC, discord.ui.ActionRow[T], Generic[T]):
50
+ """ABC defining the interface for paginator controls.
51
+
52
+ This ABC specifies the minimum requirements for a paginator controls
53
+ component, ensuring consistent behavior across different implementations.
54
+ """
55
+
56
+ @abstractmethod
57
+ def __init__(self, view: PaginatorInterfaceBase) -> None:
58
+ """Initialize paginator controls.
59
+
60
+ Args:
61
+ view: The parent paginator view implementing PaginatorInterfaceBase.
62
+ """
63
+ ...
64
+
65
+
66
+ class PageButton(discord.ui.Button[PaginatorInterfaceBase]):
67
+ """Navigation button for jumping to a specific page.
68
+
69
+ Updates the parent view's page index and refreshes the UI when clicked.
70
+
71
+ Attributes:
72
+ page (int): The target page index this button navigates to.
73
+ """
74
+
75
+ def __init__(self, page: int, **kwargs: Any) -> None:
76
+ """Initialize a page navigation button.
77
+
78
+ Args:
79
+ page: The target page index (0-based) to navigate to when clicked.
80
+ **kwargs: Additional keyword arguments passed to discord.ui.Button
81
+ (e.g., ``emoji``, ``style``, ``disabled``).
82
+ """
83
+ super().__init__(**kwargs)
84
+ self.page: int = page
85
+
86
+ @override
87
+ async def callback(self, interaction: discord.Interaction) -> None:
88
+ """Handle button click interactions.
89
+
90
+ Updates the parent view's page index, triggers a view update, and
91
+ edits the message to reflect the new page state.
92
+
93
+ Args:
94
+ interaction: The interaction object from the button click.
95
+
96
+ Raises:
97
+ TypeError: If the button is not attached to a view.
98
+ """
99
+ if self.view is None:
100
+ msg = "This button is not attached to a view."
101
+ raise TypeError(msg)
102
+ v = self.view
103
+ v.page = self.page
104
+ await maybe_awaitable(v.update())
105
+ if interaction.response.is_done():
106
+ await interaction.edit_original_response(view=v)
107
+ else:
108
+ await interaction.response.edit_message(view=v)
109
+
110
+
111
+ class PaginatorIndicatorModal(discord.ui.DesignerModal):
112
+ """Modal for direct page navigation input.
113
+
114
+ Allows users to jump to a specific page by entering a page number.
115
+
116
+ Attributes:
117
+ view (PaginatorInterfaceBase): The parent paginator view.
118
+ input (discord.ui.TextInput): The text input field for page number.
119
+ invalid (str): Error message shown for invalid page numbers.
120
+ """
121
+
122
+ def __init__(
123
+ self, modal_title: str, label_title: str, label_description: str, invalid: str, view: PaginatorInterfaceBase
124
+ ) -> None:
125
+ """Initialize the page navigation modal.
126
+
127
+ Args:
128
+ modal_title: The title displayed at the top of the modal.
129
+ label_title: The label text for the input field.
130
+ label_description: The description text for the input field.
131
+ invalid: Error message shown when an invalid page number is entered.
132
+ view: The parent paginator view.
133
+ """
134
+ self.view: PaginatorInterfaceBase = view
135
+ self.input: discord.ui.TextInput = discord.ui.TextInput(
136
+ style=discord.InputTextStyle.short, max_length=len(str(view.max_page))
137
+ )
138
+ self.invalid: str = invalid
139
+ label: discord.ui.Label[Self] = discord.ui.Label(
140
+ label=label_title, description=label_description, item=self.input
141
+ )
142
+ super().__init__(label, title=modal_title)
143
+
144
+ @override
145
+ async def callback(self, interaction: Interaction) -> None:
146
+ """Handle modal submission.
147
+
148
+ Validates the page number and navigates to the specified page if valid.
149
+
150
+ Args:
151
+ interaction: The interaction object from the modal submission.
152
+ """
153
+ await interaction.response.defer()
154
+ page = self.input.value
155
+ if not page or not page.isdigit() or (int_page := int(page) - 1) > self.view.max_page:
156
+ await interaction.respond(self.invalid, ephemeral=True)
157
+ return
158
+ self.view.page = int_page
159
+ await maybe_awaitable(self.view.update())
160
+ await interaction.edit(view=self.view)
161
+
162
+
163
+ class PageIndicatorButton(discord.ui.Button[PaginatorInterfaceBase]):
164
+ """Button displaying the current page number.
165
+
166
+ Shows the current page position in the format "X/Y". Can optionally be
167
+ made interactable to open a modal for direct page navigation.
168
+
169
+ Attributes:
170
+ disabled (bool): Whether the button is disabled.
171
+ modal_title (str): Title for the page navigation modal.
172
+ label_title (str): Label title for the modal input field.
173
+ label_description (str): Description for the modal input field.
174
+ invalid (str): Error message for invalid page input.
175
+ """
176
+
177
+ def __init__(
178
+ self,
179
+ *,
180
+ label: str,
181
+ interactable: bool,
182
+ modal_title: str,
183
+ label_title: str,
184
+ label_description: str,
185
+ invalid: str,
186
+ ) -> None:
187
+ """Initialize a page indicator button.
188
+
189
+ Args:
190
+ label: The text to display on the button (typically "X/Y" format).
191
+ interactable: Whether clicking the button should open a navigation modal.
192
+ modal_title: Title for the page navigation modal.
193
+ label_title: Label for the modal's input field.
194
+ label_description: Description for the modal's input field.
195
+ invalid: Error message shown for invalid page numbers.
196
+ """
197
+ super().__init__(style=discord.ButtonStyle.secondary, label=label)
198
+ self.disabled: bool = not interactable
199
+ self.modal_title: str = modal_title
200
+ self.label_title: str = label_title
201
+ self.label_description: str = label_description
202
+ self.invalid: str = invalid
203
+
204
+ @override
205
+ async def callback(self, interaction: discord.Interaction) -> None:
206
+ """Handle button click interactions.
207
+
208
+ Opens a modal for direct page navigation when clicked.
209
+
210
+ Args:
211
+ interaction: The interaction object from the button click.
212
+
213
+ Raises:
214
+ TypeError: If the button is not attached to a view.
215
+ """
216
+ if self.view is None:
217
+ msg = "This button is not attached to a view."
218
+ raise TypeError(msg)
219
+
220
+ await interaction.response.send_modal(
221
+ PaginatorIndicatorModal( # pyright: ignore[reportArgumentType]
222
+ modal_title=self.modal_title,
223
+ label_title=self.label_title,
224
+ label_description=self.label_description,
225
+ invalid=self.invalid,
226
+ view=self.view,
227
+ )
228
+ )
229
+
230
+
231
+ class PaginatorControls(PaginatorControlsBase[T], Generic[T]):
232
+ """Navigation controls row for paginator interfaces.
233
+
234
+ Provides five navigation elements: first page, previous page, page indicator,
235
+ next page, and last page. Buttons are automatically disabled when they would
236
+ navigate beyond valid page boundaries.
237
+
238
+ Class Attributes:
239
+ FIRST_EMOJI (EmojiType): Emoji for the "first page" button.
240
+ LEFT_EMOJI (EmojiType): Emoji for the "previous page" button.
241
+ RIGHT_EMOJI (EmojiType): Emoji for the "next page" button.
242
+ LAST_EMOJI (EmojiType): Emoji for the "last page" button.
243
+ INTERACTABLE_INDICATOR (bool): Whether the page indicator opens a navigation modal.
244
+ MODAL_TITLE (str): Title for the page navigation modal.
245
+ MODAL_LABEL_TITLE (str): Label for the modal's input field.
246
+ MODAL_LABEL_DESCRIPTION (str): Description for the modal's input field.
247
+ MODAL_INVALID (str): Error message for invalid page input.
248
+ """
249
+
250
+ FIRST_EMOJI: EmojiType = discord.PartialEmoji(name="⏮️")
251
+ LEFT_EMOJI: EmojiType = discord.PartialEmoji(name="◀️")
252
+ RIGHT_EMOJI: EmojiType = discord.PartialEmoji(name="▶️")
253
+ LAST_EMOJI: EmojiType = discord.PartialEmoji(name="⏭️")
254
+
255
+ INTERACTABLE_INDICATOR: bool = True
256
+ MODAL_TITLE: str = "Page Navigation"
257
+ MODAL_LABEL_TITLE: str = "Page Number:"
258
+ MODAL_LABEL_DESCRIPTION: str = "Input a page number to jump to that page."
259
+ MODAL_INVALID: str = "Please input a valid page number."
260
+
261
+ def __init__(
262
+ self,
263
+ view: T,
264
+ ) -> None:
265
+ """Initialize paginator navigation controls.
266
+
267
+ Creates a row of navigation buttons with appropriate enabled/disabled
268
+ states based on the current page position.
269
+
270
+ Args:
271
+ view: The parent paginator view implementing PaginatorInterfaceBase.
272
+ """
273
+ discord.ui.ActionRow.__init__(self)
274
+ self.add_item(
275
+ PageButton(
276
+ 0,
277
+ emoji=self.FIRST_EMOJI,
278
+ style=discord.ButtonStyle.primary,
279
+ disabled=view.page == 0,
280
+ ),
281
+ )
282
+ self.add_item(
283
+ PageButton(
284
+ view.page - 1,
285
+ emoji=self.LEFT_EMOJI,
286
+ style=discord.ButtonStyle.secondary,
287
+ disabled=view.page == 0,
288
+ )
289
+ )
290
+ self.add_item(
291
+ PageIndicatorButton(
292
+ label=f"{view.page + 1}/{view.max_page + 1}",
293
+ interactable=self.INTERACTABLE_INDICATOR,
294
+ modal_title=self.MODAL_TITLE,
295
+ label_title=self.MODAL_LABEL_TITLE,
296
+ label_description=self.MODAL_LABEL_DESCRIPTION,
297
+ invalid=self.MODAL_INVALID,
298
+ ),
299
+ )
300
+ self.add_item(
301
+ PageButton(
302
+ view.page + 1,
303
+ emoji=self.RIGHT_EMOJI,
304
+ style=discord.ButtonStyle.secondary,
305
+ disabled=view.page == view.max_page,
306
+ )
307
+ )
308
+ self.add_item(
309
+ PageButton(
310
+ view.max_page,
311
+ emoji=self.LAST_EMOJI,
312
+ style=discord.ButtonStyle.primary,
313
+ disabled=view.page == view.max_page,
314
+ )
315
+ )
316
+
317
+
318
+ class PaginatorInterface(PaginatorInterfaceBase):
319
+ """Multi-page view with automatic navigation controls.
320
+
321
+ Manages navigation between multiple pages of UI components, automatically
322
+ displaying the appropriate page content and navigation controls. Each page
323
+ is dynamically loaded when navigating between pages.
324
+
325
+ Attributes:
326
+ pages (Sequence[Sequence[discord.ui.ViewItem[discord.ui.DesignerView]]]):
327
+ The sequence of pages, where each page is a sequence of view items.
328
+ page (int): The current page index (0-based).
329
+ controls (type[PaginatorControlsBase[Self]]): The paginator controls
330
+ class to use for navigation.
331
+ """
332
+
333
+ def __init__(
334
+ self,
335
+ pages: Sequence[Sequence[discord.ui.ViewItem[discord.ui.DesignerView]]],
336
+ controls: type[PaginatorControlsBase[Self]] = PaginatorControls,
337
+ ) -> None:
338
+ """Initialize a paginator view.
339
+
340
+ Args:
341
+ pages: A sequence of pages, where each page is a sequence of view items
342
+ (buttons, selects, etc.) to display on that page. Navigation controls
343
+ are automatically added to each page.
344
+ controls: The paginator controls class to use for navigation. Defaults
345
+ to PaginatorControls.
346
+ """
347
+ super().__init__(timeout=300)
348
+ self.pages: Sequence[Sequence[discord.ui.ViewItem[discord.ui.DesignerView]]] = pages
349
+ self.page: int = 0
350
+ self.controls: type[PaginatorControlsBase[Self]] = controls
351
+ self.update()
352
+
353
+ @property
354
+ @override
355
+ def max_page(self) -> int:
356
+ """The maximum page index (0-based).
357
+
358
+ Returns:
359
+ The index of the last page.
360
+ """
361
+ return len(self.pages) - 1
362
+
363
+ @override
364
+ def update(self) -> None:
365
+ """Update the view to display the current page.
366
+
367
+ Clears all existing items, adds items from the current page, and
368
+ appends navigation controls. This method is called automatically
369
+ when navigating between pages.
370
+ """
371
+ self.clear_items()
372
+ for item in self.pages[self.page]:
373
+ self.add_item(item)
374
+ if len(self.pages) > 1:
375
+ self.add_item(self.controls(self))
376
+
377
+
378
+ __all__ = (
379
+ "PageButton",
380
+ "PageIndicatorButton",
381
+ "PaginatorControls",
382
+ "PaginatorControlsBase",
383
+ "PaginatorIndicatorModal",
384
+ "PaginatorInterface",
385
+ "PaginatorInterfaceBase",
386
+ )
@@ -0,0 +1,93 @@
1
+ Metadata-Version: 2.4
2
+ Name: discordcn
3
+ Version: 0.0.1a4
4
+ Summary: An unofficial, community-driven implementation for Discord app interface patterns.
5
+ Project-URL: Homepage, https://github.com/nicebots-xyz/discordcn-py
6
+ Project-URL: source_archive, https://github.com/nicebots-xyz/discordcn-py/archive/b4a168d863738ba6dfaa0f6dfa823336eebd447f.zip
7
+ Author-email: Paillat-dev <me@paillat.dev>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: discord
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: <3.15,>=3.10
23
+ Requires-Dist: typing-extensions>=4.15.0
24
+ Provides-Extra: dpy
25
+ Requires-Dist: discord-py==2.6.4; extra == 'dpy'
26
+ Provides-Extra: pycord
27
+ Requires-Dist: py-cord==2.7; extra == 'pycord'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # discordcn-py
31
+
32
+ Python library implementations for [DiscordCN](https://discordcn.dev) - an unofficial, community-driven reference for
33
+ Discord app interface patterns.
34
+
35
+ > \**NOTE**:
36
+ > This project is NOT made by Discord and does NOT represent official Discord UI guidelines. For official resources, see
37
+ > the [Discord Developer Docs](https://discord.com/developers/docs/components/overview).
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ pip install discordcn
43
+ ```
44
+
45
+ ### With Optional Dependencies
46
+
47
+ For Pycord:
48
+
49
+ ```bash
50
+ pip install discordcn[pycord]
51
+ ```
52
+
53
+ For discord.py:
54
+
55
+ ```bash
56
+ pip install discordcn[dpy]
57
+ ```
58
+
59
+ ## Requirements
60
+
61
+ * Python >=3.10, <3.15
62
+ * typing-extensions >=4.15.0
63
+
64
+ ## Status
65
+
66
+ This project is currently in alpha development. Features and API may change.
67
+
68
+ ## Related Projects
69
+
70
+ * [discordcn](https://github.com/nicebots-xyz/discordcn) - Main project with documentation and examples
71
+ * [discord.builders](https://github.com/StartITBot/discord.builders) - Code generation engine and SDK
72
+
73
+ ## Community & Support
74
+
75
+ * **Website:** [discordcn.dev](https://discordcn.dev)
76
+ * **Issues:** [GitHub Issues](https://github.com/nicebots-xyz/discordcn-py/issues)
77
+ * **Discord:** [NiceBots Discord](https://discord.gg/4Rj2bcZ7MD)
78
+
79
+ ## Contributing & Governance
80
+
81
+ For contributing guidelines, security policies, and code of conduct, please refer to the
82
+ main [discordcn repository](https://github.com/nicebots-xyz/discordcn):
83
+
84
+ * [CONTRIBUTING.md](https://github.com/nicebots-xyz/discordcn/blob/master/CONTRIBUTING.md)
85
+ * [SECURITY.md](https://github.com/nicebots-xyz/discordcn/blob/master/SECURITY.md)
86
+ * [CODE\_OF\_CONDUCT.md](https://github.com/nicebots-xyz/discordcn/blob/master/CODE_OF_CONDUCT.md)
87
+ * [SUPPORT.md](https://github.com/nicebots-xyz/discordcn/blob/master/SUPPORT.md)
88
+
89
+ ## License
90
+
91
+ MIT License - see [LICENSE](https://github.com/nicebots-xyz/discordcn-py/tree/masterLICENSE) file for details.
92
+
93
+ Discord is a trademark of Discord Inc. and is not affiliated with DiscordCN or NiceBots.
@@ -0,0 +1,17 @@
1
+ discordcn/__init__.py,sha256=M-tPvQrNqMrw6MX2TX2v8XMEc_nnaF_PUdkjAUEnWEA,30
2
+ discordcn/_version.py,sha256=wvECKgt5TpWmMZcvP9PJmnR2v91tADC9a9ydpd1BOlM,712
3
+ discordcn/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ discordcn/_utils/__init__.py,sha256=Kjk4SSBNj8W1tez3WFgBGcCXiOCF_k5rUw_u42hzkyk,70
5
+ discordcn/_utils/dependencies.py,sha256=ipGc1DYJfbV7zgLwWN9sZnWjJseh0zqHPm-OabGFEKM,5932
6
+ discordcn/pycord/__init__.py,sha256=czJI77-ZYNOZxl2IN7KUmQW6rZqTnvhfguoFtH36nM4,665
7
+ discordcn/pycord/_utils/__init__.py,sha256=qdiz75w8BWBe3Hx_pEjRjLOVIXCLqAesHsf0jERIUQI,228
8
+ discordcn/pycord/_utils/_asyncio.py,sha256=C1mvWsaysEcI-r_c8rVPq-Xqm5vFxjIRaH5o8mNOHyc,756
9
+ discordcn/pycord/interfaces/__init__.py,sha256=f1gTnkAFU-Rq2SwxMZKa_MaSedfUixPZhHQqKHljNhg,570
10
+ discordcn/pycord/interfaces/_types.py,sha256=wC30UtEU81qk5_PnVJb8tZIwpQ5LhpoSknlETeuAWmU,353
11
+ discordcn/pycord/interfaces/accordion.py,sha256=VbXM56rmJZvSP4Tg4iG3v2T9rVgxUuHzHVsO9_ZPK6g,17799
12
+ discordcn/pycord/interfaces/confirm.py,sha256=LIXZRVk9wnhv8PDzTY34U3AiwZgOdKG2bnlxS8rfdqs,8194
13
+ discordcn/pycord/interfaces/paginator.py,sha256=1TtXfXCz1UYzvzEgQtj0kh7hMQhe4DPaixhmxYYudds,13637
14
+ discordcn-0.0.1a4.dist-info/METADATA,sha256=sm_W-5h9U_3FdPCm55iUB1AUEr_FL5eVicfPgriiAro,3187
15
+ discordcn-0.0.1a4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
+ discordcn-0.0.1a4.dist-info/licenses/LICENSE,sha256=GtM5nF4RszKjPj7YvDquoqanaubrF0KhbD8-k6t_648,1069
17
+ discordcn-0.0.1a4.dist-info/RECORD,,
@@ -1,26 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: discordcn
3
- Version: 0.0.1a2
4
- Dynamic: Project-URL
5
- Summary: TBD
6
- Author-email: Paillat-dev <me@paillat.dev>
7
- License-Expression: MIT
8
- License-File: LICENSE
9
- Keywords: discord
10
- Classifier: Development Status :: 3 - Alpha
11
- Classifier: Intended Audience :: Developers
12
- Classifier: License :: OSI Approved :: MIT License
13
- Classifier: Operating System :: OS Independent
14
- Classifier: Programming Language :: Python :: 3 :: Only
15
- Classifier: Programming Language :: Python :: 3.10
16
- Classifier: Programming Language :: Python :: 3.11
17
- Classifier: Programming Language :: Python :: 3.12
18
- Classifier: Programming Language :: Python :: 3.13
19
- Classifier: Programming Language :: Python :: 3.14
20
- Classifier: Typing :: Typed
21
- Requires-Python: <3.15,>=3.10
22
- Description-Content-Type: text/markdown
23
-
24
- # discordcn (Python)
25
-
26
- Reserved for future use by [nicebots.xyz](https://nicebots.xyz/). Please contact me@paillat.dev for more information.
@@ -1,7 +0,0 @@
1
- discordcn/__init__.py,sha256=1Ol9uOvBq1H9Hw4UIwV1CCw6vEaAaYEDnuCqMa7MrpM,63
2
- discordcn/_version.py,sha256=cJu3a6F6LwSc1cJz18IpVaxyDouCVdfx5VAGQZd4tFY,712
3
- discordcn/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- discordcn-0.0.1a2.dist-info/METADATA,sha256=66UYQaB3Gl34FuP69wIXvUHlhXQFDkfEUecYlqlXZO0,932
5
- discordcn-0.0.1a2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
6
- discordcn-0.0.1a2.dist-info/licenses/LICENSE,sha256=GtM5nF4RszKjPj7YvDquoqanaubrF0KhbD8-k6t_648,1069
7
- discordcn-0.0.1a2.dist-info/RECORD,,