diff --git a/jsdoc-date.json b/jsdoc-date.json
new file mode 100644
index 0000000..cf6e989
--- /dev/null
+++ b/jsdoc-date.json
@@ -0,0 +1,22 @@
+{
+    "source": {
+        "include": ["lib/ui/date.js"],
+        "includePattern": "\\.js$",
+        "exclude": []
+    },
+    "plugins": ["plugins/markdown"],
+    "opts": {
+        "encoding": "utf8",
+        "destination": "./jsdoc/date",
+        "recurse": true,
+        "verbose": false,
+        "template": "node_modules/docdash",
+        "theme_opts": {
+            "default_theme": "light"
+        }
+    },
+    "markdown": {
+        "hardwrap": false,
+        "idInHeadings": true
+    }
+}
\ No newline at end of file
diff --git a/jsdoc.json b/jsdoc.json
new file mode 100644
index 0000000..81092f5
--- /dev/null
+++ b/jsdoc.json
@@ -0,0 +1,23 @@
+{
+    "source": {
+        "include": ["lib/ui/grid", "README.md"],
+        "includePattern": "\\.js$",
+        "exclude": []
+    },
+    "plugins": ["plugins/markdown"],
+    "opts": {
+        "encoding": "utf8",
+        "readme": "./README.md",
+        "destination": "./jsdoc/grid",
+        "recurse": true,
+        "verbose": false,
+        "template": "node_modules/docdash",
+        "theme_opts": {
+            "default_theme": "light"
+        }
+    },
+    "markdown": {
+        "hardwrap": false,
+        "idInHeadings": true
+    }
+}
\ No newline at end of file
diff --git a/lib/ui/css/grid.scss b/lib/ui/css/grid.scss
index 5c492aa..eb08683 100644
--- a/lib/ui/css/grid.scss
+++ b/lib/ui/css/grid.scss
@@ -599,6 +599,7 @@
         >.ui-sort-panel-grid {
             flex: 1 1 auto;
             position: relative;
+            height: calc(100% - 30px);
         }
     }
 }
diff --git a/lib/ui/date.d.ts b/lib/ui/date.d.ts
index e5e3715..fd93895 100644
--- a/lib/ui/date.d.ts
+++ b/lib/ui/date.d.ts
@@ -66,7 +66,7 @@ export class DateSelector {
      *  // 控制台会输出:dateFrom.value = '2024-01-30', formatted: '1/30/2024'
      *  ```
      */
-    static resolve(dom?: HTMLElement, trigger?: (date: Date) => void): HTMLElement;
+    static resolve(dom?: HTMLElement, trigger?: (this: DateSelector, date: Date) => void): HTMLElement;
 
     /**
      * 日期选择框构造函数
@@ -122,5 +122,5 @@ export class DateSelector {
      * @param date 日期值,或者经自定义参数中格式化函数格式后的值
      * @eventProperty
      */
-    onDateChanged?: (date: Date | any) => void;
+    onDateChanged?: (this: DateSelector, date: Date | any) => void;
 }
\ No newline at end of file
diff --git a/lib/ui/date.js b/lib/ui/date.js
index 018efd2..c7542e2 100644
--- a/lib/ui/date.js
+++ b/lib/ui/date.js
@@ -1,5 +1,12 @@
 import { createElement } from "../functions";
 
+/**
+ * 创建或转换日期选择框
+ * @param {string} [min] - 最小可选日期
+ * @param {string} [max] - 最大可选日期
+ * @param {HTMLInputElement} [element] - 转换该元素为日期选择框
+ * @returns {HTMLInputElement} 返回创建或转换的日期选择框
+ */
 export function createDateInput(min, max, element) {
     let date;
     if (element instanceof HTMLInputElement) {
@@ -39,6 +46,17 @@ function resolveDate(s) {
     return new Date(s);
 }
 
+/**
+ * 格式化日期为 M/d/yyyy 格式的字符串
+ * @param {Date | number | string} date - 需要格式化的日期值,支持的格式如下:
+ * 
+ * * `"2024-01-26"`
+ * * `"2024-01-26T00:00:00"`
+ * * `"1/26/2024"`
+ * * `"638418240000000000"`
+ * * `new Date('2024-01-26')`
+ * @returns {string} 返回格式化后的日期字符串
+ */
 export function formatDate(date) {
     date = resolveDate(date);
     if (date instanceof Date && !isNaN(date)) {
@@ -47,13 +65,18 @@ export function formatDate(date) {
     return '';
 }
 
+/**
+ * 设置显示日期
+ * @param {HTMLElement} element - 要设置显示日期的元素
+ * @param {Date | number | string} val - 日期值,支持格式参见 {@linkcode formatDate}
+ */
 export function setDateValue(element, val) {
     if (element.tagName === 'INPUT') {
         if (val === '') {
             element.value = '';
         } else if (isNaN(val)) {
-            if (/^\d{4}-\d{2}-\d{2}$/.test(val)) {
-                element.value = val;
+            if (/^\d{4}-\d{2}-\d{2}/.test(val)) {
+                element.value = String(val).substring(0, 10);
             } else if (/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(val)) {
                 element.value = toDateValue(new Date(val));
             } else {
@@ -70,6 +93,19 @@ export function setDateValue(element, val) {
     }
 }
 
+/**
+ * 自定义日期格式化回调函数
+ * @callback DateFormatterCallback
+ * @param {Date} date - 日期值
+ * @returns {any} 返回格式化后的结果
+ */
+
+/**
+ * 从日期选择框获取日期值
+ * @param {HTMLInputElement} element - 要获取的日期选择框
+ * @param {DateFormatterCallback} [formatter] - 自定义格式化函数,传入参数为 `Date` 类型
+ * @returns {string | any} 默认返回日期 `ticks` 的字符串
+ */
 export function getDateValue(element, formatter) {
     const date = element?.valueAsDate;
     if (date instanceof Date && !isNaN(date)) {
@@ -85,19 +121,47 @@ export function getDateValue(element, formatter) {
     return '';
 }
 
+/**
+ * 日期选中触发的回调函数
+ * @callback DateSelectedCallback
+ * @param {Date} date - 修改后的日期值
+ * @this DateSelector
+ */
+
+/**
+ * 日期选择框类
+ * @class
+ */
 export class DateSelector {
     _var = {
-        parent: null,
         options: null
     };
 
+    /**
+     * 日期发生变化时触发的事件
+     * @event
+     * @type {DateSelectedCallback}
+     */
     onDateChanged;
 
+    /**
+     * 日期选择框构造函数
+     * @constructor
+     * @param {object} [opts] - 日期选择框初始化参数
+     * @param {boolean} [opts.enabled] - 是否可用
+     * @param {string} [opts.minDate] - 最小可选择日期
+     * @param {string} [opts.maxDate] - 最大可选择日期
+     * @param {DateFormatterCallback} [opts.valueFormatter] - 自定义格式化函数
+     */
     constructor(opts) {
-        opts ??= {};
-        this._var.options = opts;
+        this._var.options = opts ?? {};
     }
 
+    /**
+     * 创建或转换日期选择框元素
+     * @param {HTMLInputElement} [element] - 转换该元素为日期选择框
+     * @returns {HTMLInputElement} 返回创建或转换的日期选择元素
+     */
     create(element) {
         const opts = this._var.options;
         const el = createDateInput(opts.minDate, opts.maxDate, element);
@@ -117,24 +181,45 @@ export class DateSelector {
         return el;
     }
 
+    /**
+     * 获取日期选择框元素
+     * @readonly
+     * @type {HTMLInputElement}
+     */
     get element() { return this._var.el }
 
+    /**
+     * 获取或设置日期选择框是否启用
+     * @type {boolean}
+     */
     get enabled() { return !this._var.el.disabled }
     set enabled(flag) {
         this._var.el.disabled = flag === false;
     }
 
+    /**
+     * 获取格式化的日期值,或设置日期值,支持的格式参见 {@linkcode formatDate}
+     * @type {string | any}
+     */
     get value() { return this._getDate(this._var.el.valueAsDate) }
     set value(val) {
         setDateValue(this._var.el, val);
     }
 
+    /**
+     * 获取或设置最小可选择日期
+     * @type {string}
+     */
     get minDate() { return this._var.el.min }
     set minDate(date) {
         this._var.el.min = date;
         this._var.options.minDate = date;
     }
 
+    /**
+     * 获取或设置最大可选择日期
+     * @type {string}
+     */
     get maxDate() { return this._var.el.max }
     set maxDate(date) {
         this._var.el.max = date;
@@ -155,6 +240,25 @@ export class DateSelector {
         return null;
     }
 
+    /**
+     * 把父容器下所有匹配 `input[data-type="date"]` 的元素修改为统一的日期选择框<br/><br/>
+     * 解析的属性为 `id`, `class`, `data-min`, `data-max`, `disabled`
+     * @static
+     * @param {HTMLElement} [dom=document.body] 父元素
+     * @param {DateSelectedCallback} trigger 日期设置事件触发函数(上下文为触发设置日期的 `DateSelector` 实例)
+     * @example <caption>HTML</caption>
+     *  <input id="dateFrom" data-type="date" data-min="1980-01-01"/>
+     * @example <caption>解析父容器</caption>
+     *  // 解析 document.body 下所有符合条件的元素,转换为日期选择框,第二个参数可选
+     *  DateSelector.resolve(document.body, function (date) {
+     *      console.log(`element(#${this.element.id}), date changed to: ${formatDate(date)}`);
+     *      // 当日期选择改变时,控制台将会输出:element(#dateFrom), date changed to: 1/30/2024
+     *  });
+     *  @example <caption>其他地方调用时</caption>
+     *  const value = document.querySelector('#dateFrom').value;
+     *  console.log(`dateFrom.value = '${value}', formatted: '${formatDate(value)}'`);
+     *  // 控制台会输出:dateFrom.value = '2024-01-30', formatted: '1/30/2024'
+     */
     static resolve(dom = document.body, trigger) {
         const dates = dom.querySelectorAll('input[data-type="date"]');
         for (let dat of dates) {
diff --git a/lib/ui/dropdown.js b/lib/ui/dropdown.js
index 58e27ba..8bd72a2 100644
--- a/lib/ui/dropdown.js
+++ b/lib/ui/dropdown.js
@@ -82,6 +82,27 @@ function filterSource(searchkeys, textkey, key, source) {
     return source;
 }
 
+/**
+ * 下拉列表参数对象
+ * @typedef DropdownOptions
+ * @property {string} [textKey=text] - 文本关键字
+ * @property {string} [valueKey=value] - 值关键字
+ * @property {string} [htmlKey=html] - 源码显示的关键字
+ * @property {number} [maxLength=500] - 最大输入长度
+ * @property {boolean} [multiSelect] - 是否允许多选
+ * @property {string} [selected] - 选中值
+ * @property {string[]} [selectedList] - 选中的数组
+ * @property {boolean} [disabled] - 是否禁用
+ * @property {boolean} [input] - 是否支持输入
+ * @property {boolean} [search] - 是否支持搜索
+ * @property {string[]} [searchKeys] - 搜索的关键字数组
+ * @property {string} [searchPlaceholder] - 搜索提示文本,默认值取语言资源 `searchHolder` "Search..."
+ * @property {number} [tabIndex] - 焦点索引
+ * @property {string} [placeholder] - 输入框的提示文本
+ * @property {boolean} [slideFixed] - 是否固定为向下展开
+ * @property {HTMLElement} [wrapper] - 父元素,默认添加到头元素之后
+ */
+
 export class Dropdown {
     _var = {};
     // _var.options;
diff --git a/lib/ui/grid/column.js b/lib/ui/grid/column.js
index 8a00887..7af4691 100644
--- a/lib/ui/grid/column.js
+++ b/lib/ui/grid/column.js
@@ -7,15 +7,71 @@ import { Dropdown } from "../dropdown";
 import { convertCssStyle } from "../extension";
 import { createDateInput, formatDate, setDateValue, getDateValue } from "../date";
 
+/**
+ * 列定义基类
+ * @class
+ */
 export class GridColumn {
+    /**
+     * 创建显示单元格时调用的方法
+     * @param {GridColumnDefinition} col - 列定义对象
+     * @returns {HTMLElement} 返回创建的单元格元素
+     * @virtual
+     */
     static create() {
         return createElement('span');
     }
 
+    /**
+     * 创建编辑单元格时调用的方法
+     * 
+     * 元素修改后设置行包装对象的 `__editing` 后,支持在离开编辑状态时及时触发 [leaveEdit]{@linkcode GridColumn.leaveEdit} 方法<br/>
+     * 更多例子参考代码中 {@linkcode GridDropdownColumn} 的实现。
+     * @param {Function} trigger - 编辑事件回调函数,`e` 参数会传递给 [getValue]{@linkcode GridColumn.getValue} 方法
+     * @param {GridColumnDefinition} col - 列定义对象
+     * @param {HTMLElement} container - 父容器元素
+     * @param {GridItemWrapper} vals - 行包装对象,其 `values` 属性为行数据对象
+     * @returns {HTMLElement} 返回创建的编辑状态的单元格元素
+     * @virtual
+     */
+    static createEdit() { }
+
+    /**
+     * 创建列头时调用的方法
+     * @param {GridColumnDefinition} col - 列定义对象
+     * @returns {HTMLElement} 返回创建的列头元素
+     * @virtual
+     */
+    static createCaption() { }
+
+    /**
+     * 设置单元格值时调用的方法
+     * @param {HTMLElement} element - 单元格元素
+     * @param {(string | boolean | number)} val - 待设置的单元格值
+     * @param {GridItemWrapper} vals - 行包装对象
+     * @param {GridColumnDefinition} col - 列定义对象
+     * @param {Grid} grid - Grid 对象
+     * @virtual
+     */
     static setValue(element, val) {
         element.innerText = val;
     }
 
+    /**
+     * 获取编辑状态单元格值时调用的方法
+     * @param {any} `e` 由 [createEdit]{@linkcode GridColumn.createEdit} 方法中 `trigger` 函数传递来的对象
+     * @param {GridColumnDefinition} col - 列定义对象
+     * @returns {(string | boolean | number)} 返回单元格的值
+     * @virtual
+     */
+    static getValue() { }
+
+    /**
+     * 设置单元格样式时调用的方法
+     * @param {HTMLElement} element - 单元格元素
+     * @param {object} style - 样式对象
+     * @virtual
+     */
     static setStyle(element, style) {
         // for (let css of Object.entries(style)) {
         //     element.style.setProperty(css[0], css[1]);
@@ -23,6 +79,12 @@ export class GridColumn {
         element.style.cssText = convertCssStyle(style);
     }
 
+    /**
+     * 设置单元格可用性时调用的方法
+     * @param {HTMLElement} element - 单元格元素
+     * @param {boolean} enabled - 启用值,为 `false` 时代表禁用
+     * @virtual
+     */
     static setEnabled(element, enabled) {
         const tooltip = element.querySelector('.ui-tooltip-wrapper');
         if (tooltip != null) {
@@ -30,10 +92,29 @@ export class GridColumn {
         }
     }
 
+    /**
+     * 单元格离开编辑元素时触发,需要由行包装对象的 `__editing` 来确定是否触发。
+     * @param {HTMLElement} element - 单元格元素
+     * @param {HTMLElement} container - 父容器元素
+     * @virtual
+     */
+    static leaveEdit() { }
+
     static toString() { return '[object Column]' }
 }
 
+/**
+ * 单行文本列
+ * @extends GridColumn
+ */
 export class GridInputColumn extends GridColumn {
+    /**
+     * 设置该类型是否支持触发 [onInputEnded]{@linkcode GridColumnDefinition.onInputEnded} 方法<br/>
+     * 该属性返回 `true` 后,在任意事件中修改行包装对象的 `__editing` 值,则会在行列元素变动时及时触发 [onInputEnded]{@linkcode GridColumnDefinition.onInputEnded} 方法,避免例如文本框还未触发 `onchange` 事件就被移除元素而导致的问题<br/>
+     * 更多例子参考代码中 {@linkcode GridInputColumn} 的实现
+     * @readonly
+     * @type {boolean}
+     */
     static get editing() { return true };
 
     static createEdit(trigger, col, _wrapper, vals) {
diff --git a/lib/ui/grid/grid.d.ts b/lib/ui/grid/grid.d.ts
index 3522076..50743ec 100644
--- a/lib/ui/grid/grid.d.ts
+++ b/lib/ui/grid/grid.d.ts
@@ -2,11 +2,11 @@ import { GridColumnDefinition } from "./column"
 /**
  * 单元格点击回调函数
  * 
- * @callback cellClickedCallback
  * @param {number} index - 点击的行索引
  * @param {number} colIndex - 点击的列索引
  * @returns {boolean} 返回 `false` 则取消事件冒泡
  */
+declare function cellClickedCallback(index: number, colIndex: number): boolean;
 
 /** 列数据接口 */
 interface GridItem {
@@ -159,10 +159,9 @@ export class Grid {
     willSelect?: (index: number, colIndex: number) => boolean;
     /**
      * 单元格单击时触发,colIndex 为 -1 则表示点击的是行的空白处,返回 false 则取消事件冒泡
-     * @property {cellClickedCallback}
      * @eventProperty
      */
-    cellClicked?: (index: number, colIndex: number) => boolean;
+    cellClicked?: typeof cellClickedCallback;
 
     /**
      * 选中行发生变化时触发的事件
diff --git a/lib/ui/grid/grid.js b/lib/ui/grid/grid.js
index 0cd2ce9..3291823 100644
--- a/lib/ui/grid/grid.js
+++ b/lib/ui/grid/grid.js
@@ -35,7 +35,7 @@ function indexOfParent(target) {
     return Array.prototype.indexOf.call(target.parentElement.children, target);
 }
 
-const ColumnTypes = {
+const ColumnTypeDefs = {
     0: GridColumn,
     1: GridInputColumn,
     2: GridDropdownColumn,
@@ -47,6 +47,246 @@ const ColumnTypes = {
 
 let r = lang;
 
+/**
+ * 行数据接口
+ * @typedef {object} GridItem
+ * @property {any} Value - 值
+ * @property {string} DisplayValue - 显示值
+ */
+
+/**
+ * 行数据包装接口
+ * @typedef GridItemWrapper
+ * @property {object} values - 真实数据对象
+ * @property {(GridItem | any)} values.[key]] 数据属性
+ * @property {object} source - 下拉数据源缓存对象
+ * @property {GridSourceItem[]} source.[key]] 数据源
+ */
+
+/**
+ * 下拉框数据源接口
+ * @typedef {object} GridSourceItem
+ * @property {string} value - 值
+ * @property {string} text - 显示文本
+ */
+
+/**
+ * 行数据可用性回调函数
+ * @callback GridItemBooleanCallback
+ * @param {GridItem} item - 行数据对象
+ * @returns {boolean} 返回是否可用
+ * @this GridColumnDefinition
+ */
+
+/**
+ * 行数据过滤回调函数
+ * @callback GridItemFilterCallback
+ * @param {GridItem} item - 行数据对象
+ * @param {boolean} editing - 是否处于编辑状态
+ * @param {HTMLElement} [body] - Grid 控件的 `<tbody>` 部分
+ * @this GridColumnDefinition
+ */
+
+/**
+ * 行数据处理回调函数
+ * @callback GridItemObjectCallback
+ * @param {GridItem} item - 行数据对象
+ * @returns {object} 返回任意对象
+ * @this GridColumnDefinition
+ */
+
+/**
+ * 行数据字符串回调函数
+ * @callback GridItemStringCallback
+ * @param {GridItem} item - 行数据对象
+ * @returns {string} 返回字符串
+ * @this GridColumnDefinition
+ */
+
+/**
+ * 列过滤器数据源回调函数
+ * @callback GridColumnFilterSourceCallback
+ * @param {GridColumnDefinition} col - 列定义对象
+ * @returns {GridItem[]} 返回过滤器的数据数组
+ * @this Grid
+ */
+
+/**
+ * 行数据排序回调函数
+ * @callback GridItemSortCallback
+ * @param {GridItem} a - 对比行数据1
+ * @param {GridItem} b - 对比行数据2
+ * @returns {number} 返回大小对比结果
+ */
+
+/**
+ * 下拉列表数据源回调函数
+ * @callback GridDropdownSourceCallback
+ * @param {GridItem} item - 行数据对象
+ * @returns {GridSourceItem[]} 行下拉列表数据源
+ */
+
+/**
+ * 列头复选框改变时的回调函数
+ * @callback GridColumnCheckedCallback
+ * @param {GridColumnDefinition} col - 列定义对象
+ * @param {boolean} flag - 是否选中
+ * @this Grid
+ */
+
+/**
+ * 单元格发生变化时的回调函数
+ * @callback GridCellChangedCallback
+ * @param {GridItem} item - 行数据对象
+ * @param {(boolean | string | number)} value - 修改后的值
+ * @param {(boolean | string | number)} oldValue - 修改前的值
+ * @param {any} [e] - 列修改事件传递过来的任意对象
+ * @this Grid
+ */
+
+/**
+ * 文本单元格输入完成时的回调函数
+ * @callback GridCellInputEndedCallback
+ * @param {GridColumnDefinition} col - 列定义对象
+ * @param {string} value - 修改后的文本框值
+ * @this Grid
+ */
+
+/**
+ * 列过滤点 `OK` 时的回调函数
+ * @callback GridColumnFilterOkCallback
+ * @param {GridColumnDefinition} col - 列定义对象
+ * @param {GridItem[]} selected - 选中的过滤项
+ * @this Grid
+ */
+
+/**
+ * 列过滤后的回调函数
+ * @callback GridColumnFilteredCallback
+ * @param {GridColumnDefinition} col - 列定义对象
+ * @this Grid
+ */
+
+/**
+ * 下拉框列表展开时的回调函数
+ * @callback GridColumnDropExpandedCallback
+ * @param {GridItem} item - 行数据对象
+ * @param {Dropdown} drop - 拉框对象
+ * @this GridColumnDefinition
+ */
+
+/**
+ * 列定义接口
+ * @typedef {object} GridColumnDefinition
+ * @property {string} key - 列关键字,默认以该关键字从行数据中提取单元格值,行数据的关键字属性值里包含 DisplayValue 则优先显示此值
+ * @property {(GridColumnTypeEnum | GridColumn)} [type=Grid.ColumnTypes.Common] - 列的类型,可以为 {@linkcode GridColumn} 的子类,或者内置类型 {@linkcode GridColumnTypeEnum}
+ * @property {string} [caption] - 列标题文本
+ * @property {object} [captionStyle] - 列标题的元素样式
+ * @property {number} [width] - 大于 0 则设置为该宽度,否则根据列内容自动调整列宽
+ * @property {("left" |"center" | "right")} [align=left] 列对齐方式
+ * @property {(boolean | string | GridItemBooleanCallback)} [enabled] - 列是否可用(可编辑),允许以下类型
+ * 
+ * * `boolean` 则直接使用该值
+ * * `string` 则以该值为关键字从行数据中取值作为判断条件
+ * * `(item: GridItem) => boolean` 则调用该函数(上下文为列定义对象),以返回值作为判断条件
+ * @property {GridItemFilterCallback} [filter] - 单元格取值采用该函数返回的值
+ * @property {string} [text] - 单元格以该值填充内容,忽略filter与关键字属性
+ * @property {boolean} [visible=true] - 列是否可见
+ * @property {boolean} [resizable=true] - 列是否允许调整宽度
+ * @property {boolean} [sortable=true] - 列是否允许排序
+ * @property {boolean} [orderable=true] - 列是否允许重排顺序
+ * @property {boolean} [allcheck=false] - 列为复选框类型时是否在列头增加全选复选框
+ * @property {object} [css] - 单元格css样式对象(仅在重建行元素时读取)
+ * @property {GridItemObjectCallback} [styleFilter] - 根据返回值填充单元格样式(填充行列数据时读取)
+ * @property {GridItemStringCallback} [bgFilter] - 根据返回值设置单元格背景色
+ * @property {object} [events] - 给单元格元素附加事件(事件函数上下文为数据行对象)
+ * @property {Function} events.[event]] - 事件回调函数
+ * @property {(object | GridItemObjectCallback)} [attrs] - 根据返回值设置单元格元素的附加属性,允许直接设置对象也支持函数返回对象
+ * @property {boolean} [allowFilter=false] - 是否允许进行列头过滤
+ * @property {(GridItem[] | GridColumnFilterSourceCallback)} [filterSource] - 自定义列过滤器的数据源(函数上下文为Grid)
+ * @property {GridItemSortCallback} [sortFilter] - 自定义列排序函数
+ * @property {DropdownOptions} [dropOptions] - 列为下拉列表类型时以该值设置下拉框的参数
+ * @property {(GridSourceItem[] | GridDropdownSourceCallback | Promise<GridSourceItem[]>)} [source] - 列为下拉列表类型时以该值设置下拉列表数据源,支持函数返回,也支持返回异步对象
+ * @property {boolean} [sourceCache=false] - 下拉列表数据源是否缓存结果(即行数据未发生变化时仅从source属性获取一次值)
+ * @property {("fa-light" | "fa-regular" | "fa-solid")} [iconType=fa-light] - 列为图标类型时以该值设置图标样式(函数上下文为列定义对象)
+ * @property {(string | GridItemStringCallback)} [iconClassName] - 列为图标类型时以该值作为单元格元素的额外样式类型(函数上下文为列定义对象)
+ * @property {string} [dateMin] - 列为日期类型时以该值作为最小可选日期值
+ * @property {string} [dateMax] - 列为日期类型时以该值作为最大可选日期值
+ * @property {DateFormatterCallback} [dateValueFormatter] - 列为日期类型时自定义日期格式化函数
+ * @property {(string | GridItemStringCallback)} [tooltip] - 以返回值额外设置单元格的tooltip(函数上下文为列定义对象)
+ * @property {GridColumnCheckedCallback} [onAllChecked] - 列头复选框改变时触发
+ * @property {GridColumnDefinition} onAllChecked.col - 列定义对象
+ * @property {boolean} onAllChecked.flag - 是否选中
+ * @property {GridCellChangedCallback} [onChanged] - 单元格发生变化时触发
+ * @property {GridCellInputEndedCallback} [onInputEnded] - 文本单元格在输入完成时触发的事件
+ * @property {GridColumnFilterOkCallback} [onFilterOk] - 列过滤点击 `OK` 时触发的事件
+ * @property {GridColumnFilteredCallback} [onFiltered] - 列过滤后触发的事件
+ * @property {GridColumnDropExpandedCallback} [onDropExpanded] - 列为下拉框类型时在下拉列表展开时触发的事件
+ */
+
+/**
+ * 判断复选框列的回调函数
+ * @callback ColumnTypesEnumIsCheckbox
+ * @param {number} type - 列类型
+ * @returns {boolean} 返回是否为复选框列
+ */
+
+/**
+ * 列类型枚举
+ * @enum {number}
+ */
+const GridColumnTypeEnum = {
+    /** 0 - 通用列(只读) */
+    Common: 0,
+    /** 1 - 单行文本列 */
+    Input: 1,
+    /** 2 - 下拉选择列 */
+    Dropdown: 2,
+    /** 3 - 复选框列 */
+    Checkbox: 3,
+    /** 4 - 图标列 */
+    Icon: 4,
+    /** 5 - 多行文本列 */
+    Text: 5,
+    /** 6 - 日期选择列 */
+    Date: 6,
+    /**
+     * 判断列是否为复选框列
+     * @type {ColumnTypesEnumIsCheckbox}
+     */
+    isCheckbox(type) { return type === 3 }
+};
+
+/**
+ * 列排序枚举
+ * @enum {number}
+ */
+const GridColumnDirection = {
+    /** -1 - 倒序 */
+    Descending: -1,
+    /** 1 - 升序 */
+    Ascending: 1
+};
+
+/**
+ * 列排序定义接口
+ * @typedef GridColumnSortDefinition
+ * @property {string} column - 排序列的关键字
+ * @property {("asc" | "desc")} order - 升序或降序
+ */
+
+/**
+ * 多语言文本资源回调函数
+ * @callback GridLanguageCallback
+ * @param {string} id - 资源 ID
+ * @param {string} [def] - 默认资源
+ * @returns 返回获取的多语言资源
+ */
+
+/**
+ * Grid 控件基础类
+ * @class
+ */
 export class Grid {
     _var = {
         selectedColumnIndex: -1,
@@ -74,44 +314,189 @@ export class Grid {
     // _var.colAttrs = {};
     // _var.vtable = [];
 
+    /**
+     * 列定义的数组
+     * @type {GridColumnDefinition[]}
+     */
     columns = [];
+    /**
+     * 多语言资源对象
+     * @type {object}
+     * @property {string} [all=( All )]
+     * @property {string} [ok=OK]
+     * @property {string} [reset=Reset]
+     * @property {string} [cancel=Cancel]
+     * @property {string} [null=( Null )]
+     * @property {string} [addLevel=Add Level]
+     * @property {string} [deleteLevel=Delete Level]
+     * @property {string} [copyLevel=Copy Level]
+     * @property {string} [asc=Ascending]
+     * @property {string} [desc=Descending]
+     * @property {string} [column=Column]
+     * @property {string} [order=Order]
+     * @property {string} [sort=Sort]
+     * @property {string} [requirePrompt=All sort criteria must have a column specified. Check the selected sort criteria and try again.]
+     * @property {string} [duplicatePrompt={column} is being sorted more than once. Delete the duplicate sort criteria and try again.]
+     */
     langs = {};
+    /**
+     * 行数大于等于该值则启用虚模式
+     * @type {number}
+     * @default 100
+     */
     virtualCount = 100;
+    /**
+     * 表格行高
+     * @type {number}
+     * @default 36
+     */
     rowHeight = 36;
+    /**
+     * 文本行高
+     * @type {number}
+     * @default 24
+     */
     lineHeight = 24;
+    /**
+     * 列表底部留出额外的空白行
+     * @type {number}
+     * @default 0
+     */
     extraRows = 0;
+    /**
+     * 过滤条件列表的行高
+     * @type {number}
+     * @default 30
+     */
     filterRowHeight = 30;
+    /**
+     * 列表高度值,为 0 时列表始终显示全部内容(自增高),为非数字或者小于 0 则根据容器高度来确定虚模式的渲染行数
+     * @type {number | null}
+     */
     height;
+    /**
+     * 列表是否为只读
+     * @type {boolean}
+     */
     readonly;
+    /**
+     * 是否允许多选
+     * @type {boolean}
+     * @default false
+     */
     multiSelect = false;
+    /**
+     * 为 `false` 时只有点击在单元格内才会选中行
+     * @type {boolean}
+     * @default true
+     */
     fullrowClick = true;
+    /**
+     * 单元格 tooltip 是否禁用
+     * @type {boolean}
+     * @default false
+     */
     tooltipDisabled = false;
+    /**
+     * 列头是否显示
+     * @type {boolean}
+     * @default true
+     */
     headerVisible = true;
+    /**
+     * 监听事件的窗口载体
+     * @type {(Window | HTMLElement)}
+     * @default window
+     */
     window = global;
+    /**
+     * 排序列的索引
+     * @type {number}
+     * @default -1
+     */
     sortIndex = -1;
-    sortDirection = 1;
+    /**
+     * 排序方式,正数升序,负数倒序
+     * @type {GridColumnDirection}
+     * @default GridColumnDirection.Ascending
+     */
+    sortDirection = GridColumnDirection.Ascending;
+    /**
+     * 排序列数组
+     * @type {GridColumnSortDefinition[]}
+     * @default null
+     */
     sortArray = null;
 
+    /**
+     * 即将选中行时触发
+     * @event
+     * @param {number} index - 即将选中的行索引
+     * @param {number} colIndex - 即将选中的列索引
+     * @returns {boolean} 返回 `false`、`null`、`undefined`、`0` 等则取消选中动作
+     */
     willSelect;
+    /**
+     * 单元格单击时触发,colIndex 为 -1 则表示点击的是行的空白处
+     * @event
+     * @param {number} index - 点击的行索引
+     * @param {number} colIndex - 点击的列索引
+     * @returns {boolean} 返回 false 则取消事件冒泡
+     */
     cellClicked;
 
+    /**
+     * 选中行发生变化时触发的事件
+     * @event
+     * @param {number} index - 选中的行索引
+     */
     onSelectedRowChanged;
+    /**
+     * 单元格双击时触发的事件,colIndex 为 -1 则表示点击的是行的空白处
+     * @event
+     * @param {number} index - 双击的行索引
+     * @param {number} colIndex - 双击的列索引
+     */
     onCellDblClicked;
+    /**
+     * 行双击时触发的事件
+     * @event
+     * @param {number} index - 双击的行索引
+     */
     onRowDblClicked;
+    /**
+     * 列发生变化时触发的事件
+     * @event
+     * @param {("reorder" | "resize" | "sort")} type - 事件类型
+     * 
+     * * "reorder" 为发生列重排事件,此时 value 为目标列索引
+     * * "resize" 为发生列宽调整事件,此时 value 为列宽度值
+     * * "sort" 为发生列排序事件,此时 value 为 1(升序)或 -1(倒序)
+     * @param {number} colIndex - 发生变化事件的列索引
+     * @param {number | GridColumnDirection} value - 变化的值
+     */
     onColumnChanged;
+    /**
+     * 列滚动时触发的事件
+     * @event
+     * @param {Event} e - 滚动事件对象
+     */
     onBodyScrolled;
 
-    static ColumnTypes = {
-        Common: 0,
-        Input: 1,
-        Dropdown: 2,
-        Checkbox: 3,
-        Icon: 4,
-        Text: 5,
-        Date: 6,
-        isCheckbox(type) { return type === 3 }
-    };
+    /**
+     * 列类型枚举
+     * @readonly
+     * @type {GridColumnTypeEnum}
+     */
+    static get ColumnTypes() { return GridColumnTypeEnum }
 
+    /**
+     * 
+     * @param {(string | HTMLElement)} container Grid 控件所在的父容器,可以是 string 表示选择器,也可以是 HTMLElement 对象
+     * Grid 控件构造函数
+     * _构造时可以不进行赋值,但是调用 init 函数时则必须进行赋值_
+     * @param {GridLanguageCallback} [getText] 获取多语言文本的函数代理
+     */
     constructor(container, getText) {
         this._var.parent = typeof container === 'string' ? document.querySelector(container) : container;
         if (typeof getText === 'function') {
@@ -123,21 +508,31 @@ export class Grid {
             reset: r('reset', 'Reset'),
             cancel: r('cancel', 'Cancel'),
             null: r('null', '( Null )'),
-            addLevel: r('', 'Add level'),
-            deleteLevel: r('', 'Delete level'),
-            copyLevel: r('', 'Copy level'),
-            asc: r('', 'Ascending'),
-            desc: r('', 'Descending'),
-            column: r('', 'Column'),
-            order: r('', 'Order'),
-            sort: r('', 'Sort'),
-            requirePrompt: r('', 'Column required.'),
-            duplicatePrompt: r('', 'Column duplicated: "{column}"')
+            addLevel: r('addLevel', 'Add level'),
+            deleteLevel: r('deleteLevel', 'Delete level'),
+            copyLevel: r('copyLevel', 'Copy level'),
+            asc: r('asc', 'Ascending'),
+            desc: r('desc', 'Descending'),
+            column: r('column', 'Column'),
+            order: r('order', 'Order'),
+            sort: r('sort', 'Sort'),
+            requirePrompt: r('requirePrompt', 'All sort criteria must have a column specified. Check the selected sort criteria and try again.'),
+            duplicatePrompt: r('duplicatePrompt', '{column} is being sorted more than once. Delete the duplicate sort criteria and try again.')
         };
     }
 
+    /**
+     * 获取 Grid 的页面元素
+     * @readonly
+     * @type {HTMLDivElement}
+     */
     get element() { return this._var.el }
 
+    /**
+     * 获取当前 Grid 是否已发生改变
+     * @readonly
+     * @type {boolean}
+     */
     get changed() {
         const source = this._var.source;
         if (source == null) {
@@ -146,8 +541,17 @@ export class Grid {
         return source.find(r => r.__changed) != null;
     }
 
+    /**
+     * 返回所有数据的数据(未过滤)
+     * @readonly
+     * @type {GridItem[]}
+     */
     get allSource() { return this._var.source?.map(s => s.values) }
 
+    /**
+     * 获取已过滤的数据数组,或者设置数据并刷新列表
+     * @type {GridItem[]}
+     */
     get source() { return this._var.currentSource?.map(s => s.values) }
     set source(list) {
         if (this._var.el == null) {
@@ -166,6 +570,11 @@ export class Grid {
         this._refreshSource(list);
     }
 
+    /**
+     * 设置单行数据
+     * @param {number} index - 行索引
+     * @param {GridItem} item - 待设置的行数据对象
+     */
     setItem(index, item) {
         if (this._var.currentSource == null) {
             throw new Error('no source');
@@ -174,9 +583,21 @@ export class Grid {
         // clear dropdown source cache
         delete it.source;
         it.values = item;
-        this.refresh();
+        if (this.sortIndex >= 0) {
+            this.sortColumn();
+        } else if (this.sortArray?.length > 0) {
+            this.sort();
+        } else {
+            this.refresh();
+        }
     }
 
+    /**
+     * 添加行数据
+     * @param {GridItem} item - 待添加的行数据值
+     * @param {number} [index] - 待添加的行索引
+     * @returns {GridItem} 返回已添加的行数据
+     */
     addItem(item, index) {
         if (this._var.currentSource == null) {
             throw new Error('no source');
@@ -199,10 +620,22 @@ export class Grid {
                 this._var.source.push(newIt);
             }
         }
-        this.reload();
+        if (this.sortIndex >= 0) {
+            this.sortColumn();
+        } else if (this.sortArray?.length > 0) {
+            this.sort();
+        } else {
+            this.reload();
+        }
         return item;
     }
 
+    /**
+     * 批量添加行数据
+     * @param {GridItem[]} array - 待添加的行数据数组
+     * @param {number} [index] - 待添加的行索引
+     * @returns {GridItem[]} 返回已添加的行数据数组
+     */
     addItems(array, index) {
         if (this._var.currentSource == null) {
             throw new Error('no source');
@@ -230,10 +663,21 @@ export class Grid {
                 this._var.source.push(...items);
             }
         }
-        this.reload();
+        if (this.sortIndex >= 0) {
+            this.sortColumn();
+        } else if (this.sortArray?.length > 0) {
+            this.sort();
+        } else {
+            this.reload();
+        }
         return array;
     }
 
+    /**
+     * 删除行数据
+     * @param {number} index - 待删除的行索引
+     * @returns {GridItem} 返回已删除的行数据
+     */
     removeItem(index) {
         if (this._var.currentSource == null) {
             throw new Error('no source');
@@ -257,6 +701,11 @@ export class Grid {
         return it.values;
     }
 
+    /**
+     * 批量删除行数据
+     * @param {number[]} [indexes] - 待删除的行索引数组,未传值时删除所有行
+     * @returns {GridItem[]} 返回已删除的行数据数组
+     */
     removeItems(indexes) {
         if (this._var.currentSource == null) {
             throw new Error('no source');
@@ -334,8 +783,18 @@ export class Grid {
         this.resize();
     }
 
+    /**
+     * 获取当前是否为虚模式状态
+     * @readonly
+     * @type {boolean}
+     */
     get virtual() { return this._var.currentSource?.length > this.virtualCount }
 
+    /**
+     * 获取当前排序的列关键字,为 null 则当前无排序列
+     * @readonly
+     * @type {string | null}
+     */
     get sortKey() {
         if (this.columns == null) {
             return null;
@@ -348,6 +807,10 @@ export class Grid {
         return Array.prototype.slice.call(this._var.refs.body.children);
     }
 
+    /**
+     * 获取或设置当前选中的行索引的数组,设置后会刷新列表
+     * @type {number[]}
+     */
     get selectedIndexes() { return this._var.selectedIndexes }
     set selectedIndexes(indexes) {
         const startIndex = this._var.startIndex;
@@ -368,8 +831,17 @@ export class Grid {
         }
     }
 
+    /**
+     * 获取当前选中行的索引,为 -1 则当前没有选中行
+     * @readonly
+     * @type {number}
+     */
     get selectedIndex() { return (this._var.selectedIndexes && this._var.selectedIndexes[0]) ?? -1 }
 
+    /**
+     * 获取或设置 Grid 当前的加载状态
+     * @type {boolean}
+     */
     get loading() { return this._var.refs.loading?.style?.visibility === 'visible' }
     set loading(flag) {
         if (this._var.refs.loading == null) {
@@ -384,6 +856,10 @@ export class Grid {
         }
     }
 
+    /**
+     * 获取或设置 Grid 当前滚动的偏移量
+     * @type {number}
+     */
     get scrollTop() { return this._var.el?.scrollTop; }
     set scrollTop(top) {
         if (this._var.el == null) {
@@ -393,6 +869,10 @@ export class Grid {
         this.reload(true);
     }
 
+    /**
+     * 初始化Grid控件
+     * @param {HTMLElement} [container=.ctor#container] - 父容器元素,若未传值则采用构造方法中传入的父容器元素
+     */
     init(container = this._var.parent) {
         this._var.el = null;
         this._var.refs = {};
@@ -488,15 +968,28 @@ export class Grid {
         }
     }
 
+    /**
+     * 设置数据列表,该方法为 [source]{@linkcode Grid#source} 属性的语法糖
+     * @param {GridItem[]} source - 待设置的数据列表
+     */
     setData(source) {
         this.source = source;
     }
 
+    /**
+     * 滚动到指定行的位置
+     * @param {number} index - 待滚动至的行索引
+     */
     scrollToIndex(index) {
         const top = this._scrollToTop(index * (this.rowHeight + 1), true);
         this._var.el.scrollTop = top;
     }
 
+    /**
+     * 调整 Grid 元素的大小,一般需要在宽度变化时(如页面大小发生变化时)调用
+     * @param {boolean} [force] - 是否强制 [reload]{@linkcode Grid#reload},默认只有待渲染的行数发生变化时才会调用
+     * @param {boolean} [keep] - 是否保持当前滚动位置
+     */
     resize(force, keep) {
         if (this._var.rendering || this._var.el == null) {
             return;
@@ -518,6 +1011,10 @@ export class Grid {
         this._var.bodyClientWidth = body.clientWidth;
     }
 
+    /**
+     * 重新计算需要渲染的行,并载入元素,一般需要在高度变化时调用
+     * @param {boolean} [keep] - 是否保持当前滚动位置
+     */
     reload(keep) {
         const filtered = this.columns.some(c => c.filterValues != null);
         if ((filtered ^ this._var.colAttrs.__filtered) === 1) {
@@ -552,6 +1049,9 @@ export class Grid {
         this.refresh();
     }
 
+    /**
+     * 重新填充Grid单元格数据
+     */
     refresh() {
         if (this._var.refs.body == null) {
             throw new Error('body has not been created.');
@@ -575,6 +1075,9 @@ export class Grid {
         }
     }
 
+    /**
+     * 把所有行重置为未修改的状态
+     */
     resetChange() {
         if (this._var.source == null) {
             return;
@@ -608,6 +1111,10 @@ export class Grid {
         return (a, b) => col.sortFilter(a.values, b.values) * direction;
     }
 
+    /**
+     * 根据当前排序字段进行列排序
+     * @param {boolean} [reload] - 为 `true` 则在列排序后调用 [reload]{@linkcode Grid#reload} 方法
+     */
     sortColumn(reload) {
         const index = this.sortIndex;
         const col = this.columns[index];
@@ -642,6 +1149,10 @@ export class Grid {
         }
     }
 
+    /**
+     * 根据当前排序列数组进行多列排序
+     * @param {boolean} [reload] - 为 `true` 则在多列排序后调用 [reload]{@linkcode Grid#reload} 方法
+     */
     sort(reload) {
         const sortArray = this.sortArray;
         if (sortArray == null || sortArray.length === 0) {
@@ -689,11 +1200,17 @@ export class Grid {
         });
     }
 
+    /**
+     * 清除列头复选框的选中状态
+     */
     clearHeaderCheckbox() {
         const boxes = this._var.refs.header.querySelectorAll('.ui-check-wrapper>input');
         boxes.forEach(box => box.checked = false);
     }
 
+    /**
+     * 显示多列排序设置面板
+     */
     showSortPanel() {
         const content = createElement('div', 'ui-sort-panel-content');
         const buttonWrapper = createElement('div', 'ui-sort-panel-buttons');
@@ -811,7 +1328,7 @@ export class Grid {
                 key: 'column',
                 caption: this.langs.column,
                 width: 270,
-                type: Grid.ColumnTypes.Dropdown,
+                type: GridColumnTypeEnum.Dropdown,
                 dropOptions: {
                     textKey: 'caption',
                     valueKey: 'key'
@@ -824,7 +1341,7 @@ export class Grid {
                 key: 'order',
                 caption: this.langs.order,
                 width: 150,
-                type: Grid.ColumnTypes.Dropdown,
+                type: GridColumnTypeEnum.Dropdown,
                 source: [
                     { value: 'asc', text: this.langs.asc },
                     { value: 'desc', text: this.langs.desc }
@@ -915,13 +1432,13 @@ export class Grid {
                 continue;
             }
             // style
-            const isCheckbox = Grid.ColumnTypes.isCheckbox(col.type);
+            const isCheckbox = GridColumnTypeEnum.isCheckbox(col.type);
             let type = this._var.colTypes[col.key];
             if (type == null) {
                 if (isNaN(col.type)) {
                     type = col.type;
                 } else {
-                    type = ColumnTypes[col.type];
+                    type = ColumnTypeDefs[col.type];
                 }
                 type ??= GridColumn;
                 this._var.colTypes[col.key] = type;
@@ -1097,7 +1614,7 @@ export class Grid {
                         if (style !== '') {
                             cell.style.cssText = style;
                         }
-                        if (Grid.ColumnTypes.isCheckbox(col.type)) {
+                        if (GridColumnTypeEnum.isCheckbox(col.type)) {
                             cell.appendChild(GridCheckboxColumn.createEdit(e => this._onRowChanged(e, exists + i, col, e.target.checked, cell)));
                             // this._var.colTypes[col.key] = GridCheckboxColumn;
                         } else {
@@ -1106,7 +1623,7 @@ export class Grid {
                                 if (isNaN(col.type)) {
                                     type = col.type;
                                 } else {
-                                    type = ColumnTypes[col.type];
+                                    type = ColumnTypeDefs[col.type];
                                 }
                                 type ??= GridColumn;
                                 this._var.colTypes[col.key] = type;
@@ -1184,7 +1701,7 @@ export class Grid {
                     const bgColor = col.bgFilter(item);
                     cell.style.backgroundColor = bgColor ?? '';
                 }
-                const isCheckbox = Grid.ColumnTypes.isCheckbox(col.type);
+                const isCheckbox = GridColumnTypeEnum.isCheckbox(col.type);
                 const type = isCheckbox ? GridCheckboxColumn : this._var.colTypes[col.key] ?? GridColumn;
                 let element;
                 if (!isCheckbox && typeof type.createEdit === 'function') {
diff --git a/package-lock.json b/package-lock.json
index 40764eb..68a7af7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,9 @@
       "version": "0.0.1",
       "devDependencies": {
         "@mxssfd/typedoc-theme": "^1.1.3",
+        "clean-jsdoc-theme": "^4.2.17",
+        "docdash": "^2.0.2",
+        "jsdoc": "^4.0.2",
         "postcss-preset-env": "^9.3.0",
         "sass": "^1.70.0",
         "typedoc": "^0.25.7",
@@ -16,6 +19,18 @@
         "vite-plugin-externals": "^0.6.2"
       }
     },
+    "node_modules/@babel/parser": {
+      "version": "7.23.9",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz",
+      "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==",
+      "dev": true,
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
     "node_modules/@csstools/cascade-layer-name-parser": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-1.0.7.tgz",
@@ -1281,6 +1296,76 @@
         "node": ">=12"
       }
     },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
+      "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/set-array": "^1.0.1",
+        "@jridgewell/sourcemap-codec": "^1.4.10",
+        "@jridgewell/trace-mapping": "^0.3.9"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/resolve-uri": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
+      "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/set-array": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+      "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/source-map": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
+      "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.0",
+        "@jridgewell/trace-mapping": "^0.3.9"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.4.15",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+      "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+      "dev": true
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.22",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz",
+      "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
+      }
+    },
+    "node_modules/@jsdoc/salty": {
+      "version": "0.2.7",
+      "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.7.tgz",
+      "integrity": "sha512-mh8LbS9d4Jq84KLw8pzho7XC2q2/IJGiJss3xwRoLD1A+EE16SjN4PfaG4jRCzKegTFLlN0Zd8SdUPE6XdoPFg==",
+      "dev": true,
+      "dependencies": {
+        "lodash": "^4.17.21"
+      },
+      "engines": {
+        "node": ">=v12.0.0"
+      }
+    },
     "node_modules/@mxssfd/typedoc-theme": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/@mxssfd/typedoc-theme/-/typedoc-theme-1.1.3.tgz",
@@ -1468,6 +1553,28 @@
       "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
       "dev": true
     },
+    "node_modules/@types/linkify-it": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz",
+      "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==",
+      "dev": true
+    },
+    "node_modules/@types/markdown-it": {
+      "version": "12.2.3",
+      "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz",
+      "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/linkify-it": "*",
+        "@types/mdurl": "*"
+      }
+    },
+    "node_modules/@types/mdurl": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz",
+      "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==",
+      "dev": true
+    },
     "node_modules/acorn": {
       "version": "8.11.3",
       "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
@@ -1499,6 +1606,12 @@
         "node": ">= 8"
       }
     },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true
+    },
     "node_modules/autoprefixer": {
       "version": "10.4.17",
       "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz",
@@ -1551,6 +1664,12 @@
         "node": ">=8"
       }
     },
+    "node_modules/bluebird": {
+      "version": "3.7.2",
+      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+      "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+      "dev": true
+    },
     "node_modules/brace-expansion": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
@@ -1604,6 +1723,22 @@
         "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
       }
     },
+    "node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "dev": true
+    },
+    "node_modules/camel-case": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
+      "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+      "dev": true,
+      "dependencies": {
+        "pascal-case": "^3.1.2",
+        "tslib": "^2.0.3"
+      }
+    },
     "node_modules/caniuse-lite": {
       "version": "1.0.30001580",
       "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001580.tgz",
@@ -1624,6 +1759,18 @@
         }
       ]
     },
+    "node_modules/catharsis": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
+      "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==",
+      "dev": true,
+      "dependencies": {
+        "lodash": "^4.17.15"
+      },
+      "engines": {
+        "node": ">= 10"
+      }
+    },
     "node_modules/chokidar": {
       "version": "3.5.3",
       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
@@ -1651,6 +1798,44 @@
         "fsevents": "~2.3.2"
       }
     },
+    "node_modules/clean-css": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
+      "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
+      "dev": true,
+      "dependencies": {
+        "source-map": "~0.6.0"
+      },
+      "engines": {
+        "node": ">= 10.0"
+      }
+    },
+    "node_modules/clean-jsdoc-theme": {
+      "version": "4.2.17",
+      "resolved": "https://registry.npmjs.org/clean-jsdoc-theme/-/clean-jsdoc-theme-4.2.17.tgz",
+      "integrity": "sha512-5SbJNXcQHUXd7N13g+3OpGFiBQdxz36xwEP3p1r1vbo/apLcDRtugaFdUZ56H6Rvlb68Q33EChoBkajSlnD11w==",
+      "dev": true,
+      "dependencies": {
+        "@jsdoc/salty": "^0.2.4",
+        "fs-extra": "^10.1.0",
+        "html-minifier-terser": "^7.2.0",
+        "klaw-sync": "^6.0.0",
+        "lodash": "^4.17.21",
+        "showdown": "^2.1.0"
+      },
+      "peerDependencies": {
+        "jsdoc": ">=3.x <=4.x"
+      }
+    },
+    "node_modules/commander": {
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
+      "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
+      "dev": true,
+      "engines": {
+        "node": ">=14"
+      }
+    },
     "node_modules/css-blank-pseudo": {
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-6.0.1.tgz",
@@ -1753,12 +1938,40 @@
         "node": ">=4"
       }
     },
+    "node_modules/docdash": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/docdash/-/docdash-2.0.2.tgz",
+      "integrity": "sha512-3SDDheh9ddrwjzf6dPFe1a16M6ftstqTNjik2+1fx46l24H9dD2osT2q9y+nBEC1wWz4GIqA48JmicOLQ0R8xA==",
+      "dev": true,
+      "dependencies": {
+        "@jsdoc/salty": "^0.2.1"
+      }
+    },
+    "node_modules/dot-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+      "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+      "dev": true,
+      "dependencies": {
+        "no-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
     "node_modules/electron-to-chromium": {
       "version": "1.4.645",
       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.645.tgz",
       "integrity": "sha512-EeS1oQDCmnYsRDRy2zTeC336a/4LZ6WKqvSaM1jLocEk5ZuyszkQtCpsqvuvaIXGOUjwtvF6LTcS8WueibXvSw==",
       "dev": true
     },
+    "node_modules/entities": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
+      "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
     "node_modules/es-module-lexer": {
       "version": "0.4.1",
       "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.4.1.tgz",
@@ -1812,6 +2025,15 @@
         "node": ">=6"
       }
     },
+    "node_modules/escape-string-regexp": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+      "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/fill-range": {
       "version": "7.0.1",
       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -1883,6 +2105,39 @@
       "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
       "dev": true
     },
+    "node_modules/html-minifier-terser": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz",
+      "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==",
+      "dev": true,
+      "dependencies": {
+        "camel-case": "^4.1.2",
+        "clean-css": "~5.3.2",
+        "commander": "^10.0.0",
+        "entities": "^4.4.0",
+        "param-case": "^3.0.4",
+        "relateurl": "^0.2.7",
+        "terser": "^5.15.1"
+      },
+      "bin": {
+        "html-minifier-terser": "cli.js"
+      },
+      "engines": {
+        "node": "^14.13.1 || >=16.0.0"
+      }
+    },
+    "node_modules/html-minifier-terser/node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
     "node_modules/immutable": {
       "version": "4.3.4",
       "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz",
@@ -1931,6 +2186,44 @@
         "node": ">=0.12.0"
       }
     },
+    "node_modules/js2xmlparser": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz",
+      "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==",
+      "dev": true,
+      "dependencies": {
+        "xmlcreate": "^2.0.4"
+      }
+    },
+    "node_modules/jsdoc": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz",
+      "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/parser": "^7.20.15",
+        "@jsdoc/salty": "^0.2.1",
+        "@types/markdown-it": "^12.2.3",
+        "bluebird": "^3.7.2",
+        "catharsis": "^0.9.0",
+        "escape-string-regexp": "^2.0.0",
+        "js2xmlparser": "^4.0.2",
+        "klaw": "^3.0.0",
+        "markdown-it": "^12.3.2",
+        "markdown-it-anchor": "^8.4.1",
+        "marked": "^4.0.10",
+        "mkdirp": "^1.0.4",
+        "requizzle": "^0.2.3",
+        "strip-json-comments": "^3.1.0",
+        "underscore": "~1.13.2"
+      },
+      "bin": {
+        "jsdoc": "jsdoc.js"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
     "node_modules/jsonc-parser": {
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz",
@@ -1949,6 +2242,48 @@
         "graceful-fs": "^4.1.6"
       }
     },
+    "node_modules/klaw": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz",
+      "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==",
+      "dev": true,
+      "dependencies": {
+        "graceful-fs": "^4.1.9"
+      }
+    },
+    "node_modules/klaw-sync": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
+      "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
+      "dev": true,
+      "dependencies": {
+        "graceful-fs": "^4.1.11"
+      }
+    },
+    "node_modules/linkify-it": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz",
+      "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==",
+      "dev": true,
+      "dependencies": {
+        "uc.micro": "^1.0.1"
+      }
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+      "dev": true
+    },
+    "node_modules/lower-case": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+      "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+      "dev": true,
+      "dependencies": {
+        "tslib": "^2.0.3"
+      }
+    },
     "node_modules/lunr": {
       "version": "2.3.9",
       "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
@@ -1964,6 +2299,32 @@
         "sourcemap-codec": "^1.4.8"
       }
     },
+    "node_modules/markdown-it": {
+      "version": "12.3.2",
+      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz",
+      "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==",
+      "dev": true,
+      "dependencies": {
+        "argparse": "^2.0.1",
+        "entities": "~2.1.0",
+        "linkify-it": "^3.0.1",
+        "mdurl": "^1.0.1",
+        "uc.micro": "^1.0.5"
+      },
+      "bin": {
+        "markdown-it": "bin/markdown-it.js"
+      }
+    },
+    "node_modules/markdown-it-anchor": {
+      "version": "8.6.7",
+      "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz",
+      "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==",
+      "dev": true,
+      "peerDependencies": {
+        "@types/markdown-it": "*",
+        "markdown-it": "*"
+      }
+    },
     "node_modules/marked": {
       "version": "4.3.0",
       "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz",
@@ -1976,6 +2337,12 @@
         "node": ">= 12"
       }
     },
+    "node_modules/mdurl": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
+      "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
+      "dev": true
+    },
     "node_modules/minimatch": {
       "version": "9.0.3",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
@@ -1991,6 +2358,18 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/mkdirp": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+      "dev": true,
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/nanoid": {
       "version": "3.3.7",
       "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
@@ -2009,6 +2388,16 @@
         "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
       }
     },
+    "node_modules/no-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+      "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+      "dev": true,
+      "dependencies": {
+        "lower-case": "^2.0.2",
+        "tslib": "^2.0.3"
+      }
+    },
     "node_modules/node-releases": {
       "version": "2.0.14",
       "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
@@ -2033,6 +2422,26 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/param-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
+      "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+      "dev": true,
+      "dependencies": {
+        "dot-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/pascal-case": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+      "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+      "dev": true,
+      "dependencies": {
+        "no-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
     "node_modules/picocolors": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -2760,6 +3169,24 @@
         "node": ">=8.10.0"
       }
     },
+    "node_modules/relateurl": {
+      "version": "0.2.7",
+      "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+      "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/requizzle": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz",
+      "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==",
+      "dev": true,
+      "dependencies": {
+        "lodash": "^4.17.21"
+      }
+    },
     "node_modules/rollup": {
       "version": "4.9.6",
       "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.6.tgz",
@@ -2821,6 +3248,40 @@
         "vscode-textmate": "^8.0.0"
       }
     },
+    "node_modules/showdown": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz",
+      "integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==",
+      "dev": true,
+      "dependencies": {
+        "commander": "^9.0.0"
+      },
+      "bin": {
+        "showdown": "bin/showdown.js"
+      },
+      "funding": {
+        "type": "individual",
+        "url": "https://www.paypal.me/tiviesantos"
+      }
+    },
+    "node_modules/showdown/node_modules/commander": {
+      "version": "9.5.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
+      "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
+      "dev": true,
+      "engines": {
+        "node": "^12.20.0 || >=14"
+      }
+    },
+    "node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/source-map-js": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
@@ -2830,6 +3291,16 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/source-map-support": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+      "dev": true,
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
     "node_modules/sourcemap-codec": {
       "version": "1.4.8",
       "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
@@ -2837,6 +3308,42 @@
       "deprecated": "Please use @jridgewell/sourcemap-codec instead",
       "dev": true
     },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/terser": {
+      "version": "5.27.0",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz",
+      "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/source-map": "^0.3.3",
+        "acorn": "^8.8.2",
+        "commander": "^2.20.0",
+        "source-map-support": "~0.5.20"
+      },
+      "bin": {
+        "terser": "bin/terser"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/terser/node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true
+    },
     "node_modules/to-regex-range": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -2849,6 +3356,12 @@
         "node": ">=8.0"
       }
     },
+    "node_modules/tslib": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+      "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+      "dev": true
+    },
     "node_modules/typedoc": {
       "version": "0.25.7",
       "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.7.tgz",
@@ -2884,6 +3397,18 @@
         "node": ">=14.17"
       }
     },
+    "node_modules/uc.micro": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
+      "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
+      "dev": true
+    },
+    "node_modules/underscore": {
+      "version": "1.13.6",
+      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
+      "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
+      "dev": true
+    },
     "node_modules/universalify": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
@@ -3013,6 +3538,12 @@
       "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz",
       "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==",
       "dev": true
+    },
+    "node_modules/xmlcreate": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz",
+      "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==",
+      "dev": true
     }
   }
 }
diff --git a/package.json b/package.json
index 1ec841f..b860176 100644
--- a/package.json
+++ b/package.json
@@ -23,10 +23,15 @@
   "scripts": {
     "dev": "vite",
     "build": "node ./vite.build.js && typedoc",
-    "doc": "typedoc"
+    "doc": "typedoc",
+    "jsdoc": "jsdoc -c jsdoc.json",
+    "jsdoc-date": "jsdoc -c jsdoc-date.json"
   },
   "devDependencies": {
     "@mxssfd/typedoc-theme": "^1.1.3",
+    "clean-jsdoc-theme": "^4.2.17",
+    "docdash": "^2.0.2",
+    "jsdoc": "^4.0.2",
     "postcss-preset-env": "^9.3.0",
     "sass": "^1.70.0",
     "typedoc": "^0.25.7",