Repository: League-of-Foundry-Developers/foundry-vtt-types
Pull request #2592 (2024-07-06 02:52:05)

Updates `string.mjs`, `number.mjs`, and `math.mjs`
Solves #2587, #2586,  and #2585.

### Math.mjs
- Adds SQRT3 and SQRT1_3 values.
- Adds Math.clamp.
- Deprecates Math.clamped.
- Deprecates Math.normalizeDegrees with the base parameter.
- Removes Math.fastRound.

### String.mjs
- Adds compare function.

### Number.mjs
- Updates function definition text.


Patch:
diff --git a/src/foundry/client-esm/applications/api/dialog.d.mts b/src/foundry/client-esm/applications/api/dialog.d.mts
index 165f70db9..67a9058e5 100644
--- a/src/foundry/client-esm/applications/api/dialog.d.mts
+++ b/src/foundry/client-esm/applications/api/dialog.d.mts
@@ -1,3 +1,5 @@
+import type { DeepPartial } from "../../../../types/utils.d.mts";
+import type { ApplicationConfiguration, ApplicationRenderContext } from "../_types.mjs";
 import type ApplicationV2 from "./application.d.mts";
 
 /**
@@ -60,4 +62,205 @@ import type ApplicationV2 from "./application.d.mts";
  * }).render({ force: true });
  * ```
  */
-export default class DialogV2 extends ApplicationV2 {}
+declare class DialogV2 extends ApplicationV2<DialogV2.DialogV2Configuration> {
+  static override DEFAULT_OPTIONS: DeepPartial<ApplicationConfiguration>;
+
+  protected override _initializeApplicationOptions(
+    options: Partial<DialogV2.DialogV2Configuration>,
+  ): Partial<DialogV2.DialogV2Configuration>;
+
+  protected override _renderHTML(
+    _context: ApplicationRenderContext,
+    _options: DialogV2.DialogV2Configuration,
+  ): Promise<HTMLFormElement>;
+
+  /**
+   * Render configured buttons.
+   */
+  protected _renderButtons(): string;
+
+  /**
+   * Handle submitting the dialog.
+   * @param target - The button that was clicked or the default button.
+   * @param event - The triggering event.
+   */
+  protected _onSubmit(target: HTMLButtonElement, event: PointerEvent | SubmitEvent): Promise<DialogV2>;
+
+  protected override _onFirstRender(
+    _context: ApplicationRenderContext,
+    _options: Partial<DialogV2.DialogV2Configuration>,
+  ): void;
+
+  protected override _replaceHTML(
+    result: HTMLFormElement,
+    content: HTMLElement,
+    _options: Partial<DialogV2.DialogV2Configuration>,
+  ): void;
+
+  /**
+   * Handle keypresses within the dialog.
+   * @param event - The triggering event.
+   */
+  protected _onKeyDown(event: KeyboardEvent): void;
+
+  /**
+   * @param event - The originating click event.
+   * @param target - The button element that was clicked.
+   */
+  protected static _onClickButton(this: DialogV2, event: PointerEvent, target: HTMLButtonElement): void;
+
+  /**
+   * A utility helper to generate a dialog with yes and no buttons.
+   * @returns Resolves to true if the yes button was pressed, or false if the no button
+   *          was pressed. If additional buttons were provided, the Promise resolves to
+   *          the identifier of the one that was pressed, or the value returned by its
+   *          callback. If the dialog was dismissed, and rejectClose is false, the
+   *          Promise resolves to null.
+   */
+  static confirm<Options extends Partial<DialogV2.DialogV2WaitOptions>, YesReturn = true, NoReturn = false>(
+    options?: Options & {
+      /** Options to overwrite the default yes button configuration. */
+      yes?: DialogV2.DialogV2Button<YesReturn>;
+      /** Options to overwrite the default no button configuration. */
+      no?: DialogV2.DialogV2Button<NoReturn>;
+    },
+  ): Promise<YesReturn | NoReturn | InferButtonReturnTypes<Options> | InferDismissType<Options>>;
+
+  /**
+   * A utility helper to generate a dialog with a single confirmation button.
+   * @returns  - Resolves to the identifier of the button used to submit the dialog,
+   *             or the value returned by that button's callback. If the dialog was
+   *             dismissed, and rejectClose is false, the Promise resolves to null.
+   */
+  static prompt<Options extends Partial<DialogV2.DialogV2WaitOptions>, OKReturn = string>(
+    options?: Options & {
+      /** Options to overwrite the default confirmation button configuration. */
+      ok?: Partial<DialogV2.DialogV2Button<OKReturn>>;
+    },
+  ): Promise<OKReturn | InferButtonReturnTypes<Options> | InferDismissType<Options>>;
+
+  /**
+   * Spawn a dialog and wait for it to be dismissed or submitted.
+   * @returns Resolves to the identifier of the button used to submit the
+   *          dialog, or the value returned by that button's callback. If the
+   *          dialog was dismissed, and rejectClose is false, the Promise
+   *          resolves to null.
+   * @remarks Despite being the `wait` function this doesn't actually use that interface
+   */
+  static wait<Options extends Partial<DialogV2.DialogV2Configuration>>(
+    options?: Options & {
+      /** A function to invoke whenever the dialog is rendered. */
+      render?: DialogV2.DialogV2RenderCallback;
+      /** A function to invoke when the dialog is closed under any circumstances. */
+      close?: DialogV2.DialogV2CloseCallback;
+      /**
+       * Throw a Promise rejection if the dialog is dismissed.
+       * @defaultValue `true`
+       */
+      rejectClose?: boolean;
+    },
+  ): Promise<InferButtonReturnTypes<Options> | InferDismissType<Options>>;
+}
+
+declare namespace DialogV2 {
+  type DialogV2Button<CallBackReturn> = {
+    /**
+     * The button action identifier.
+     */
+    action: string;
+    /**
+     * The button label. Will be localized.
+     */
+    label: string;
+    /**
+     * FontAwesome icon classes.
+     */
+    icon?: string;
+    /**
+     * CSS classes to apply to the button.
+     */
+    class?: string;
+    /**
+     * Whether this button represents the default action to take if the user
+     * submits the form without pressing a button, i.e. with an Enter
+     * keypress.
+     */
+    default?: boolean;
+    /**
+     * A function to invoke when the button is clicked. The value returned
+     * from this function will be used as the dialog's submitted value.
+     * Otherwise, the button's identifier is used.
+     */
+    callback?: DialogV2ButtonCallback<CallBackReturn>;
+  };
+
+  type DialogV2ButtonCallback<T> = (
+    event: PointerEvent | SubmitEvent,
+    button: HTMLButtonElement,
+    dialog: HTMLDialogElement,
+  ) => Promise<T>;
+
+  interface DialogV2Configuration extends ApplicationConfiguration {
+    /**
+     * Modal dialogs prevent interaction with the rest of the UI until they
+     * are dismissed or submitted.
+     */
+    modal?: boolean;
+
+    /**
+     * Button configuration.
+     */
+    buttons: DialogV2Button<any>[];
+
+    /**
+     * The dialog content.
+     */
+    content?: string;
+
+    /**
+     * A function to invoke when the dialog is submitted. This will not be
+     * called if the dialog is dismissed.
+     */
+    submit?: DialogV2SubmitCallback;
+  }
+
+  type DialogV2RenderCallback = (event: Event, dialog: HTMLDialogElement) => any;
+
+  type DialogV2CloseCallback = (event: Event, dialog: DialogV2) => any;
+
+  type DialogV2SubmitCallback = (result: any) => Promise<void>;
+
+  interface DialogV2WaitOptions extends DialogV2Configuration {
+    /**
+     * A synchronous function to invoke whenever the dialog is rendered.
+     */
+    render?: DialogV2RenderCallback;
+    /**
+     * A synchronous function to invoke when the dialog is closed under any
+     * circumstances.
+     */
+    close?: DialogV2CloseCallback;
+    /**
+     * Throw a Promise rejection if the dialog is dismissed.
+     * @defaultValue `true`
+     */
+    rejectClose?: boolean;
+  }
+}
+
+type InferDismissType<Options extends Partial<DialogV2.DialogV2WaitOptions>> = Options["rejectClose"] extends boolean
+  ? Options["rejectClose"] extends true
+    ? never
+    : null
+  : never;
+
+type InferButtonReturnTypes<Options extends Partial<DialogV2.DialogV2WaitOptions>> =
+  Options["buttons"] extends Array<infer Button>
+    ? Button extends DialogV2.DialogV2Button<infer Callback>
+      ? Button["callback"] extends Function
+        ? Callback
+        : string
+      : never
+    : never;
+
+export default DialogV2;
diff --git a/src/foundry/client-esm/applications/api/handlebars-application.d.mts b/src/foundry/client-esm/applications/api/handlebars-application.d.mts
index 92cc90ee4..af4f99b57 100644
--- a/src/foundry/client-esm/applications/api/handlebars-application.d.mts
+++ b/src/foundry/client-esm/applications/api/handlebars-application.d.mts
@@ -2,43 +2,6 @@ import type { DeepPartial, Mixin } from "../../../../types/utils.d.mts";
 import type { ApplicationFormConfiguration, ApplicationRenderContext, ApplicationRenderOptions } from "../_types.d.mts";
 import type ApplicationV2 from "./application.d.mts";
 
-// TODO: How does this merge with DocumentSheetRenderOptions?
-interface HandlebarsRenderOptions extends ApplicationRenderOptions {
-  parts: string[];
-}
-
-type HandlebarsTemplatePart = {
-  /**
-   * The template entry-point for the part
-   */
-  template: string;
-  /**
-   * A CSS id to assign to the top-level element of the rendered part.
-   * This id string is automatically prefixed by the application id.
-   */
-  id?: string;
-  /**
-   * An array of CSS classes to apply to the top-level element of the
-   * rendered part.
-   */
-  classes?: string[];
-  /**
-   * An array of templates that are required to render the part.
-   * If omitted, only the entry-point is inferred as required.
-   */
-  templates?: string[];
-  /**
-   * An array of selectors within this part whose scroll positions should
-   * be persisted during a re-render operation. A blank string is used
-   * to denote that the root level of the part is scrollable.
-   */
-  scrollabe?: string[];
-  /**
-   * A registry of forms selectors and submission handlers.
-   */
-  forms?: Record<string, ApplicationFormConfiguration>;
-};
-
 /**
  * The mixed application class augmented with [Handlebars](https://handlebarsjs.com) template rendering behavior.
  */
@@ -50,7 +13,7 @@ declare class HandlebarsApplication {
    * Configure a registry of template parts which are supported for this application for partial rendering.
    * @defaultValue `{}`
    */
-  static PARTS: Record<string, HandlebarsTemplatePart>;
+  static PARTS: Record<string, HandlebarsApplicationMixin.HandlebarsTemplatePart>;
 
   /**
    * A record of all rendered template parts.
@@ -58,7 +21,7 @@ declare class HandlebarsApplication {
    */
   get parts(): Record<string, HTMLElement>;
 
-  protected _configureRenderOptions(options: DeepPartial<HandlebarsRenderOptions>): void;
+  protected _configureRenderOptions(options: DeepPartial<HandlebarsApplicationMixin.HandlebarsRenderOptions>): void;
 
   protected _preFirstRender(
     context: DeepPartial<ApplicationRenderContext>,
@@ -91,7 +54,7 @@ declare class HandlebarsApplication {
   protected _preparePartContext(
     partId: string,
     context: ApplicationRenderContext,
-    options: DeepPartial<HandlebarsRenderOptions>,
+    options: DeepPartial<HandlebarsApplicationMixin.HandlebarsRenderOptions>,
   ): Promise<ApplicationRenderContext>;
 
   /**
@@ -143,14 +106,14 @@ declare class HandlebarsApplication {
   protected _attachPartListeners(
     partId: string,
     htmlElement: HTMLElement,
-    options: DeepPartial<HandlebarsRenderOptions>,
+    options: DeepPartial<HandlebarsApplicationMixin.HandlebarsRenderOptions>,
   ): void;
 }
 
 /**
  * Augment an Application class with [Handlebars](https://handlebarsjs.com) template rendering behavior.
  */
-export default function HandlebarsApplicationMixin<BaseClass extends typeof ApplicationV2>(
+declare function HandlebarsApplicationMixin<BaseClass extends typeof ApplicationV2>(
   BaseApplication: BaseClass,
 ): Mixin<typeof HandlebarsApplication, BaseClass>;
 
@@ -159,4 +122,43 @@ declare namespace HandlebarsApplicationMixin {
     scrollPositions: Array<[el1: HTMLElement, scrollTop: number, scrollLeft: number]>;
     focus?: string | undefined;
   }
+
+  // TODO: How does this merge with DocumentSheetRenderOptions?
+  interface HandlebarsRenderOptions extends ApplicationRenderOptions {
+    parts: string[];
+  }
+
+  type HandlebarsTemplatePart = {
+    /**
+     * The template entry-point for the part
+     */
+    template: string;
+    /**
+     * A CSS id to assign to the top-level element of the rendered part.
+     * This id string is automatically prefixed by the application id.
+     */
+    id?: string;
+    /**
+     * An array of CSS classes to apply to the top-level element of the
+     * rendered part.
+     */
+    classes?: string[];
+    /**
+     * An array of templates that are required to render the part.
+     * If omitted, only the entry-point is inferred as required.
+     */
+    templates?: string[];
+    /**
+     * An array of selectors within this part whose scroll positions should
+     * be persisted during a re-render operation. A blank string is used
+     * to denote that the root level of the part is scrollable.
+     */
+    scrollabe?: string[];
+    /**
+     * A registry of forms selectors and submission handlers.
+     */
+    forms?: Record<string, ApplicationFormConfiguration>;
+  };
 }
+
+export default HandlebarsApplicationMixin;
diff --git a/src/foundry/client-esm/applications/elements/color-picker.d.mts b/src/foundry/client-esm/applications/elements/color-picker.d.mts
new file mode 100644
index 000000000..d67ed8721
--- /dev/null
+++ b/src/foundry/client-esm/applications/elements/color-picker.d.mts
@@ -0,0 +1,26 @@
+import type { FormInputConfig } from "../forms/fields.d.mts";
+import AbstractFormInputElement from "./form-element.mjs";
+
+/**
+ * A custom HTMLElement used to select a color using a linked pair of input fields.
+ */
+export default class HTMLColorPickerElement extends AbstractFormInputElement<string> {
+  constructor();
+
+  static override tagName: "color-picker";
+
+  protected override _buildElements(): HTMLInputElement[];
+
+  protected override _refresh(): void;
+
+  protected override _activateListeners(): void;
+
+  protected override _toggleDisabled(disabled: boolean): void;
+
+  protected override _onClick(event: PointerEvent): void;
+
+  /**
+   * Create a HTMLColorPickerElement using provided configuration data.
+   */
+  static create(config: FormInputConfig): HTMLColorPickerElement;
+}
diff --git a/src/foundry/client-esm/applications/elements/document-tags.d.mts b/src/foundry/client-esm/applications/elements/document-tags.d.mts
new file mode 100644
index 000000000..55ea7d6e1
--- /dev/null
+++ b/src/foundry/client-esm/applications/elements/document-tags.d.mts
@@ -0,0 +1,90 @@
+import type { FormInputConfig } from "../forms/fields.d.mts";
+import AbstractFormInputElement from "./form-element.mjs";
+
+/**
+ * A custom HTMLElement used to render a set of associated Documents referenced by UUID.
+ */
+declare class HTMLDocumentTagsElement extends AbstractFormInputElement<Record<string, string> | string | string[]> {
+  constructor();
+
+  _value: {};
+
+  set type(value: string);
+
+  /**
+   * Restrict this element to documents of a particular type.
+   */
+  get type(): string | null;
+
+  /**
+   * Restrict to only allow referencing a single Document instead of an array of documents.
+   */
+  get single(): boolean;
+  set single(value: boolean);
+
+  set max(value: number);
+
+  /**
+   * Allow a maximum number of documents to be tagged to the element.
+   */
+  get max(): number;
+
+  /**
+   * Initialize innerText or an initial value attribute of the element as a serialized JSON array.
+   */
+  protected _initializeTags(): void;
+
+  protected override _buildElements(): (HTMLButtonElement | HTMLDivElement)[];
+
+  protected override _refresh(): void;
+
+  /**
+   * Create an HTML string fragment for a single document tag.
+   * @param uuid     - The document UUID
+   * @param name     - The document name
+   * @param editable - Is the tag editable?
+   *                   (default: `true`)
+   */
+  static renderTag(uuid: string, name: string, editable?: boolean): HTMLDivElement;
+
+  /**
+   * Validate that the tagged document is allowed to be added to this field.
+   * Subclasses may impose more strict validation as to which types of documents are allowed.
+   * @param document - A candidate document or compendium index entry to tag
+   * @throws An error if the candidate document is not allowed
+   */
+  _validateDocument(document: foundry.abstract.Document.Any | object): void;
+
+  protected override _getValue(): string | string[];
+
+  protected override _setValue(value: string | string[]): void;
+
+  protected override _toggleDisabled(disabled: any): void;
+
+  /**
+   * Create a HTMLDocumentTagsElement using provided configuration data.
+   */
+  static create(config: HTMLDocumentTagsElement.DocumentTagsInputConfig): HTMLDocumentTagsElement;
+}
+
+declare namespace HTMLDocumentTagsElement {
+  interface DocumentTagsInputConfig extends FormInputConfig {
+    /**
+     * A specific document type in CONST.ALL_DOCUMENT_TYPES
+     */
+    type?: foundry.CONST.ALL_DOCUMENT_TYPES;
+
+    /**
+     * Only allow referencing a single document. In this case the submitted form value will
+     *    be a single UUID string rather than an array
+     */
+    single?: boolean;
+
+    /**
+     * Only allow attaching a maximum number of documents
+     */
+    max?: number;
+  }
+}
+
+export default HTMLDocumentTagsElement;
diff --git a/src/foundry/client-esm/applications/elements/file-picker.d.mts b/src/foundry/client-esm/applications/elements/file-picker.d.mts
new file mode 100644
index 000000000..5a534eb8c
--- /dev/null
+++ b/src/foundry/client-esm/applications/elements/file-picker.d.mts
@@ -0,0 +1,62 @@
+import type { FormInputConfig } from "../forms/fields.d.mts";
+import AbstractFormInputElement from "./form-element.mjs";
+
+/**
+ * A custom HTML element responsible for rendering a file input field and associated FilePicker button.
+ */
+declare class HTMLFilePickerElement extends AbstractFormInputElement<string> {
+  constructor();
+
+  static override tagName: "file-picker";
+
+  /**
+   * The file path selected.
+   */
+  input: HTMLInputElement;
+
+  /**
+   * A button to open the file picker interface.
+   */
+  button: HTMLButtonElement;
+
+  /**
+   * A reference to the FilePicker application instance originated by this element.
+   */
+  picker: FilePicker;
+
+  /**
+   * A type of file which can be selected in this field.
+   * @see {@link FilePicker.FILE_TYPES}
+   */
+  get type(): FilePicker.Type;
+  set type(value: FilePicker.Type);
+
+  /**
+   * Prevent uploading new files as part of this element's FilePicker dialog.
+   */
+  get noupload(): boolean;
+  set noupload(value: boolean);
+
+  protected override _buildElements(): (HTMLInputElement | HTMLButtonElement)[];
+
+  protected override _toggleDisabled(disabled: any): void;
+
+  protected override _activateListeners(): void;
+
+  protected override _onClick(event: any): void;
+
+  /**
+   * Create a HTMLFilePickerElement using provided configuration data.
+   */
+  static create(config: HTMLFilePickerElement.FilePickerInputConfig): HTMLElement;
+}
+
+declare namespace HTMLFilePickerElement {
+  interface FilePickerInputConfig extends FormInputConfig<string> {
+    type?: FilePicker.Type;
+    placeholder?: string;
+    noupload?: boolean;
+  }
+}
+
+export default HTMLFilePickerElement;
diff --git a/src/foundry/client-esm/applications/elements/form-element.d.mts b/src/foundry/client-esm/applications/elements/form-element.d.mts
new file mode 100644
index 000000000..dd70bf2e4
--- /dev/null
+++ b/src/foundry/client-esm/applications/elements/form-element.d.mts
@@ -0,0 +1,106 @@
+/**
+ * An abstract custom HTMLElement designed for use with form inputs.
+ * @fires {Event} input           An "input" event when the value of the input changes
+ * @fires {Event} change          A "change" event when the value of the element changes
+ */
+export default abstract class AbstractFormInputElement<FormInputValueType> extends HTMLElement {
+  constructor();
+
+  /**
+   * The HTML tag name used by this element.
+   */
+  static tagName: string;
+
+  /**
+   * Declare that this custom element provides form element functionality.
+   */
+  static formAssociated: boolean;
+
+  /**
+   * Attached ElementInternals which provides form handling functionality.
+   */
+  protected _internals: ElementInternals;
+
+  /**
+   * The form this element belongs to.
+   */
+  get form(): HTMLFormElement;
+  set name(value: string);
+
+  /**
+   * The input element name.
+   */
+  get name(): string;
+  set value(value: FormInputValueType);
+
+  /**
+   * The value of the input element.
+   */
+  get value(): FormInputValueType;
+
+  /**
+   * The underlying value of the element.
+   */
+  protected _value: FormInputValueType;
+
+  /**
+   * Return the value of the input element which should be submitted to the form.
+   */
+  protected _getValue(): FormInputValueType;
+
+  /**
+   * Translate user-provided input value into the format that should be stored.
+   * @param value - A new value to assign to the element
+   * @throws An error if the provided value is invalid
+   */
+  protected _setValue(value: FormInputValueType): void;
+
+  set disabled(value: boolean);
+  /**
+   * Is this element disabled?
+   */
+  get disabled(): boolean;
+  /**
+   * Is this field editable? The field can be neither disabled nor readonly.
+   */
+  get editable(): boolean;
+
+  /**
+   * Special behaviors that the subclass should implement when toggling the disabled state of the input.
+   * @param disabled - The new disabled state
+   */
+  protected _toggleDisabled(disabled: boolean): void;
+
+  /**
+   * Initialize the custom element, constructing its HTML.
+   */
+  connectedCallback(): void;
+
+  /**
+   * Create the HTML elements that should be included in this custom element.
+   * Elements are returned as an array of ordered children.
+   */
+  protected _buildElements(): HTMLElement[];
+
+  /**
+   * Refresh the active state of the custom element.
+   */
+  protected _refresh(): void;
+
+  /**
+   * Apply key attributes on the containing custom HTML element to input elements contained within it.
+   * @internal
+   */
+  protected _applyInputAttributes(input: any): void;
+
+  /**
+   * Activate event listeners which add dynamic behavior to the custom element.
+   */
+  protected _activateListeners(): void;
+
+  /**
+   * Special handling when the custom element is clicked. This should be implemented to transfer focus to an
+   * appropriate internal element.
+   */
+  protected _onClick(event: PointerEvent): void;
+}
diff --git a/src/foundry/client-esm/applications/elements/hue-slider.d.mts b/src/foundry/client-esm/applications/elements/hue-slider.d.mts
new file mode 100644
index 000000000..eb7f13074
--- /dev/null
+++ b/src/foundry/client-esm/applications/elements/hue-slider.d.mts
@@ -0,0 +1,20 @@
+import AbstractFormInputElement from "./form-element.mjs";
+
+/**
+ * A class designed to standardize the behavior for a hue selector UI component.
+ */
+export default class HTMLHueSelectorSlider extends AbstractFormInputElement<number> {
+  constructor();
+
+  static override tagName: "hue-slider";
+
+  protected override _buildElements(): HTMLInputElement[];
+
+  protected override _refresh(): void;
+
+  protected override _activateListeners(): void;
+
+  protected override _setValue(value: any): void;
+
+  protected override _toggleDisabled(disabled: any): void;
+}
diff --git a/src/foundry/client-esm/applications/elements/multi-select.d.mts b/src/foundry/client-esm/applications/elements/multi-select.d.mts
new file mode 100644
index 000000000..7ae5afbcc
--- /dev/null
+++ b/src/foundry/client-esm/applications/elements/multi-select.d.mts
@@ -0,0 +1,91 @@
+import type { FormInputConfig, SelectInputConfig } from "../forms/fields.d.mts";
+import AbstractFormInputElement from "./form-element.mjs";
+
+/**
+ * An abstract base class designed to standardize the behavior for a multi-select UI component.
+ * Multi-select components return an array of values as part of form submission.
+ * Different implementations may provide different experiences around how inputs are presented to the user.
+ */
+export class AbstractMultiSelectElement extends AbstractFormInputElement<Set<string> | string[]> {
+  constructor();
+  _value: Set<any>;
+  /**
+   * Predefined <option> and <optgroup> elements which were defined in the original HTML.
+   */
+  protected _options: (HTMLOptionElement | HTMLOptGroupElement)[];
+  /**
+   * An object which maps option values to displayed labels.
+   */
+  protected _choices: {
+    [x: string]: string;
+  };
+  /**
+   * Preserve existing <option> and <optgroup> elements which are defined in the original HTML.
+   */
+  protected _initialize(): void;
+  /**
+   * Mark a choice as selected.
+   * @param value - The value to add to the chosen set
+   */
+  select(value: string): void;
+  /**
+   * Mark a choice as un-selected.
+   * @param value - The value to delete from the chosen set
+   */
+  unselect(value: string): void;
+
+  protected override _getValue(): string[];
+
+  protected override _setValue(value: string[]): void;
+}
+/**
+ * Provide a multi-select workflow using a select element as the input mechanism.
+ *
+ * @example Multi-Select HTML Markup
+ * ```html
+ * <multi-select name="select-many-things">
+ *   <optgroup label="Basic Options">
+ *     <option value="foo">Foo</option>
+ *     <option value="bar">Bar</option>
+ *     <option value="baz">Baz</option>
+ *   </optgroup>
+ *   <optgroup label="Advanced Options">
+ *    <option value="fizz">Fizz</option>
+ *     <option value="buzz">Buzz</option>
+ *   </optgroup>
+ * </multi-select>
+ * ```
+ */
+export class HTMLMultiSelectElement extends AbstractMultiSelectElement {
+  /**
+   * Create a HTMLMultiSelectElement using provided configuration data.
+   */
+  static create(config: FormInputConfig<string[]> & Omit<SelectInputConfig, "blank">): HTMLMultiSelectElement;
+
+  protected override _buildElements(): (HTMLDivElement | HTMLSelectElement)[];
+
+  protected override _toggleDisabled(disabled: any): void;
+}
+/**
+ * Provide a multi-select workflow as a grid of input checkbox elements.
+ *
+ * @example Multi-Checkbox HTML Markup
+ * ```html
+ * <multi-checkbox name="check-many-boxes">
+ *   <optgroup label="Basic Options">
+ *     <option value="foo">Foo</option>
+ *     <option value="bar">Bar</option>
+ *     <option value="baz">Baz</option>
+ *   </optgroup>
+ *   <optgroup label="Advanced Options">
+ *    <option value="fizz">Fizz</option>
+ *     <option value="buzz">Buzz</option>
+ *   </optgroup>
+ * </multi-checkbox>
+ * ```
+ */
+export class HTMLMultiCheckboxElement extends AbstractMultiSelectElement {
+  protected override _buildElements(): (HTMLFieldSetElement | HTMLLabelElement)[];
+
+  protected override _toggleDisabled(disabled: any): void;
+}
diff --git a/src/foundry/client-esm/applications/elements/prosemirror-editor.d.mts b/src/foundry/client-esm/applications/elements/prosemirror-editor.d.mts
new file mode 100644
index 000000000..30d0a6237
--- /dev/null
+++ b/src/foundry/client-esm/applications/elements/prosemirror-editor.d.mts
@@ -0,0 +1,47 @@
+import type ProseMirrorPlugin from "../../../prosemirror/plugin.d.mts";
+import type { FormInputConfig } from "../forms/fields.d.mts";
+import AbstractFormInputElement from "./form-element.mjs";
+
+/**
+ * A custom HTML element responsible displaying a ProseMirror rich text editor.
+ */
+declare class HTMLProseMirrorElement extends AbstractFormInputElement<string> {
+  constructor();
+
+  static override tagName: "prose-mirror";
+
+  disconnectedCallback(): void;
+
+  override _buildElements(): (HTMLButtonElement | HTMLDivElement)[];
+
+  override _getValue(): string;
+  /**
+   * Configure ProseMirror editor plugins.
+   */
+  protected _configurePlugins(): Record<string, ProseMirrorPlugin>;
+
+  override _toggleDisabled(disabled: boolean): void;
+
+  /**
+   * Create a HTMLProseMirrorElement using provided configuration data.
+   */
+  static create(config: HTMLProseMirrorElement.ProseMirrorInputConfig): HTMLProseMirrorElement;
+}
+
+declare namespace HTMLProseMirrorElement {
+  interface ProseMirrorInputConfig extends FormInputConfig<string> {
+    /** Is this editor toggled (true) or always active (false) */
+    toggled: boolean;
+
+    /** If the editor is toggled, provide the enrichedHTML which is displayed while the editor is not active. */
+    enriched: string;
+
+    /** Does this editor instance support collaborative editing? */
+    collaborate: boolean;
+
+    /** A Document UUID. Required for collaborative editing */
+    documentUUID: string;
+  }
+}
+
+export default HTMLProseMirrorElement;
diff --git a/src/foundry/client-esm/applications/elements/range-picker.d.mts b/src/foundry/client-esm/applications/elements/range-picker.d.mts
new file mode 100644
index 000000000..55fda81c0
--- /dev/null
+++ b/src/foundry/client-esm/applications/elements/range-picker.d.mts
@@ -0,0 +1,41 @@
+import type { FormInputConfig } from "../forms/fields.d.mts";
+import AbstractFormInputElement from "./form-element.mjs";
+
+/**
+ * A custom HTML element responsible selecting a value on a range slider with a linked number input field.
+ */
+declare class HTMLRangePickerElement extends AbstractFormInputElement<number> {
+  constructor();
+
+  static override tagName: "range-picker";
+
+  /**
+   * The value of the input element.
+   */
+  get valueAsNumber(): number;
+
+  protected override _buildElements(): HTMLInputElement[];
+
+  protected override _setValue(value: number): void;
+
+  protected override _refresh(): void;
+
+  protected override _activateListeners(): void;
+
+  protected override _toggleDisabled(disabled: boolean): void;
+
+  /**
+   * Create a HTMLRangePickerElement using provided configuration data.
+   */
+  static create(config: HTMLRangePickerElement.RangePickerInputConfig): HTMLRangePickerElement;
+}
+
+declare namespace HTMLRangePickerElement {
+  interface RangePickerInputConfig extends FormInputConfig<number> {
+    min: number;
+    max: number;
+    step?: number;
+  }
+}
+
+export default HTMLRangePickerElement;
diff --git a/src/foundry/client-esm/applications/elements/string-tags.d.mts b/src/foundry/client-esm/applications/elements/string-tags.d.mts
new file mode 100644
index 000000000..880ef7f0a
--- /dev/null
+++ b/src/foundry/client-esm/applications/elements/string-tags.d.mts
@@ -0,0 +1,64 @@
+import type { FormInputConfig } from "../forms/fields.d.mts";
+import AbstractFormInputElement from "./form-element.mjs";
+
+/**
+ * A custom HTML element which allows for arbitrary assignment of a set of string tags.
+ * This element may be used directly or subclassed to impose additional validation or functionality.
+ */
+export default class HTMLStringTagsElement extends AbstractFormInputElement<Set<string> | string[]> {
+  constructor();
+
+  static override tagName: "string-tags";
+
+  static icons: {
+    add: string;
+    remove: string;
+  };
+
+  static labels: {
+    add: string;
+    remove: string;
+    placeholder: string;
+  };
+
+  _value: Set<string>;
+
+  /**
+   * Initialize innerText or an initial value attribute of the element as a comma-separated list of currently assigned
+   * string tags.
+   */
+  protected _initializeTags(): void;
+
+  /**
+   * Subclasses may impose more strict validation on what tags are allowed.
+   * @param tag - A candidate tag
+   * @throws An error if the candidate tag is not allowed
+   */
+  protected _validateTag(tag: string): void;
+
+  protected override _buildElements(): (HTMLButtonElement | HTMLDivElement)[];
+
+  protected override _refresh(): void;
+
+  /**
+   * Render the tagged string as an HTML element.
+   * @param tag      - The raw tag value
+   * @param label    - An optional tag label
+   * @param editable - Is the tag editable?
+   * @returns A rendered HTML element for the tag
+   */
+  static renderTag(tag: string, label?: string, editable?: boolean): HTMLDivElement;
+
+  protected override _activateListeners(): void;
+
+  protected override _getValue(): string[];
+
+  protected override _setValue(value: string[] | Set<string>): void;
+
+  protected override _toggleDisabled(disabled: boolean): void;
+
+  /**
+   * Create a HTMLStringTagsElement using provided configuration data.
+   */
+  static create(config: FormInputConfig<Set<string> | string[]>): HTMLElement;
+}
diff --git a/src/foundry/client-esm/applications/forms/fields.d.mts b/src/foundry/client-esm/applications/forms/fields.d.mts
index 806378e47..2a855230d 100644
--- a/src/foundry/client-esm/applications/forms/fields.d.mts
+++ b/src/foundry/client-esm/applications/forms/fields.d.mts
@@ -1,11 +1,218 @@
-declare global {
-  type CustomFormGroup = unknown;
+export type CustomFormGroup = (
+  field: foundry.data.fields.DataField,
+  groupConfig: FormGroupConfig,
+  inputConfig: FormInputConfig,
+) => HTMLDivElement;
 
-  type CustomFormElement = unknown;
+export type CustomFormInput = (
+  field: foundry.data.fields.DataField,
+  config: FormInputConfig,
+) => HTMLElement | HTMLCollection;
 
-  interface FormGroupConfig {}
+export interface FormGroupConfig {
+  /**
+   * A text label to apply to the form group
+   */
+  label: string;
+  /**
+   * An optional units string which is appended to the label
+   */
+  units?: string;
+  /**
+   * An HTML element or collection of elements which provide the inputs
+   * for the group
+   */
+  input: HTMLElement | HTMLCollection;
+  /**
+   * Hint text displayed as part of the form group
+   */
+  hint?: string;
+  /**
+   * Some parent CSS id within which field names are unique. If provided,
+   *                     this root ID is used to automatically assign "id" attributes to input
+   *                     elements and "for" attributes to corresponding labels
+   */
+  rootId?: string;
+  /**
+   * An array of CSS classes applied to the form group element
+   */
+  classes?: string[];
+  /**
+   * Is the "stacked" class applied to the form group
+   */
+  stacked?: boolean;
+  /**
+   * Should labels or other elements within this form group be
+   *            automatically localized?
+   */
+  localize?: boolean;
+  /**
+   * A custom form group widget function which replaces the default
+   *            group HTML generation
+   */
+  widget?: CustomFormGroup;
+}
 
-  interface FormInputConfig {}
+export interface FormInputConfig<FormInputValue = any> {
+  /**
+   * The name of the form element
+   */
+  name: string;
+  /**
+   * The current value of the form element
+   */
+  value?: FormInputValue;
+  /**
+   * Is the field required?
+   */
+  required?: boolean;
+  /**
+   * Is the field disabled?
+   */
+  disabled?: boolean;
+  /**
+   * Is the field readonly?
+   */
+  readonly?: boolean;
+  /**
+   * Is the field autofocused?
+   */
+  autofocus?: boolean;
+  /**
+   * Localize values of this field?
+   */
+  localize?: boolean;
+  /**
+   * Additional dataset attributes to assign to the input
+   */
+  dataset?: Record<string, string>;
+  /**
+   * A placeholder value, if supported by the element type
+   */
+  placeholder?: string;
+  input?: CustomFormInput;
 }
 
+/**
+ * Create a standardized form field group.
+ */
 export function createFormGroup(config: FormGroupConfig): HTMLDivElement;
+
+/**
+ * Create an `<input type="checkbox">` element for a BooleanField.
+ */
+export function createCheckboxInput(config: FormInputConfig<boolean>): HTMLInputElement;
+
+export interface EditorInputConfig extends FormInputConfig<string> {
+  /** @defaultValue `"prosemirror"` */
+  engine?: string;
+  height?: number;
+  editable?: boolean;
+  button?: boolean;
+  collaborate?: boolean;
+}
+
+/**
+ * Create a `<div class="editor">` element for a StringField.
+ */
+export function createEditorInput(config: EditorInputConfig): HTMLDivElement;
+
+/**
+ * Create a `<multi-select>` element for a StringField.
+ */
+export function createMultiSelectInput(
+  config: FormInputConfig<string[]> & Omit<SelectInputConfig, "blank">,
+): HTMLSelectElement;
+
+export interface NumberInputConfig extends FormInputConfig<number> {
+  min: number;
+  max: number;
+  step: number | "any";
+  type?: "range" | "number";
+}
+
+/**
+ * Create an `<input type="number">` element for a NumberField.
+ */
+export function createNumberInput(config: NumberInputConfig): HTMLInputElement;
+
+export interface FormSelectOption {
+  value: string;
+  label: string;
+  group?: string;
+  disabled?: boolean;
+  selected?: boolean;
+}
+
+// Compatible with multiple different types of FormInputConfig so this does *not* extend that interface
+export interface SelectInputConfig {
+  options: FormSelectOption[];
+  /**
+   * An option to control the order and display of optgroup elements. The order of
+   *         strings defines the displayed order of optgroup elements.
+   *         A blank string may be used to define the position of ungrouped options.
+   *         If not defined, the order of groups corresponds to the order of options.
+   */
+  groups?: string[];
+  blank?: string;
+  /**
+   * An alternative value key of the object passed to the options array
+   */
+  valueAttr?: string;
+  /**
+   * An alternative label key of the object passed to the options array
+   */
+  labelAttr?: string;
+  /**
+   * Localize value labels
+   */
+  localize?: boolean;
+  /**
+   * Sort options alphabetically by label within groups
+   */
+  sort?: boolean;
+  /**
+   * Customize the type of select that is created
+   */
+  type?: "single" | "multi" | "checkboxes";
+}
+
+export interface TextAreaInputConfig extends FormInputConfig<string> {
+  rows: number;
+}
+
+/**
+ * Create a `<textarea>` element for a StringField.
+ */
+export function createTextareaInput(config: TextAreaInputConfig): HTMLTextAreaElement;
+
+/**
+ * Create an `<input type="text">` element for a StringField.
+ */
+export function createTextInput(config: FormInputConfig<string>): HTMLInputElement;
+
+/**
+ * Structure a provided array of select options into a standardized format for rendering optgroup and option elements.
+ *
+ * @example
+ * ```js
+ * const options = [
+ *   {value: "bar", label: "Bar", selected: true, group: "Good Options"},
+ *   {value: "foo", label: "Foo", disabled: true, group: "Bad Options"},
+ *   {value: "baz", label: "Baz", group: "Good Options"}
+ * ];
+ * const groups = ["Good Options", "Bad Options", "Unused Options"];
+ * const optgroups = foundry.applications.fields.prepareSelectOptionGroups({options, groups, blank: true, sort: true});
+ * ```
+ */
+export function prepareSelectOptionGroups(config: FormInputConfig & SelectInputConfig): {
+  group: string;
+  options: FormSelectOption[];
+}[];
+
+/**
+ * Apply standard attributes to all input elements.
+ * @param input  - The element being configured
+ * @param config - Configuration for the element
+ */
+export function setInputAttributes(input: HTMLElement, config: FormInputConfig<any>): void;
diff --git a/src/foundry/client/apps/components/index.d.mts b/src/foundry/client/apps/components/index.d.mts
deleted file mode 100644
index 035b1ef1b..000000000
--- a/src/foundry/client/apps/components/index.d.mts
+++ /dev/null
@@ -1,1 +0,0 @@
-import "./multi-select.d.mts";
diff --git a/src/foundry/client/apps/components/multi-select.d.mts b/src/foundry/client/apps/components/multi-select.d.mts
deleted file mode 100644
index 6d221e5db..000000000
--- a/src/foundry/client/apps/components/multi-select.d.mts
+++ /dev/null
@@ -1,149 +0,0 @@
-export {};
-
-declare global {
-  /**
-   * An abstract base class designed to standardize the behavior for a multi-select UI component.
-   * Multi-select components return an array of values as part of form submission.
-   * Different implementations may provide different experiences around how inputs are presented to the user.
-   * @internal
-   */
-  abstract class AbstractMultiSelectElement extends HTMLElement {
-    /**
-     * The "change" event is emitted when the values of the multi-select element are changed.
-     * @param event     - A "change" event passed to event listeners.
-     */
-    static onChange: (event: Event) => void;
-
-    /**
-     * Predefined <option> and <optgroup> elements which were defined in the original HTML.
-     */
-    protected _options: (HTMLOptionElement | HTMLOptGroupElement)[];
-
-    /**
-     * An object which maps option values to displayed labels.
-     * @defaultValue `{}`
-     */
-    protected _choices: Record<string, string>;
-
-    /**
-     * An array of identifiers which have been chosen.
-     * @defaultValue `new Set()`
-     */
-    protected _chosen: Set<string>;
-
-    /**
-     * The form this custom element belongs to, if any.
-     * @defaultValue `null`
-     */
-    form: HTMLFormElement | null;
-
-    /**
-     * Preserve existing <option> and <optgroup> elements which are defined in the original HTML.
-     */
-    protected _initializeOptions(): void;
-
-    /**
-     * The name of the multi-select input element.
-     */
-    get name(): string;
-    set name(value: string);
-
-    /**
-     * The values of the multi-select input are expressed as an array of strings.
-     */
-    get value(): string[];
-    set value(values: string[]);
-
-    /**
-     * Activate the custom element when it is attached to the DOM.
-     */
-    connectedCallback(): void;
-
-    /**
-     * Deactivate the custom element when it is detached from the DOM.
-     */
-    disconnectedCallback(): void;
-
-    /**
-     * Mark a choice as selected.
-     * @param  value      - The value to add to the chosen set
-     */
-    select(value: string): void;
-
-    /**
-     * Mark a choice as un-selected.
-     * @param  value      - The value to delete from the chosen set
-     */
-    unselect(value: string): void;
-
-    /**
-     * Create the HTML elements that should be included in this custom element.
-     * Elements are returned as an array of ordered children.
-     */
-    protected _buildElements(): HTMLElement[];
-
-    /**
-     * Refresh the active state of the custom element by reflecting changes to the _chosen set.
-     */
-    protected _refresh(): void;
-
-    /**
-     * Activate event listeners which add dynamic behavior to the custom element.
-     */
-    protected _activateListeners(): void;
-  }
-
-  /**
-   * Provide a multi-select workflow using a select element as the input mechanism.
-   * @internal
-   *
-   * @example Multi-Select HTML Markup
-   * ```html
-   * <multi-select name="select-many-things">
-   *   <optgroup label="Basic Options">
-   *     <option value="foo">Foo</option>
-   *     <option value="bar">Bar</option>
-   *     <option value="baz">Baz</option>
-   *   </optgroup>
-   *   <optgroup label="Advanced Options">
-   *    <option value="fizz">Fizz</option>
-   *     <option value="buzz">Buzz</option>
-   *   </optgroup>
-   * </multi-select>
-   * ```
-   */
-  class HTMLMultiSelectElement extends AbstractMultiSelectElement {
-    protected override _buildElements(): HTMLElement[];
-
-    protected override _refresh(): void;
-
-    protected override _activateListeners(): void;
-  }
-
-  /**
-   * Provide a multi-select workflow as a grid of input checkbox elements.
-   * @internal
-   *
-   * @example Multi-Checkbox HTML Markup
-   * ```html
-   * <multi-checkbox name="check-many-boxes">
-   *   <optgroup label="Basic Options">
-   *     <option value="foo">Foo</option>
-   *     <option value="bar">Bar</option>
-   *     <option value="baz">Baz</option>
-   *   </optgroup>
-   *   <optgroup label="Advanced Options">
-   *    <option value="fizz">Fizz</option>
-   *     <option value="buzz">Buzz</option>
-   *   </optgroup>
-   * </multi-checkbox>
-   * ```
-   */
-  class HTMLMultiCheckboxElement extends AbstractMultiSelectElement {
-    protected override _buildElements(): HTMLElement[];
-
-    protected override _refresh(): void;
-
-    protected override _activateListeners(): void;
-  }
-}
diff --git a/src/foundry/client/apps/index.d.mts b/src/foundry/client/apps/index.d.mts
index 062f3f4f8..c0ed94ab8 100644
--- a/src/foundry/client/apps/index.d.mts
+++ b/src/foundry/client/apps/index.d.mts
@@ -1,6 +1,5 @@
 import "./app.d.mts";
 import "./av/index.d.mts";
-import "./components/index.d.mts";
 import "./dialogs/index.d.mts";
 import "./form.d.mts";
 import "./forms/index.d.mts";
diff --git a/src/foundry/common/primitives/math.d.mts b/src/foundry/common/primitives/math.d.mts
index ab89ce71b..a39b6f5f0 100644
--- a/src/foundry/common/primitives/math.d.mts
+++ b/src/foundry/common/primitives/math.d.mts
@@ -2,12 +2,32 @@ export {};
 
 declare global {
   interface Math {
+    /**
+     * √3
+     */
+    SQRT3: 1.7320508075688772;
+
+    /**
+     * √⅓
+     */
+    SQRT1_3: 0.5773502691896257;
+
+    /**
+     * Bound a number between some minimum and maximum value, inclusively.
+     * @param num - The current value
+     * @param min - The minimum allowed value
+     * @param max - The maximum allowed value
+     * @returns The clamped number
+     */
+    clamp(num: number, min: number, max: number): number;
+
     /**
      * Bound a number between some minimum and maximum value, inclusively
      * @param num - The current value
      * @param min - The minimum allowed value
      * @param max - The maximum allowed value
      * @returns The clamped number
+     * @deprecated since v12 till v14
      */
     clamped(num: number, min: number, max: number): number;
 
@@ -20,11 +40,20 @@ declare global {
      */
     mix(a: number, b: number, w: number): number;
 
+    /**
+     * Transform an angle in degrees to be bounded within the domain [0, 360]
+     * @param degrees - An angle in degrees
+     * @returns The same angle on the range [0, 360) or (0, 360]
+     */
+    normalizeDegrees(degrees: number): number;
+
     /**
      * Transform an angle in degrees to be bounded within the domain [0, 360]
      * @param degrees - An angle in degrees
      * @param base    - The base angle to normalize to, either 0 for [0, 360) or 360 for (0, 360] (default: `0`)
      * @returns The same angle on the range [0, 360) or (0, 360]
+     * @deprecated since v12, until v14.
+     * @remarks Use Math.normalizeDegrees(degrees: number): number.
      */
     normalizeDegrees(degrees: number, base?: number): number;
 
@@ -39,20 +68,10 @@ declare global {
      * Round a floating point number to a certain number of decimal places
      * @param number - A floating point number
      * @param places - An integer number of decimal places
+     * @deprecated since v12, until v14
      */
     roundDecimals(number: number, places: number): number;
 
-    /**
-     * To keep compatibility with previous implementation.
-     * roundFast was bugged and the performance advantage was not there.
-     * @param number - A finite number
-     * @returns The rounded number
-     *
-     * @deprecated since v10, will be removed in v12
-     * @remarks roundFast is deprecated in favor of {@link Math.round}
-     */
-    roundFast(number: number): number;
-
     /**
      * Transform an angle in radians to a number in degrees
      * @param angle - An angle in radians
@@ -72,11 +91,12 @@ declare global {
      * @param  minVal - The minimal value of the oscillation.
      * @param  maxVal - The maximum value of the oscillation.
      * @param  t      - The time value.
-     * @param  p      - The period (can't be equal to 0).
+     * @param  p      - The period (must be nonzero).
      *                  (default: `1`)
      * @param  func   - The optional math function to use for oscillation.
      *                  (default: `Math.cos`)
-     * @returns The oscillation according to t.
+     *                  Its period must be 2π
+     * @returns The oscillation according to t. `((maxValue - minValue) * (f(2π * t / p) + 1) / 2) + minValue`
      */
     oscillation(minVal: number, maxVal: number, t: number, p?: number, func?: (radians: number) => number): number;
   }
diff --git a/src/foundry/common/primitives/number.d.mts b/src/foundry/common/primitives/number.d.mts
index 220fbd44e..35e0ba701 100644
--- a/src/foundry/common/primitives/number.d.mts
+++ b/src/foundry/common/primitives/number.d.mts
@@ -36,6 +36,8 @@ declare global {
 
     /**
      * Round a number to the nearest number which is a multiple of a given interval
+     * This is a convenience function intended to humanize issues of floating point precision.
+     * The interval is treated as a standard string representation to determine the amount of decimal truncation applied.
      * @param interval - The interval to round the number to the nearest multiple of
      *                   (default: `1`)
      * @param method   - The rounding method in: round, ceil, floor
diff --git a/src/foundry/common/primitives/string.d.mts b/src/foundry/common/primitives/string.d.mts
index 57f93b26f..bee75bd3d 100644
--- a/src/foundry/common/primitives/string.d.mts
+++ b/src/foundry/common/primitives/string.d.mts
@@ -7,6 +7,15 @@ declare global {
      */
     capitalize<S extends string>(this: S): Capitalize<S>;
 
+    /**
+     * Compare this string (x) with the other string (y) by comparing each character's Unicode code point value.
+     * This is the same comparison function that used by Array#sort if the compare function argument is omitted.
+     * The result is host/locale-independent.
+     * @param other - The other string to compare this string to.
+     * @returns A negative Number if x \< y, a positive Number if x \> y, or a zero otherwise.
+     */
+    compare<S extends string>(this: S, other: string): number;
+
     /**
      * Convert a string to Title Case where the first letter of each word is capitalized
      */


Test patch:
diff --git a/tests/foundry/client-esm/applications/api/dialog.test-d.ts b/tests/foundry/client-esm/applications/api/dialog.test-d.ts
new file mode 100644
index 000000000..ed4073669
--- /dev/null
+++ b/tests/foundry/client-esm/applications/api/dialog.test-d.ts
@@ -0,0 +1,71 @@
+import { expectTypeOf } from "vitest";
+
+expectTypeOf(await foundry.applications.api.DialogV2.confirm()).toEqualTypeOf<boolean>();
+expectTypeOf(await foundry.applications.api.DialogV2.confirm({ rejectClose: true })).toEqualTypeOf<boolean>();
+expectTypeOf(await foundry.applications.api.DialogV2.confirm({ rejectClose: false })).toEqualTypeOf<boolean | null>();
+expectTypeOf(await foundry.applications.api.DialogV2.confirm({ rejectClose: 3 > 2 })).toEqualTypeOf<boolean | null>();
+
+const numberCallback = async () => 5;
+const okButton = {
+  callback: numberCallback,
+};
+
+expectTypeOf(
+  await foundry.applications.api.DialogV2.prompt({
+    ok: okButton,
+  }),
+).toEqualTypeOf<number>();
+expectTypeOf(
+  await foundry.applications.api.DialogV2.prompt({
+    ok: okButton,
+    rejectClose: false,
+  }),
+).toEqualTypeOf<number | null>();
+expectTypeOf(
+  await foundry.applications.api.DialogV2.prompt({
+    ok: okButton,
+    buttons: [
+      {
+        label: "Foo",
+        action: "foo",
+        callback: async () => 3 > 2,
+      },
+      {
+        label: "Bar",
+        action: "bar",
+      },
+    ],
+  }),
+).toEqualTypeOf<number | boolean | string>();
+
+expectTypeOf(
+  await foundry.applications.api.DialogV2.wait({
+    buttons: [
+      {
+        label: "Foo",
+        action: "foo",
+        callback: async () => 3 > 2,
+      },
+      {
+        label: "Bar",
+        action: "bar",
+      },
+    ],
+  }),
+).toEqualTypeOf<boolean | string>();
+expectTypeOf(
+  await foundry.applications.api.DialogV2.wait({
+    buttons: [
+      {
+        label: "Foo",
+        action: "foo",
+        callback: async () => 3 > 2,
+      },
+      {
+        label: "Bar",
+        action: "bar",
+      },
+    ],
+    rejectClose: false,
+  }),
+).toEqualTypeOf<boolean | string | null>();
diff --git a/tests/foundry/common/primitives/math.mjs.test-d.ts b/tests/foundry/common/primitives/math.mjs.test-d.ts
new file mode 100644
index 000000000..6ffd6be0e
--- /dev/null
+++ b/tests/foundry/common/primitives/math.mjs.test-d.ts
@@ -0,0 +1,22 @@
+import { expectTypeOf } from "vitest";
+
+expectTypeOf(Math.SQRT3).toEqualTypeOf<1.7320508075688772>();
+expectTypeOf(Math.SQRT1_3).toEqualTypeOf<0.5773502691896257>();
+
+expectTypeOf(Math.clamp(5, 1, 3)).toEqualTypeOf<number>();
+expectTypeOf(Math.clamped(5, 1, 3)).toEqualTypeOf<number>();
+
+expectTypeOf(Math.mix(5, 10, 0.5)).toEqualTypeOf<number>();
+
+expectTypeOf(Math.normalizeDegrees(240, 360)).toEqualTypeOf<number>();
+expectTypeOf(Math.normalizeDegrees(240)).toEqualTypeOf<number>();
+
+expectTypeOf(Math.normalizeRadians(4.18879)).toEqualTypeOf<number>();
+
+expectTypeOf(Math.toDegrees(4.18879)).toEqualTypeOf<number>();
+
+expectTypeOf(Math.toRadians(240)).toEqualTypeOf<number>();
+
+expectTypeOf(Math.oscillation(0.5, 2.0, 126387.8)).toEqualTypeOf<number>();
+expectTypeOf(Math.oscillation(0.5, 2.0, 126387.8, 2000)).toEqualTypeOf<number>();
+expectTypeOf(Math.oscillation(0.5, 2.0, 126387.8, 2000, Math.sin)).toEqualTypeOf<number>();