diff --git a/changelog/392.doc.rst b/changelog/392.doc.rst new file mode 100644 index 0000000000..386bb9842f --- /dev/null +++ b/changelog/392.doc.rst @@ -0,0 +1,5 @@ +Split the monolithic API Reference pages into multiple sub-references. +- Main ``disnake`` API reference can now be found at :doc:`API Reference `. +- ``disnake.ext.commands`` API reference is now under :doc:`Commands API Reference `. +- Legacy ``api.html`` and ``ext/commands/api.html`` pages are deprecated. +- - Links with pre-existing references (eg ``/api.html#disnake.AppInfo``) will be redirected to their appropriate page. diff --git a/disnake/abc.py b/disnake/abc.py index 918abca639..567189e9b9 100644 --- a/disnake/abc.py +++ b/disnake/abc.py @@ -98,7 +98,7 @@ class Snowflake(Protocol): """An ABC that details the common operations on a Discord model. - Almost all :ref:`Discord models ` meet this + Almost all :ref:`Discord models ` meet this abstract base class. If you want to create a snowflake on your own, consider using diff --git a/disnake/activity.py b/disnake/activity.py index 3f647a0e19..e5e4321af2 100644 --- a/disnake/activity.py +++ b/disnake/activity.py @@ -139,6 +139,7 @@ def to_dict(self) -> ActivityPayload: # tag type for user-settable activities class BaseActivity(_BaseActivity): """The base activity that all user-settable activities inherit from. + A user-settable activity is one that can be used in :meth:`Client.change_presence`. The following types currently count as user-settable: diff --git a/disnake/client.py b/disnake/client.py index 2fe572ce83..56d60280f8 100644 --- a/disnake/client.py +++ b/disnake/client.py @@ -1538,7 +1538,7 @@ def wait_for( In case the event returns multiple arguments, a :class:`tuple` containing those arguments is returned instead. Please check the - :ref:`documentation ` for a list of events and their + :ref:`documentation ` for a list of events and their parameters. This function returns the **first event that meets the requirements**. @@ -1594,7 +1594,7 @@ def check(reaction, user): Parameters ---------- event: Union[:class:`str`, :class:`.Event`] - The event name, similar to the :ref:`event reference `, + The event name, similar to the :ref:`event reference `, but without the ``on_`` prefix, to wait for. It's recommended to use :class:`.Event`. check: Optional[Callable[..., :class:`bool`]] @@ -1614,7 +1614,7 @@ def check(reaction, user): Any Returns no arguments, a single argument, or a :class:`tuple` of multiple arguments that mirrors the parameters passed in the - :ref:`event reference `. + :ref:`event `. """ future = self.loop.create_future() if check is None: @@ -1639,7 +1639,7 @@ def _check(*args) -> bool: def event(self, coro: CoroT) -> CoroT: """A decorator that registers an event to listen to. - You can find more info about the events on the :ref:`documentation below `. + You can find more info about the events in the :ref:`documentation `. The events must be a :ref:`coroutine `, if not, :exc:`TypeError` is raised. diff --git a/disnake/enums.py b/disnake/enums.py index 6e093dd70b..d3ccef8455 100644 --- a/disnake/enums.py +++ b/disnake/enums.py @@ -1001,14 +1001,14 @@ class Event(Enum): """Called when a `Guild` creates a new `Role`. Represents the :func:`on_guild_role_create` event. """ - guild_role_update = "guild_role_update" - """Called when a `Guild` updates a `Role`. - Represents the :func:`on_guild_role_update` event. - """ guild_role_delete = "guild_role_delete" """Called when a `Guild` deletes a `Role`. Represents the :func:`on_guild_role_delete` event. """ + guild_role_update = "guild_role_update" + """Called when a `Guild` updates a `Role`. + Represents the :func:`on_guild_role_update` event. + """ guild_emojis_update = "guild_emojis_update" """Called when a `Guild` adds or removes `Emoji`. Represents the :func:`on_guild_emojis_update` event. @@ -1069,6 +1069,10 @@ class Event(Enum): """Called when an `AutoModRule` is deleted. Represents the :func:`on_automod_rule_delete` event. """ + audit_log_entry_create = "audit_log_entry_create" + """Called when an audit log entry is created. + Represents the :func:`on_audit_log_entry_create` event. + """ integration_create = "integration_create" """Called when an integration is created. Represents the :func:`on_integration_create` event. @@ -1085,14 +1089,14 @@ class Event(Enum): """Called when a `Member` joins a `Guild`. Represents the :func:`on_member_join` event. """ - member_update = "member_update" - """Called when a `Member` updates their profile. - Represents the :func:`on_member_update` event. - """ member_remove = "member_remove" """Called when a `Member` leaves a `Guild`. Represents the :func:`on_member_remove` event. """ + member_update = "member_update" + """Called when a `Member` updates their profile. + Represents the :func:`on_member_update` event. + """ raw_member_remove = "raw_member_remove" """Called when a member leaves a `Guild` regardless of the member cache. Represents the :func:`on_raw_member_remove` event. @@ -1101,10 +1105,6 @@ class Event(Enum): """Called when a member updates their profile regardless of the member cache. Represents the :func:`on_raw_member_update` event. """ - audit_log_entry_create = "audit_log_entry_create" - """Called when an audit log entry is created. - Represents the :func:`on_audit_log_entry_create` event. - """ member_ban = "member_ban" """Called when user gets banned from a `Guild`. Represents the :func:`on_member_ban` event. @@ -1129,14 +1129,14 @@ class Event(Enum): """Called when a `StageInstance` is created for a `StageChannel`. Represents the :func:`on_stage_instance_create` event. """ - stage_instance_update = "stage_instance_update" - """Called when a `StageInstance` is updated. - Represents the :func:`on_stage_instance_update` event. - """ stage_instance_delete = "stage_instance_delete" """Called when a `StageInstance` is deleted for a `StageChannel`. Represents the :func:`on_stage_instance_delete` event. """ + stage_instance_update = "stage_instance_update" + """Called when a `StageInstance` is updated. + Represents the :func:`on_stage_instance_update` event. + """ application_command = "application_command" """Called when an application command is invoked. Represents the :func:`on_application_command` event. @@ -1181,14 +1181,14 @@ class Event(Enum): """Called when messages are bulk deleted. Represents the :func:`on_bulk_message_delete` event. """ - raw_message_delete = "raw_message_delete" - """Called when a message is deleted regardless of the message being in the internal message cache or not. - Represents the :func:`on_raw_message_delete` event. - """ raw_message_edit = "raw_message_edit" """Called when a message is edited regardless of the state of the internal message cache. Represents the :func:`on_raw_message_edit` event. """ + raw_message_delete = "raw_message_delete" + """Called when a message is deleted regardless of the message being in the internal message cache or not. + Represents the :func:`on_raw_message_delete` event. + """ raw_bulk_message_delete = "raw_bulk_message_delete" """Called when a bulk delete is triggered regardless of the messages being in the internal message cache or not. Represents the :func:`on_raw_bulk_message_delete` event. diff --git a/disnake/ext/commands/bot.py b/disnake/ext/commands/bot.py index 8640ea86f2..5e135d829c 100644 --- a/disnake/ext/commands/bot.py +++ b/disnake/ext/commands/bot.py @@ -170,7 +170,7 @@ class Bot(BotBase, InteractionBotBase, disnake.Client): help_command: Optional[:class:`.HelpCommand`] The help command implementation to use. This can be dynamically set at runtime. To remove the help command pass ``None``. For more - information on implementing a help command, see :ref:`ext_commands_help_command`. + information on implementing a help command, see :ref:`ext_commands_api_help_commands`. This can be provided as a parameter at creation. diff --git a/disnake/ext/commands/core.py b/disnake/ext/commands/core.py index 407ed72c91..53d88a418e 100644 --- a/disnake/ext/commands/core.py +++ b/disnake/ext/commands/core.py @@ -904,7 +904,7 @@ async def invoke(self, ctx: Context) -> None: await self.prepare(ctx) # terminate the invoked_subcommand chain. - # since we're in a regular command (and not a group) then + # since we're in a regular prefix command (and not a group) then # the invoked subcommand is None. ctx.invoked_subcommand = None ctx.subcommand_passed = None diff --git a/disnake/ext/commands/interaction_bot_base.py b/disnake/ext/commands/interaction_bot_base.py index 33795711e6..25308c3649 100644 --- a/disnake/ext/commands/interaction_bot_base.py +++ b/disnake/ext/commands/interaction_bot_base.py @@ -496,7 +496,7 @@ def slash_command( extras: Optional[Dict[str, Any]] = None, **kwargs, ) -> Callable[[CommandCallback], InvokableSlashCommand]: - """A shortcut decorator that invokes :func:`.slash_command` and adds it to + """A shortcut decorator that invokes :func:`~disnake.ext.commands.slash_command` and adds it to the internal command list. Parameters @@ -589,7 +589,7 @@ def user_command( ) -> Callable[ [InteractionCommandCallback[CogT, UserCommandInteraction, P]], InvokableUserCommand ]: - """A shortcut decorator that invokes :func:`.user_command` and adds it to + """A shortcut decorator that invokes :func:`~disnake.ext.commands.user_command` and adds it to the internal command list. Parameters @@ -666,7 +666,7 @@ def message_command( ) -> Callable[ [InteractionCommandCallback[CogT, MessageCommandInteraction, P]], InvokableMessageCommand ]: - """A shortcut decorator that invokes :func:`.message_command` and adds it to + """A shortcut decorator that invokes :func:`~disnake.ext.commands.message_command` and adds it to the internal command list. Parameters diff --git a/docs/_static/sidebar.js b/docs/_static/sidebar.js index 7394c7b974..91a2f63ca6 100644 --- a/docs/_static/sidebar.js +++ b/docs/_static/sidebar.js @@ -35,7 +35,6 @@ class Sidebar { let next = ref.nextElementSibling; if (next && next.tagName === "UL") { - let icon = document.createElement('span'); icon.className = 'material-icons collapsible-arrow expanded'; icon.innerText = 'expand_more'; @@ -54,24 +53,36 @@ class Sidebar { ref.classList.add('ref-internal-padding') ref.parentNode.insertBefore(icon, ref); + + // collapse all top-level toc entries, except the current page's + // (i.e. all entries that don't contain a `#`) + const refUrl = new URL(ref.href); + if (!refUrl.hash) { + // `false` to update immediately + this.collapseSection(icon, false); + } } } } - collapseSection(icon) { + collapseSection(icon, defer = true) { icon.classList.remove('expanded'); icon.classList.add('collapsed'); let children = icon.nextElementSibling.nextElementSibling; // // --> - setTimeout(() => children.style.display = "none", 75) + const update = () => children.style.display = "none"; + if (defer) setTimeout(update, 75); + else update(); } - expandSection(icon) { + expandSection(icon, defer = true) { icon.classList.remove('collapse'); icon.classList.add('expanded'); let children = icon.nextElementSibling.nextElementSibling; - setTimeout(() => children.style.display = "block", 75) + const update = () => children.style.display = "block"; + if (defer) setTimeout(update, 75); + else update(); } setActiveLink(section) { @@ -92,6 +103,13 @@ class Sidebar { } } + scrollToCurrent() { + const currentSection = this.element.querySelector("li.current"); + if (currentSection) { + // setTimeout(..., 0) to avoid layout race condition + setTimeout(() => currentSection.scrollIntoView({block: "center"}), 0); + } + } } function getCurrentSection() { @@ -101,10 +119,12 @@ function getCurrentSection() { } else { if (sections) { + const headerOffset = document.querySelector("main").offsetTop; // height of header sections.forEach(section => { - let rect = section.getBoundingClientRect(); - // offset to give space for the sticky header - if (rect.top - 90 + document.body.offsetTop < 1) { + const rect = section.getBoundingClientRect(); + // plus offset for more leniency + // (section doesn't have to be scrolled all the way to the top to be considered active) + if (rect.top < headerOffset + 50) { currentSection = section; } }); @@ -113,9 +133,11 @@ function getCurrentSection() { return currentSection; } +// create interactive sidebar document.addEventListener('DOMContentLoaded', () => { sidebar = new Sidebar(document.getElementById('sidebar')); sidebar.createCollapsableSections(); + sidebar.scrollToCurrent(); window.addEventListener('scroll', () => { sidebar.setActiveLink(getCurrentSection()); diff --git a/docs/_templates/api_redirect.js_t b/docs/_templates/api_redirect.js_t new file mode 100644 index 0000000000..ee4b68b64e --- /dev/null +++ b/docs/_templates/api_redirect.js_t @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT + +"use strict"; + +const redirects_map = {{ redirect_data }}; + +(() => { + let url = new URL(document.location.href); + + if (!url.pathname.endsWith("/api.html")) return; + + // URL_ROOT is relative to `url`, and points to e.g. `/en/latest/` + const root = new URL(DOCUMENTATION_OPTIONS.URL_ROOT, url); + if (!root.pathname.endsWith("/")) root.pathname += "/"; + + const targetPath = redirects_map[url.hash.slice(1)]; + + let newUrl = null; + if (targetPath) { + // Get current path relative to documentation root + // `/en/latest/xyz/api.html` => `xyz/api/` + let expectedPrefix = url.pathname.slice(root.pathname.length).replace(/\.html$/, "/"); + + // The target path (`xyz/api/events.html`) should start with `xyz/api/` + if (targetPath.startsWith(expectedPrefix)) { + newUrl = new URL(targetPath, root); + newUrl.hash = url.hash; + } + } + + // If it's still unset, the #hash is unknown or the target doesn't match the current section; + // in that case, just redirect to the relevant index page. + if (newUrl === null) { + newUrl = new URL(url); + newUrl.pathname = newUrl.pathname.replace(/\/api.html$/, "/api/index.html"); + newUrl.hash = ""; + } + + console.log(`Redirecting to ${newUrl.href}`); + window.location.replace(newUrl); +})(); diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html index 8235b82e15..ef97d73439 100644 --- a/docs/_templates/layout.html +++ b/docs/_templates/layout.html @@ -7,7 +7,6 @@ {{ title|striptags|e }}{{ titlesuffix }} {%- block extrahead %} {% endblock %} - {%- block css %} {%- for css in css_files %} @@ -22,6 +21,10 @@ {%- block scripts %} + {% if ("/" + pagename).endswith("/api") %} + + + {% endif %} {%- for js in script_files %} {{ js_tag(js) }} {%- endfor %} @@ -66,7 +69,7 @@
{#- The sidebar component #}