Add UI button registration API (registerButton, unregisterButton, getButtonsForLocation)
This commit is contained in:
2
dist/PluginContext.d.ts.map
vendored
2
dist/PluginContext.d.ts.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"PluginContext.d.ts","sourceRoot":"","sources":["../src/PluginContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAC;IAEjC;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,oBAAoB,EAC7B,QAAQ,EAAE,cAAc,GACvB,aAAa,CAqJf"}
|
{"version":3,"file":"PluginContext.d.ts","sourceRoot":"","sources":["../src/PluginContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAC;IAEjC;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,oBAAoB,EAC7B,QAAQ,EAAE,cAAc,GACvB,aAAa,CA4Jf"}
|
||||||
7
dist/PluginContext.js
vendored
7
dist/PluginContext.js
vendored
@@ -40,6 +40,13 @@ export function createPluginContext(options, registry) {
|
|||||||
unregisterRenderer: (type) => {
|
unregisterRenderer: (type) => {
|
||||||
registry.unregisterRenderer(type);
|
registry.unregisterRenderer(type);
|
||||||
},
|
},
|
||||||
|
registerButton: (button) => {
|
||||||
|
registry.registerButton(pluginId, button);
|
||||||
|
registry.addLog(pluginId, 'log', [`Registered button: ${button.id} at ${button.location}`]);
|
||||||
|
},
|
||||||
|
unregisterButton: (buttonId) => {
|
||||||
|
registry.unregisterButton(pluginId, buttonId);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
define: (schema) => {
|
define: (schema) => {
|
||||||
|
|||||||
2
dist/PluginContext.js.map
vendored
2
dist/PluginContext.js.map
vendored
File diff suppressed because one or more lines are too long
44
dist/PluginInterfaces.d.ts
vendored
44
dist/PluginInterfaces.d.ts
vendored
@@ -139,6 +139,8 @@ export interface PluginContext {
|
|||||||
ui: {
|
ui: {
|
||||||
registerRenderer: (type: string, renderer: CustomRenderer) => void;
|
registerRenderer: (type: string, renderer: CustomRenderer) => void;
|
||||||
unregisterRenderer: (type: string) => void;
|
unregisterRenderer: (type: string) => void;
|
||||||
|
registerButton: (button: UIButtonDefinition) => void;
|
||||||
|
unregisterButton: (buttonId: string) => void;
|
||||||
};
|
};
|
||||||
/** Settings API */
|
/** Settings API */
|
||||||
settings: {
|
settings: {
|
||||||
@@ -188,7 +190,47 @@ export interface PluginSettingsSection {
|
|||||||
component: unknown;
|
component: unknown;
|
||||||
}
|
}
|
||||||
/** UI locations where plugins can inject content. */
|
/** UI locations where plugins can inject content. */
|
||||||
export type UILocation = 'room-header' | 'message-actions' | 'user-menu' | 'room-menu' | 'composer-actions';
|
export type UILocation = 'room-header' | 'message-actions' | 'user-menu' | 'room-menu' | 'composer-actions' | 'channel-list' | 'direct-messages' | 'home-section' | 'search-notification-section' | 'text-composer-toolbar' | 'sidebar-actions';
|
||||||
|
/**
|
||||||
|
* Button positioning configuration for plugins.
|
||||||
|
* Allows buttons to be placed before or after existing elements or groups.
|
||||||
|
*/
|
||||||
|
export interface UIButtonPosition {
|
||||||
|
/** Place button before this element/group ID */
|
||||||
|
before?: string;
|
||||||
|
/** Place button after this element/group ID */
|
||||||
|
after?: string;
|
||||||
|
/**
|
||||||
|
* Optional group ID this button belongs to.
|
||||||
|
* Buttons in the same group are rendered together.
|
||||||
|
*/
|
||||||
|
group?: string;
|
||||||
|
/** Order within the group (lower numbers appear first) */
|
||||||
|
order?: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Definition for a UI button registered by a plugin.
|
||||||
|
* Framework-agnostic - the component/element is passed as unknown.
|
||||||
|
*/
|
||||||
|
export interface UIButtonDefinition {
|
||||||
|
/** Unique ID for this button */
|
||||||
|
id: string;
|
||||||
|
/** Location where the button should appear */
|
||||||
|
location: UILocation;
|
||||||
|
/** Button label or tooltip */
|
||||||
|
label: string;
|
||||||
|
/** Optional icon identifier */
|
||||||
|
icon?: string;
|
||||||
|
/** Positioning configuration */
|
||||||
|
position?: UIButtonPosition;
|
||||||
|
/**
|
||||||
|
* The actual button component or render function.
|
||||||
|
* Framework-agnostic - hosts using React should cast to ReactNode.
|
||||||
|
*/
|
||||||
|
component: unknown;
|
||||||
|
/** Optional callback when clicked (if component doesn't handle it) */
|
||||||
|
onClick?: () => void | Promise<void>;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* The main plugin interface that all plugins must implement.
|
* The main plugin interface that all plugins must implement.
|
||||||
*/
|
*/
|
||||||
|
|||||||
2
dist/PluginInterfaces.d.ts.map
vendored
2
dist/PluginInterfaces.d.ts.map
vendored
File diff suppressed because one or more lines are too long
21
dist/PluginRegistry.d.ts
vendored
21
dist/PluginRegistry.d.ts
vendored
@@ -1,4 +1,4 @@
|
|||||||
import type { Plugin, PluginContext, PluginCommand, MessageInterceptor, MessageContext, CustomRenderer, SettingsSchema, PluginTheme, PluginLogEntry } from './PluginInterfaces.js';
|
import type { Plugin, PluginContext, PluginCommand, MessageInterceptor, MessageContext, CustomRenderer, SettingsSchema, PluginTheme, PluginLogEntry, UIButtonDefinition, UILocation } from './PluginInterfaces.js';
|
||||||
import type { IPluginStorage } from './interfaces.js';
|
import type { IPluginStorage } from './interfaces.js';
|
||||||
/**
|
/**
|
||||||
* Configuration options for the PluginRegistry.
|
* Configuration options for the PluginRegistry.
|
||||||
@@ -32,6 +32,7 @@ export declare class PluginRegistry {
|
|||||||
private beforeSendInterceptors;
|
private beforeSendInterceptors;
|
||||||
private receiveInterceptors;
|
private receiveInterceptors;
|
||||||
private readonly renderers;
|
private readonly renderers;
|
||||||
|
private readonly buttons;
|
||||||
private readonly settingsSchemas;
|
private readonly settingsSchemas;
|
||||||
private readonly pluginSettings;
|
private readonly pluginSettings;
|
||||||
private readonly matrixHandlers;
|
private readonly matrixHandlers;
|
||||||
@@ -52,7 +53,7 @@ export declare class PluginRegistry {
|
|||||||
/** Remove a registered command by name. */
|
/** Remove a registered command by name. */
|
||||||
unregisterCommand(name: string): void;
|
unregisterCommand(name: string): void;
|
||||||
/** Execute a command by name, passing raw argument string. */
|
/** Execute a command by name, passing raw argument string. */
|
||||||
executeCommand(name: string, rawArgs: string): Promise<string | void>;
|
executeCommand(name: string, rawArgs: string, roomId?: string): Promise<string | void>;
|
||||||
private parseCommandArgs;
|
private parseCommandArgs;
|
||||||
/** Return all registered commands. */
|
/** Return all registered commands. */
|
||||||
getCommands(): Array<{
|
getCommands(): Array<{
|
||||||
@@ -74,6 +75,22 @@ export declare class PluginRegistry {
|
|||||||
unregisterRenderer(type: string): void;
|
unregisterRenderer(type: string): void;
|
||||||
/** Get the renderer for a given type, if any. */
|
/** Get the renderer for a given type, if any. */
|
||||||
getRenderer(type: string): CustomRenderer | undefined;
|
getRenderer(type: string): CustomRenderer | undefined;
|
||||||
|
/** Register a UI button contributed by a plugin. */
|
||||||
|
registerButton(pluginId: string, button: UIButtonDefinition): void;
|
||||||
|
/** Remove a registered button by ID. */
|
||||||
|
unregisterButton(pluginId: string, buttonId: string): void;
|
||||||
|
/**
|
||||||
|
* Get all buttons for a specific UI location, sorted by position configuration.
|
||||||
|
* Returns buttons in the order they should be rendered.
|
||||||
|
*/
|
||||||
|
getButtonsForLocation(location: UILocation): UIButtonDefinition[];
|
||||||
|
/**
|
||||||
|
* Sort buttons according to their position configuration.
|
||||||
|
* - Buttons with position.before/after are positioned relative to existing buttons
|
||||||
|
* - Buttons in groups are kept together
|
||||||
|
* - Within groups, buttons are sorted by order property
|
||||||
|
*/
|
||||||
|
private sortButtonsByPosition;
|
||||||
/** Define the settings schema for a plugin and load persisted values. */
|
/** Define the settings schema for a plugin and load persisted values. */
|
||||||
defineSettings(pluginId: string, schema: SettingsSchema): void;
|
defineSettings(pluginId: string, schema: SettingsSchema): void;
|
||||||
/** Get a plugin setting value. */
|
/** Get a plugin setting value. */
|
||||||
|
|||||||
2
dist/PluginRegistry.d.ts.map
vendored
2
dist/PluginRegistry.d.ts.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"PluginRegistry.d.ts","sourceRoot":"","sources":["../src/PluginRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,cAAc,EACd,WAAW,EACX,cAAc,EACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,cAAc,EAAsB,MAAM,iBAAiB,CAAC;AAI1E;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;IAEzB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAE9E;;;OAGG;IACH,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD;AAED;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiE;IACzF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmE;IAC5F,OAAO,CAAC,sBAAsB,CAAoE;IAClG,OAAO,CAAC,mBAAmB,CAAoE;IAC/F,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqE;IAC/F,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqC;IACrE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA8C;IAC7E,OAAO,CAAC,QAAQ,CAAC,cAAc,CAG3B;IACJ,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAGnB;IACJ,OAAO,CAAC,IAAI,CAAwB;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAA6C;IAChF,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAA+C;gBAExE,OAAO,GAAE,qBAA0B;IAU/C,sFAAsF;IACtF,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI;IASxE,uFAAuF;IACjF,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkDjD,kDAAkD;IAClD,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI;IAQ/D,2CAA2C;IAC3C,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIrC,8DAA8D;IACxD,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAW3E,OAAO,CAAC,gBAAgB;IAWxB,sCAAsC;IACtC,WAAW,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,aAAa,CAAA;KAAE,CAAC;IAQhF,kDAAkD;IAClD,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,kBAAkB,GAAG,IAAI;IAIjF,8CAA8C;IAC9C,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,kBAAkB,GAAG,IAAI;IAI9E,gFAAgF;IAC1E,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;IAYrE,4EAA4E;IACtE,cAAc,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;IAgBlE,0DAA0D;IAC1D,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI;IAIhF,iCAAiC;IACjC,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAItC,iDAAiD;IACjD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAQrD,yEAAyE;IACzE,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI;IAmB9D,kCAAkC;IAClC,gBAAgB,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAI3E,iDAAiD;IACjD,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAOrE,4CAA4C;IAC5C,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAQrE,gDAAgD;IAChD,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI;IAazD,kDAAkD;IAClD,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAQxD,2CAA2C;IAC3C,eAAe,IAAI,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAajG,qDAAqD;IACrD,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAAG,IAAI;IAO7F,iDAAiD;IACjD,kBAAkB,CAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAChC,IAAI;IAQP,kDAAkD;IAClD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;KAAE,CAAC;IAQnG,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAMjD,yEAAyE;IACzE,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IASpD,wCAAwC;IACxC,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAOhF,oEAAoE;IACpE,OAAO,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,cAAc,EAAE;IAI5C,iEAAiE;IACjE,SAAS,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAQlC,iDAAiD;IACjD,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAI3C;;;OAGG;IACH,oBAAoB,IAAI,KAAK,CAAC;QAC5B,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,gBAAgB,EAAE,MAAM,CAAC;QACzB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,OAAO,CAAC;KACtB,CAAC;IAoBF,sEAAsE;IAChE,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAUjG,uDAAuD;IACvD,KAAK,IAAI,IAAI;CAgBd"}
|
{"version":3,"file":"PluginRegistry.d.ts","sourceRoot":"","sources":["../src/PluginRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,cAAc,EACd,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,UAAU,EACX,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,cAAc,EAAsB,MAAM,iBAAiB,CAAC;AAI1E;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;IAEzB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAE9E;;;OAGG;IACH,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD;AAED;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiE;IACzF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmE;IAC5F,OAAO,CAAC,sBAAsB,CAAoE;IAClG,OAAO,CAAC,mBAAmB,CAAoE;IAC/F,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqE;IAC/F,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuE;IAC/F,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqC;IACrE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA8C;IAC7E,OAAO,CAAC,QAAQ,CAAC,cAAc,CAG3B;IACJ,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAGnB;IACJ,OAAO,CAAC,IAAI,CAAwB;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAA6C;IAChF,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAA+C;gBAExE,OAAO,GAAE,qBAA0B;IAU/C,sFAAsF;IACtF,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI;IASxE,uFAAuF;IACjF,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsDjD,kDAAkD;IAClD,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI;IAQ/D,2CAA2C;IAC3C,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIrC,8DAA8D;IACxD,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAc5F,OAAO,CAAC,gBAAgB;IA4BxB,sCAAsC;IACtC,WAAW,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,aAAa,CAAA;KAAE,CAAC;IAQhF,kDAAkD;IAClD,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,kBAAkB,GAAG,IAAI;IAIjF,8CAA8C;IAC9C,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,kBAAkB,GAAG,IAAI;IAI9E,gFAAgF;IAC1E,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;IAYrE,4EAA4E;IACtE,cAAc,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;IAgBlE,0DAA0D;IAC1D,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI;IAIhF,iCAAiC;IACjC,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAItC,iDAAiD;IACjD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAQrD,oDAAoD;IACpD,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI;IASlE,wCAAwC;IACxC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK1D;;;OAGG;IACH,qBAAqB,CAAC,QAAQ,EAAE,UAAU,GAAG,kBAAkB,EAAE;IAQjE;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IA8F7B,yEAAyE;IACzE,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI;IAmB9D,kCAAkC;IAClC,gBAAgB,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAI3E,iDAAiD;IACjD,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAOrE,4CAA4C;IAC5C,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAQrE,gDAAgD;IAChD,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI;IAazD,kDAAkD;IAClD,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAQxD,2CAA2C;IAC3C,eAAe,IAAI,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAajG,qDAAqD;IACrD,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAAG,IAAI;IAO7F,iDAAiD;IACjD,kBAAkB,CAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAChC,IAAI;IAQP,kDAAkD;IAClD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;KAAE,CAAC;IAQnG,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAMjD,yEAAyE;IACzE,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IASpD,wCAAwC;IACxC,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAOhF,oEAAoE;IACpE,OAAO,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,cAAc,EAAE;IAI5C,iEAAiE;IACjE,SAAS,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAQlC,iDAAiD;IACjD,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAI3C;;;OAGG;IACH,oBAAoB,IAAI,KAAK,CAAC;QAC5B,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,gBAAgB,EAAE,MAAM,CAAC;QACzB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,OAAO,CAAC;KACtB,CAAC;IAoBF,sEAAsE;IAChE,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAUjG,uDAAuD;IACvD,KAAK,IAAI,IAAI;CAgBd"}
|
||||||
141
dist/PluginRegistry.js
vendored
141
dist/PluginRegistry.js
vendored
@@ -11,6 +11,7 @@ export class PluginRegistry {
|
|||||||
this.beforeSendInterceptors = [];
|
this.beforeSendInterceptors = [];
|
||||||
this.receiveInterceptors = [];
|
this.receiveInterceptors = [];
|
||||||
this.renderers = new Map();
|
this.renderers = new Map();
|
||||||
|
this.buttons = new Map();
|
||||||
this.settingsSchemas = new Map();
|
this.settingsSchemas = new Map();
|
||||||
this.pluginSettings = new Map();
|
this.pluginSettings = new Map();
|
||||||
this.matrixHandlers = new Map();
|
this.matrixHandlers = new Map();
|
||||||
@@ -57,6 +58,10 @@ export class PluginRegistry {
|
|||||||
if (renderer.pluginId === id)
|
if (renderer.pluginId === id)
|
||||||
this.renderers.delete(type);
|
this.renderers.delete(type);
|
||||||
}
|
}
|
||||||
|
for (const [buttonId, btn] of this.buttons.entries()) {
|
||||||
|
if (btn.pluginId === id)
|
||||||
|
this.buttons.delete(buttonId);
|
||||||
|
}
|
||||||
for (const [themeId, theme] of this.themes.entries()) {
|
for (const [themeId, theme] of this.themes.entries()) {
|
||||||
if (theme.pluginId === id) {
|
if (theme.pluginId === id) {
|
||||||
this.onThemeUnregistered?.(themeId);
|
this.onThemeUnregistered?.(themeId);
|
||||||
@@ -93,11 +98,14 @@ export class PluginRegistry {
|
|||||||
this.commands.delete(name);
|
this.commands.delete(name);
|
||||||
}
|
}
|
||||||
/** Execute a command by name, passing raw argument string. */
|
/** Execute a command by name, passing raw argument string. */
|
||||||
async executeCommand(name, rawArgs) {
|
async executeCommand(name, rawArgs, roomId) {
|
||||||
const cmd = this.commands.get(name);
|
const cmd = this.commands.get(name);
|
||||||
if (!cmd)
|
if (!cmd)
|
||||||
throw new Error(`Command /${name} not found`);
|
throw new Error(`Command /${name} not found`);
|
||||||
const args = this.parseCommandArgs(cmd.command, rawArgs);
|
const args = this.parseCommandArgs(cmd.command, rawArgs);
|
||||||
|
if (roomId) {
|
||||||
|
args.roomId = roomId;
|
||||||
|
}
|
||||||
const entry = this.plugins.get(cmd.pluginId);
|
const entry = this.plugins.get(cmd.pluginId);
|
||||||
if (!entry)
|
if (!entry)
|
||||||
throw new Error(`Plugin ${cmd.pluginId} not loaded`);
|
throw new Error(`Plugin ${cmd.pluginId} not loaded`);
|
||||||
@@ -106,11 +114,25 @@ export class PluginRegistry {
|
|||||||
parseCommandArgs(command, rawArgs) {
|
parseCommandArgs(command, rawArgs) {
|
||||||
if (!command.args || command.args.length === 0)
|
if (!command.args || command.args.length === 0)
|
||||||
return {};
|
return {};
|
||||||
const parts = rawArgs.trim().split(/\s+/);
|
const trimmedArgs = rawArgs.trim();
|
||||||
|
if (!trimmedArgs)
|
||||||
|
return {};
|
||||||
|
const argNames = command.args.map(arg => typeof arg === 'string' ? arg : arg.name);
|
||||||
|
// If only one arg (that's not roomId), give it all the text
|
||||||
|
const nonRoomIdArgs = argNames.filter(name => name !== 'roomId');
|
||||||
|
if (nonRoomIdArgs.length === 1) {
|
||||||
|
const result = {};
|
||||||
|
result[nonRoomIdArgs[0]] = trimmedArgs;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
// Otherwise, split by whitespace
|
||||||
|
const parts = trimmedArgs.split(/\s+/);
|
||||||
const result = {};
|
const result = {};
|
||||||
command.args.forEach((arg, index) => {
|
command.args.forEach((arg, index) => {
|
||||||
const argName = typeof arg === 'string' ? arg : arg.name;
|
const argName = typeof arg === 'string' ? arg : arg.name;
|
||||||
result[argName] = parts[index];
|
if (argName !== 'roomId') {
|
||||||
|
result[argName] = parts[index];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -171,6 +193,119 @@ export class PluginRegistry {
|
|||||||
return this.renderers.get(type)?.renderer;
|
return this.renderers.get(type)?.renderer;
|
||||||
}
|
}
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
// UI Buttons
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
/** Register a UI button contributed by a plugin. */
|
||||||
|
registerButton(pluginId, button) {
|
||||||
|
const fullId = `${pluginId}:${button.id}`;
|
||||||
|
if (this.buttons.has(fullId)) {
|
||||||
|
console.warn(`[PluginRegistry] Button ${fullId} already registered`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.buttons.set(fullId, { pluginId, button: { ...button, id: fullId } });
|
||||||
|
}
|
||||||
|
/** Remove a registered button by ID. */
|
||||||
|
unregisterButton(pluginId, buttonId) {
|
||||||
|
const fullId = `${pluginId}:${buttonId}`;
|
||||||
|
this.buttons.delete(fullId);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get all buttons for a specific UI location, sorted by position configuration.
|
||||||
|
* Returns buttons in the order they should be rendered.
|
||||||
|
*/
|
||||||
|
getButtonsForLocation(location) {
|
||||||
|
const buttons = Array.from(this.buttons.values())
|
||||||
|
.filter(({ button }) => button.location === location)
|
||||||
|
.map(({ button }) => button);
|
||||||
|
return this.sortButtonsByPosition(buttons);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Sort buttons according to their position configuration.
|
||||||
|
* - Buttons with position.before/after are positioned relative to existing buttons
|
||||||
|
* - Buttons in groups are kept together
|
||||||
|
* - Within groups, buttons are sorted by order property
|
||||||
|
*/
|
||||||
|
sortButtonsByPosition(buttons) {
|
||||||
|
if (buttons.length === 0)
|
||||||
|
return [];
|
||||||
|
const grouped = new Map();
|
||||||
|
const ungrouped = [];
|
||||||
|
const positionedButtons = new Map();
|
||||||
|
for (const button of buttons) {
|
||||||
|
positionedButtons.set(button.id, button);
|
||||||
|
if (button.position?.group) {
|
||||||
|
if (!grouped.has(button.position.group)) {
|
||||||
|
grouped.set(button.position.group, []);
|
||||||
|
}
|
||||||
|
grouped.get(button.position.group).push(button);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ungrouped.push(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const [_group, groupButtons] of grouped) {
|
||||||
|
groupButtons.sort((a, b) => {
|
||||||
|
const orderA = a.position?.order ?? 0;
|
||||||
|
const orderB = b.position?.order ?? 0;
|
||||||
|
return orderA - orderB;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const result = [];
|
||||||
|
const inserted = new Set();
|
||||||
|
const insertButton = (button) => {
|
||||||
|
if (inserted.has(button.id))
|
||||||
|
return;
|
||||||
|
const group = button.position?.group ? grouped.get(button.position.group) : [button];
|
||||||
|
if (!group)
|
||||||
|
return;
|
||||||
|
for (const btn of group) {
|
||||||
|
if (!inserted.has(btn.id)) {
|
||||||
|
result.push(btn);
|
||||||
|
inserted.add(btn.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (const button of [...ungrouped, ...Array.from(grouped.values()).flat()]) {
|
||||||
|
if (inserted.has(button.id))
|
||||||
|
continue;
|
||||||
|
if (button.position?.before) {
|
||||||
|
const beforeButton = positionedButtons.get(button.position.before);
|
||||||
|
if (beforeButton) {
|
||||||
|
const idx = result.findIndex(b => b.id === beforeButton.id);
|
||||||
|
if (idx !== -1) {
|
||||||
|
const group = button.position.group ? grouped.get(button.position.group) : [button];
|
||||||
|
if (group) {
|
||||||
|
for (let i = group.length - 1; i >= 0; i--) {
|
||||||
|
if (!inserted.has(group[i].id)) {
|
||||||
|
result.splice(idx, 0, group[i]);
|
||||||
|
inserted.add(group[i].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (button.position?.after) {
|
||||||
|
const afterButton = positionedButtons.get(button.position.after);
|
||||||
|
if (afterButton) {
|
||||||
|
const idx = result.findIndex(b => b.id === afterButton.id);
|
||||||
|
if (idx !== -1) {
|
||||||
|
insertButton(button);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
insertButton(button);
|
||||||
|
}
|
||||||
|
for (const button of buttons) {
|
||||||
|
if (!inserted.has(button.id)) {
|
||||||
|
result.push(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
// Settings
|
// Settings
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** Define the settings schema for a plugin and load persisted values. */
|
/** Define the settings schema for a plugin and load persisted values. */
|
||||||
|
|||||||
2
dist/PluginRegistry.js.map
vendored
2
dist/PluginRegistry.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/index.browser.d.ts
vendored
2
dist/index.browser.d.ts
vendored
@@ -6,7 +6,7 @@ export { PluginMarketplaceManager } from './PluginMarketplaceManager.js';
|
|||||||
export type { PluginMarketplaceHostAdapter, PluginMarketplaceInstalledRecord, PluginMarketplaceManagerOptions, } from './PluginMarketplaceManager.js';
|
export type { PluginMarketplaceHostAdapter, PluginMarketplaceInstalledRecord, PluginMarketplaceManagerOptions, } from './PluginMarketplaceManager.js';
|
||||||
export type { PluginMetadata, PluginIndex, InstalledPlugin, } from './types.js';
|
export type { PluginMetadata, PluginIndex, InstalledPlugin, } from './types.js';
|
||||||
export { PluginTab } from './types.js';
|
export { PluginTab } from './types.js';
|
||||||
export type { ThemeColorGroup, ThemePaletteGroup, PluginThemeColors, PluginTheme, CommandArg, PluginCommand, MessageInterceptor, MessageContext, CustomRenderer, SettingDefinition, SettingsSchema, NotificationOptions, PluginContext, PluginSettingsSection, UILocation, Plugin, PluginLogEntry, } from './PluginInterfaces.js';
|
export type { ThemeColorGroup, ThemePaletteGroup, PluginThemeColors, PluginTheme, CommandArg, PluginCommand, MessageInterceptor, MessageContext, CustomRenderer, SettingDefinition, SettingsSchema, NotificationOptions, PluginContext, PluginSettingsSection, UILocation, UIButtonDefinition, UIButtonPosition, Plugin, PluginLogEntry, } from './PluginInterfaces.js';
|
||||||
export { generateThemeCSS } from './theme-css.js';
|
export { generateThemeCSS } from './theme-css.js';
|
||||||
export { PluginRegistry } from './PluginRegistry.js';
|
export { PluginRegistry } from './PluginRegistry.js';
|
||||||
export type { PluginRegistryOptions } from './PluginRegistry.js';
|
export type { PluginRegistryOptions } from './PluginRegistry.js';
|
||||||
|
|||||||
2
dist/index.browser.d.ts.map
vendored
2
dist/index.browser.d.ts.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"index.browser.d.ts","sourceRoot":"","sources":["../src/index.browser.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,cAAc,EACd,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,YAAY,EACV,8BAA8B,EAC9B,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,YAAY,EACV,4BAA4B,EAC5B,gCAAgC,EAChC,+BAA+B,GAChC,MAAM,+BAA+B,CAAC;AAEvC,YAAY,EACV,cAAc,EACd,WAAW,EACX,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,UAAU,EACV,MAAM,EACN,cAAc,GACf,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC"}
|
{"version":3,"file":"index.browser.d.ts","sourceRoot":"","sources":["../src/index.browser.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,cAAc,EACd,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,YAAY,EACV,8BAA8B,EAC9B,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,YAAY,EACV,4BAA4B,EAC5B,gCAAgC,EAChC,+BAA+B,GAChC,MAAM,+BAA+B,CAAC;AAEvC,YAAY,EACV,cAAc,EACd,WAAW,EACX,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,UAAU,EACV,kBAAkB,EAClB,gBAAgB,EAChB,MAAM,EACN,cAAc,GACf,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC"}
|
||||||
2
dist/index.browser.js.map
vendored
2
dist/index.browser.js.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"index.browser.js","sourceRoot":"","sources":["../src/index.browser.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAOvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAYzE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAsBvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC"}
|
{"version":3,"file":"index.browser.js","sourceRoot":"","sources":["../src/index.browser.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAOvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAYzE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAwBvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC"}
|
||||||
2
dist/index.d.ts
vendored
2
dist/index.d.ts
vendored
@@ -6,7 +6,7 @@ export { PluginMarketplaceManager } from './PluginMarketplaceManager.js';
|
|||||||
export type { PluginMarketplaceHostAdapter, PluginMarketplaceManagerOptions, } from './PluginMarketplaceManager.js';
|
export type { PluginMarketplaceHostAdapter, PluginMarketplaceManagerOptions, } from './PluginMarketplaceManager.js';
|
||||||
export type { PluginMetadata, PluginIndex, InstalledPlugin, } from './types.js';
|
export type { PluginMetadata, PluginIndex, InstalledPlugin, } from './types.js';
|
||||||
export { PluginTab } from './types.js';
|
export { PluginTab } from './types.js';
|
||||||
export type { ThemeColorGroup, ThemePaletteGroup, PluginThemeColors, PluginTheme, CommandArg, PluginCommand, MessageInterceptor, MessageContext, CustomRenderer, SettingDefinition, SettingsSchema, NotificationOptions, PluginContext, PluginSettingsSection, UILocation, Plugin, PluginLogEntry, } from './PluginInterfaces.js';
|
export type { ThemeColorGroup, ThemePaletteGroup, PluginThemeColors, PluginTheme, CommandArg, PluginCommand, MessageInterceptor, MessageContext, CustomRenderer, SettingDefinition, SettingsSchema, NotificationOptions, PluginContext, PluginSettingsSection, UILocation, UIButtonDefinition, UIButtonPosition, Plugin, PluginLogEntry, } from './PluginInterfaces.js';
|
||||||
export { generateThemeCSS } from './theme-css.js';
|
export { generateThemeCSS } from './theme-css.js';
|
||||||
export { PluginRegistry } from './PluginRegistry.js';
|
export { PluginRegistry } from './PluginRegistry.js';
|
||||||
export type { PluginRegistryOptions } from './PluginRegistry.js';
|
export type { PluginRegistryOptions } from './PluginRegistry.js';
|
||||||
|
|||||||
2
dist/index.d.ts.map
vendored
2
dist/index.d.ts.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,cAAc,EACd,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,YAAY,EACV,8BAA8B,EAC9B,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,YAAY,EACV,4BAA4B,EAC5B,+BAA+B,GAChC,MAAM,+BAA+B,CAAC;AAEvC,YAAY,EACV,cAAc,EACd,WAAW,EACX,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,UAAU,EACV,MAAM,EACN,cAAc,GACf,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE/D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC"}
|
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,cAAc,EACd,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,YAAY,EACV,8BAA8B,EAC9B,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,YAAY,EACV,4BAA4B,EAC5B,+BAA+B,GAChC,MAAM,+BAA+B,CAAC;AAEvC,YAAY,EACV,cAAc,EACd,WAAW,EACX,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,UAAU,EACV,kBAAkB,EAClB,gBAAgB,EAChB,MAAM,EACN,cAAc,GACf,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE/D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC"}
|
||||||
2
dist/index.js.map
vendored
2
dist/index.js.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAOvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAWzE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAsBvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAGzD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAOvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAWzE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAwBvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAGzD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
||||||
@@ -72,6 +72,13 @@ export function createPluginContext(
|
|||||||
unregisterRenderer: (type) => {
|
unregisterRenderer: (type) => {
|
||||||
registry.unregisterRenderer(type);
|
registry.unregisterRenderer(type);
|
||||||
},
|
},
|
||||||
|
registerButton: (button) => {
|
||||||
|
registry.registerButton(pluginId, button);
|
||||||
|
registry.addLog(pluginId, 'log', [`Registered button: ${button.id} at ${button.location}`]);
|
||||||
|
},
|
||||||
|
unregisterButton: (buttonId) => {
|
||||||
|
registry.unregisterButton(pluginId, buttonId);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
settings: {
|
settings: {
|
||||||
|
|||||||
@@ -148,6 +148,8 @@ export interface PluginContext {
|
|||||||
ui: {
|
ui: {
|
||||||
registerRenderer: (type: string, renderer: CustomRenderer) => void;
|
registerRenderer: (type: string, renderer: CustomRenderer) => void;
|
||||||
unregisterRenderer: (type: string) => void;
|
unregisterRenderer: (type: string) => void;
|
||||||
|
registerButton: (button: UIButtonDefinition) => void;
|
||||||
|
unregisterButton: (buttonId: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Settings API */
|
/** Settings API */
|
||||||
@@ -211,7 +213,55 @@ export type UILocation =
|
|||||||
| 'message-actions'
|
| 'message-actions'
|
||||||
| 'user-menu'
|
| 'user-menu'
|
||||||
| 'room-menu'
|
| 'room-menu'
|
||||||
| 'composer-actions';
|
| 'composer-actions'
|
||||||
|
| 'channel-list'
|
||||||
|
| 'direct-messages'
|
||||||
|
| 'home-section'
|
||||||
|
| 'search-notification-section'
|
||||||
|
| 'text-composer-toolbar'
|
||||||
|
| 'sidebar-actions';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button positioning configuration for plugins.
|
||||||
|
* Allows buttons to be placed before or after existing elements or groups.
|
||||||
|
*/
|
||||||
|
export interface UIButtonPosition {
|
||||||
|
/** Place button before this element/group ID */
|
||||||
|
before?: string;
|
||||||
|
/** Place button after this element/group ID */
|
||||||
|
after?: string;
|
||||||
|
/**
|
||||||
|
* Optional group ID this button belongs to.
|
||||||
|
* Buttons in the same group are rendered together.
|
||||||
|
*/
|
||||||
|
group?: string;
|
||||||
|
/** Order within the group (lower numbers appear first) */
|
||||||
|
order?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definition for a UI button registered by a plugin.
|
||||||
|
* Framework-agnostic - the component/element is passed as unknown.
|
||||||
|
*/
|
||||||
|
export interface UIButtonDefinition {
|
||||||
|
/** Unique ID for this button */
|
||||||
|
id: string;
|
||||||
|
/** Location where the button should appear */
|
||||||
|
location: UILocation;
|
||||||
|
/** Button label or tooltip */
|
||||||
|
label: string;
|
||||||
|
/** Optional icon identifier */
|
||||||
|
icon?: string;
|
||||||
|
/** Positioning configuration */
|
||||||
|
position?: UIButtonPosition;
|
||||||
|
/**
|
||||||
|
* The actual button component or render function.
|
||||||
|
* Framework-agnostic - hosts using React should cast to ReactNode.
|
||||||
|
*/
|
||||||
|
component: unknown;
|
||||||
|
/** Optional callback when clicked (if component doesn't handle it) */
|
||||||
|
onClick?: () => void | Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main plugin interface that all plugins must implement.
|
* The main plugin interface that all plugins must implement.
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import type {
|
|||||||
SettingsSchema,
|
SettingsSchema,
|
||||||
PluginTheme,
|
PluginTheme,
|
||||||
PluginLogEntry,
|
PluginLogEntry,
|
||||||
|
UIButtonDefinition,
|
||||||
|
UILocation,
|
||||||
} from './PluginInterfaces.js';
|
} from './PluginInterfaces.js';
|
||||||
import type { IPluginStorage, IPluginEventClient } from './interfaces.js';
|
import type { IPluginStorage, IPluginEventClient } from './interfaces.js';
|
||||||
import { MemoryStorage } from './interfaces.js';
|
import { MemoryStorage } from './interfaces.js';
|
||||||
@@ -48,6 +50,7 @@ export class PluginRegistry {
|
|||||||
private beforeSendInterceptors: Array<{ pluginId: string; interceptor: MessageInterceptor }> = [];
|
private beforeSendInterceptors: Array<{ pluginId: string; interceptor: MessageInterceptor }> = [];
|
||||||
private receiveInterceptors: Array<{ pluginId: string; interceptor: MessageInterceptor }> = [];
|
private receiveInterceptors: Array<{ pluginId: string; interceptor: MessageInterceptor }> = [];
|
||||||
private readonly renderers = new Map<string, { pluginId: string; renderer: CustomRenderer }>();
|
private readonly renderers = new Map<string, { pluginId: string; renderer: CustomRenderer }>();
|
||||||
|
private readonly buttons = new Map<string, { pluginId: string; button: UIButtonDefinition }>();
|
||||||
private readonly settingsSchemas = new Map<string, SettingsSchema>();
|
private readonly settingsSchemas = new Map<string, SettingsSchema>();
|
||||||
private readonly pluginSettings = new Map<string, Record<string, unknown>>();
|
private readonly pluginSettings = new Map<string, Record<string, unknown>>();
|
||||||
private readonly matrixHandlers = new Map<
|
private readonly matrixHandlers = new Map<
|
||||||
@@ -109,6 +112,10 @@ export class PluginRegistry {
|
|||||||
if (renderer.pluginId === id) this.renderers.delete(type);
|
if (renderer.pluginId === id) this.renderers.delete(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const [buttonId, btn] of this.buttons.entries()) {
|
||||||
|
if (btn.pluginId === id) this.buttons.delete(buttonId);
|
||||||
|
}
|
||||||
|
|
||||||
for (const [themeId, theme] of this.themes.entries()) {
|
for (const [themeId, theme] of this.themes.entries()) {
|
||||||
if (theme.pluginId === id) {
|
if (theme.pluginId === id) {
|
||||||
this.onThemeUnregistered?.(themeId);
|
this.onThemeUnregistered?.(themeId);
|
||||||
@@ -151,11 +158,14 @@ export class PluginRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Execute a command by name, passing raw argument string. */
|
/** Execute a command by name, passing raw argument string. */
|
||||||
async executeCommand(name: string, rawArgs: string): Promise<string | void> {
|
async executeCommand(name: string, rawArgs: string, roomId?: string): Promise<string | void> {
|
||||||
const cmd = this.commands.get(name);
|
const cmd = this.commands.get(name);
|
||||||
if (!cmd) throw new Error(`Command /${name} not found`);
|
if (!cmd) throw new Error(`Command /${name} not found`);
|
||||||
|
|
||||||
const args = this.parseCommandArgs(cmd.command, rawArgs);
|
const args = this.parseCommandArgs(cmd.command, rawArgs);
|
||||||
|
if (roomId) {
|
||||||
|
args.roomId = roomId;
|
||||||
|
}
|
||||||
const entry = this.plugins.get(cmd.pluginId);
|
const entry = this.plugins.get(cmd.pluginId);
|
||||||
if (!entry) throw new Error(`Plugin ${cmd.pluginId} not loaded`);
|
if (!entry) throw new Error(`Plugin ${cmd.pluginId} not loaded`);
|
||||||
|
|
||||||
@@ -164,11 +174,28 @@ export class PluginRegistry {
|
|||||||
|
|
||||||
private parseCommandArgs(command: PluginCommand, rawArgs: string): Record<string, unknown> {
|
private parseCommandArgs(command: PluginCommand, rawArgs: string): Record<string, unknown> {
|
||||||
if (!command.args || command.args.length === 0) return {};
|
if (!command.args || command.args.length === 0) return {};
|
||||||
const parts = rawArgs.trim().split(/\s+/);
|
|
||||||
|
const trimmedArgs = rawArgs.trim();
|
||||||
|
if (!trimmedArgs) return {};
|
||||||
|
|
||||||
|
const argNames = command.args.map(arg => typeof arg === 'string' ? arg : arg.name);
|
||||||
|
|
||||||
|
// If only one arg (that's not roomId), give it all the text
|
||||||
|
const nonRoomIdArgs = argNames.filter(name => name !== 'roomId');
|
||||||
|
if (nonRoomIdArgs.length === 1) {
|
||||||
|
const result: Record<string, unknown> = {};
|
||||||
|
result[nonRoomIdArgs[0]] = trimmedArgs;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, split by whitespace
|
||||||
|
const parts = trimmedArgs.split(/\s+/);
|
||||||
const result: Record<string, unknown> = {};
|
const result: Record<string, unknown> = {};
|
||||||
command.args.forEach((arg, index) => {
|
command.args.forEach((arg, index) => {
|
||||||
const argName = typeof arg === 'string' ? arg : arg.name;
|
const argName = typeof arg === 'string' ? arg : arg.name;
|
||||||
result[argName] = parts[index];
|
if (argName !== 'roomId') {
|
||||||
|
result[argName] = parts[index];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -237,6 +264,134 @@ export class PluginRegistry {
|
|||||||
return this.renderers.get(type)?.renderer;
|
return this.renderers.get(type)?.renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// UI Buttons
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Register a UI button contributed by a plugin. */
|
||||||
|
registerButton(pluginId: string, button: UIButtonDefinition): void {
|
||||||
|
const fullId = `${pluginId}:${button.id}`;
|
||||||
|
if (this.buttons.has(fullId)) {
|
||||||
|
console.warn(`[PluginRegistry] Button ${fullId} already registered`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.buttons.set(fullId, { pluginId, button: { ...button, id: fullId } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Remove a registered button by ID. */
|
||||||
|
unregisterButton(pluginId: string, buttonId: string): void {
|
||||||
|
const fullId = `${pluginId}:${buttonId}`;
|
||||||
|
this.buttons.delete(fullId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all buttons for a specific UI location, sorted by position configuration.
|
||||||
|
* Returns buttons in the order they should be rendered.
|
||||||
|
*/
|
||||||
|
getButtonsForLocation(location: UILocation): UIButtonDefinition[] {
|
||||||
|
const buttons = Array.from(this.buttons.values())
|
||||||
|
.filter(({ button }) => button.location === location)
|
||||||
|
.map(({ button }) => button);
|
||||||
|
|
||||||
|
return this.sortButtonsByPosition(buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort buttons according to their position configuration.
|
||||||
|
* - Buttons with position.before/after are positioned relative to existing buttons
|
||||||
|
* - Buttons in groups are kept together
|
||||||
|
* - Within groups, buttons are sorted by order property
|
||||||
|
*/
|
||||||
|
private sortButtonsByPosition(buttons: UIButtonDefinition[]): UIButtonDefinition[] {
|
||||||
|
if (buttons.length === 0) return [];
|
||||||
|
|
||||||
|
const grouped = new Map<string, UIButtonDefinition[]>();
|
||||||
|
const ungrouped: UIButtonDefinition[] = [];
|
||||||
|
const positionedButtons = new Map<string, UIButtonDefinition>();
|
||||||
|
|
||||||
|
for (const button of buttons) {
|
||||||
|
positionedButtons.set(button.id, button);
|
||||||
|
|
||||||
|
if (button.position?.group) {
|
||||||
|
if (!grouped.has(button.position.group)) {
|
||||||
|
grouped.set(button.position.group, []);
|
||||||
|
}
|
||||||
|
grouped.get(button.position.group)!.push(button);
|
||||||
|
} else {
|
||||||
|
ungrouped.push(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [_group, groupButtons] of grouped) {
|
||||||
|
groupButtons.sort((a, b) => {
|
||||||
|
const orderA = a.position?.order ?? 0;
|
||||||
|
const orderB = b.position?.order ?? 0;
|
||||||
|
return orderA - orderB;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: UIButtonDefinition[] = [];
|
||||||
|
const inserted = new Set<string>();
|
||||||
|
|
||||||
|
const insertButton = (button: UIButtonDefinition) => {
|
||||||
|
if (inserted.has(button.id)) return;
|
||||||
|
|
||||||
|
const group = button.position?.group ? grouped.get(button.position.group) : [button];
|
||||||
|
if (!group) return;
|
||||||
|
|
||||||
|
for (const btn of group) {
|
||||||
|
if (!inserted.has(btn.id)) {
|
||||||
|
result.push(btn);
|
||||||
|
inserted.add(btn.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const button of [...ungrouped, ...Array.from(grouped.values()).flat()]) {
|
||||||
|
if (inserted.has(button.id)) continue;
|
||||||
|
|
||||||
|
if (button.position?.before) {
|
||||||
|
const beforeButton = positionedButtons.get(button.position.before);
|
||||||
|
if (beforeButton) {
|
||||||
|
const idx = result.findIndex(b => b.id === beforeButton.id);
|
||||||
|
if (idx !== -1) {
|
||||||
|
const group = button.position.group ? grouped.get(button.position.group) : [button];
|
||||||
|
if (group) {
|
||||||
|
for (let i = group.length - 1; i >= 0; i--) {
|
||||||
|
if (!inserted.has(group[i].id)) {
|
||||||
|
result.splice(idx, 0, group[i]);
|
||||||
|
inserted.add(group[i].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button.position?.after) {
|
||||||
|
const afterButton = positionedButtons.get(button.position.after);
|
||||||
|
if (afterButton) {
|
||||||
|
const idx = result.findIndex(b => b.id === afterButton.id);
|
||||||
|
if (idx !== -1) {
|
||||||
|
insertButton(button);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insertButton(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const button of buttons) {
|
||||||
|
if (!inserted.has(button.id)) {
|
||||||
|
result.push(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Settings
|
// Settings
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ export type {
|
|||||||
PluginContext,
|
PluginContext,
|
||||||
PluginSettingsSection,
|
PluginSettingsSection,
|
||||||
UILocation,
|
UILocation,
|
||||||
|
UIButtonDefinition,
|
||||||
|
UIButtonPosition,
|
||||||
Plugin,
|
Plugin,
|
||||||
PluginLogEntry,
|
PluginLogEntry,
|
||||||
} from './PluginInterfaces.js';
|
} from './PluginInterfaces.js';
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ export type {
|
|||||||
PluginContext,
|
PluginContext,
|
||||||
PluginSettingsSection,
|
PluginSettingsSection,
|
||||||
UILocation,
|
UILocation,
|
||||||
|
UIButtonDefinition,
|
||||||
|
UIButtonPosition,
|
||||||
Plugin,
|
Plugin,
|
||||||
PluginLogEntry,
|
PluginLogEntry,
|
||||||
} from './PluginInterfaces.js';
|
} from './PluginInterfaces.js';
|
||||||
|
|||||||
Reference in New Issue
Block a user