Something went wrong. Try again.
Mirror of https://github.com/improsocial/impro-sample-plugin
Something went wrong. Try again.
impro-sample-plugin main.js
17 kB · 646 lines
JavaScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647var __defProp = Object.defineProperty;var __getOwnPropDesc = Object.getOwnPropertyDescriptor;var __getOwnPropNames = Object.getOwnPropertyNames;var __hasOwnProp = Object.prototype.hasOwnProperty;var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true });};var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to;};var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/main.jsvar main_exports = {};__export(main_exports, { default: () => ImproSamplePlugin});module.exports = __toCommonJS(main_exports);
// ../impro/impro-plugin/main.jsvar SimpleUUID = class { constructor() { this._id = 0; } create() { return this._id++; }};var uuid = new SimpleUUID();var callHandlers = /* @__PURE__ */ new Map();var pendingHostCalls = /* @__PURE__ */ new Map();function hostCall(method, ...args) { const hostCallId = uuid.create(); return new Promise((resolve, reject) => { pendingHostCalls.set(hostCallId, { resolve, reject }); self.postMessage({ type: "hostCall", method, hostCallId, args }); });}var eventListeners = /* @__PURE__ */ new Map();function addEventListener(event, listener) { let listeners = eventListeners.get(event); if (!listeners) { listeners = /* @__PURE__ */ new Set(); eventListeners.set(event, listeners); const handlerId = uuid.create(); callHandlers.set(handlerId, async (...args) => { const menu = new Menu(); for (const eventListener of listeners) { try { await eventListener(menu, ...args); } catch (error) { console.error(`"${event}" listener threw:`, error); } } return menu._serialize(); }); self.postMessage({ type: "register", target: "eventListener", event, handlerId }); } listeners.add(listener);}var MenuItem = class { constructor() { this.title = ""; this.icon = null; this._callback = () => { }; } setTitle(title) { this.title = title; return this; } setIcon(icon) { this.icon = icon; return this; } onClick(callback) { this._callback = callback; return this; }};var Menu = class { constructor() { this.items = []; } addItem(builder) { const item = new MenuItem(); builder(item); this.items.push(item); return this; } _serialize() { return this.items.map((item) => { const handlerId = uuid.create(); callHandlers.set(handlerId, item._callback); return { title: item.title, icon: item.icon, handlerId }; }); }};var PluginData = class { getPost(uri) { return hostCall("getPost", { uri }); } getProfile(did) { return hostCall("getProfile", { did }); }};var App = class { constructor() { this.currentUser = null; this.data = new PluginData(); } on(event, listener) { addEventListener(event, listener); } refreshFeedFilters(feedURI = null) { return hostCall("refreshFeedFilters", feedURI); }};var registered = false;var Plugin = class { constructor() { this.app = new App(); } addSidebarItem(icon, title, callback = () => { }) { const handlerId = uuid.create(); callHandlers.set(handlerId, callback); self.postMessage({ type: "register", target: "sidebarItem", icon, title, handlerId }); } async loadData() { return hostCall("loadData"); } async saveData(data) { await hostCall("saveData", { data }); } addSettingTab(tab) { tab.plugin = this; const displayHandlerId = uuid.create(); callHandlers.set(displayHandlerId, () => { tab.containerEl = new VirtualEl("div"); tab.display(); return tab.containerEl._serialize(); }); self.postMessage({ type: "register", target: "settingTab", name: tab.name ?? null, displayHandlerId }); this._settingTab = tab; } addFeedFilter(callback = () => { }) { const handlerId = uuid.create(); callHandlers.set(handlerId, callback); self.postMessage({ type: "register", target: "feedFilter", handlerId }); } registerSlot(name, callback = () => null) { const handlerId = uuid.create(); callHandlers.set(handlerId, async (context) => { const result = await callback(context); if (result == null) return null; if (!(result instanceof VirtualEl)) { const description = result?.constructor?.name ?? typeof result; throw new Error( `Slot "${name}" must return a VirtualEl (or null), got ${description}` ); } return result._serialize(); }); self.postMessage({ type: "register", target: "slot", name, handlerId }); } onload() { } onunload() { } static register() { if (registered) return; registered = true; const instance = new this(); hostCall("getCurrentUser").then((user) => { instance.app.currentUser = user; return instance.onload(); }).then( () => self.postMessage({ type: "ready" }), (error) => self.postMessage({ type: "ready", error: error?.message ?? String(error) }) ); }};var openModals = /* @__PURE__ */ new Map();var Modal = class { constructor() { this._modalId = uuid.create(); this.contentEl = new VirtualEl("div"); this.titleEl = new VirtualEl("h2"); } open() { if (openModals.has(this._modalId)) return; openModals.set(this._modalId, this); this.onOpen(); self.postMessage({ type: "hostCall", method: "openModal", args: [ { modalId: this._modalId, title: this.titleEl._serialize(), content: this.contentEl._serialize() } ] }); } close() { if (!openModals.has(this._modalId)) return; openModals.delete(this._modalId); self.postMessage({ type: "hostCall", method: "closeModal", args: [{ modalId: this._modalId }] }); this.onClose(); } onOpen() { } onClose() { }};var PluginSettingTab = class { constructor() { this.containerEl = new VirtualEl("div"); this.name = null; } setName(name) { this.name = name; return this; } display() { } refresh({ reset = false } = {}) { return hostCall("refreshSettingTab", { reset }); }};var Setting = class { constructor(containerEl) { this.settingEl = containerEl.createDiv({ cls: "setting-item" }); this.infoEl = this.settingEl.createDiv({ cls: "setting-item-info" }); this.nameEl = this.infoEl.createEl("h2", { cls: "setting-item-name" }); this.descEl = this.infoEl.createEl("p", { cls: "setting-item-desc" }); this.controlEl = this.settingEl.createDiv({ cls: "setting-item-control" }); } setName(text) { this.nameEl.setText(text); return this; } setDesc(text) { this.descEl.setText(text); return this; } addText(callback) { const component = new TextComponent(this.controlEl); callback(component); return this; } addToggle(callback) { const component = new ToggleComponent(this.controlEl); callback(component); return this; } addDropdown(callback) { const component = new DropdownComponent(this.controlEl); callback(component); return this; } addButton(callback) { const component = new ButtonComponent(this.controlEl); callback(component); return this; }};var TextComponent = class { constructor(containerEl) { this.el = containerEl.createEl("input", { attr: { type: "text" }, cls: "setting-item-text-input" }); } setValue(value) { this.el.setAttr("value", value == null ? "" : String(value)); return this; } setPlaceholder(value) { this.el.setAttr("placeholder", value); return this; } onChange(callback) { this.el.onChange((event) => callback(event.target.value)); return this; }};var ToggleComponent = class { constructor(containerEl) { this.el = containerEl.createEl("toggle-switch", { cls: "setting-item-toggle" }); } setValue(value) { if (value) this.el.setAttr("checked", ""); else delete this.el.attrs.checked; return this; } onChange(callback) { this.el.onChange((event) => callback(event.target.checked)); return this; }};var DropdownComponent = class { constructor(containerEl) { this.el = containerEl.createEl("select", { cls: "setting-item-dropdown" }); } addOption(value, label) { this.el.createEl("option", { text: label, attr: { value } }); return this; } addOptions(map) { for (const [value, label] of Object.entries(map)) { this.addOption(value, label); } return this; } setValue(value) { for (const child of this.el.children) { if (child.attrs?.value === value) { child.attrs.selected = ""; } else if (child.attrs) { delete child.attrs.selected; } } return this; } onChange(callback) { this.el.onChange((event) => callback(event.target.value)); return this; }};var ButtonComponent = class { constructor(containerEl) { this.el = containerEl.createEl("button", { cls: "rounded-button" }); } setButtonText(text) { this.el.setText(text); return this; } setCta() { this.el.addClass("rounded-button-primary"); return this; } onClick(callback) { this.el.onClick(callback); return this; }};var IconComponent = class { constructor(containerEl) { this.el = containerEl.createEl("plugin-icon"); } setIcon(name) { this.el.setAttr("icon", name); return this; }};var ProfilesListComponent = class { constructor(containerEl) { this.el = containerEl.createEl("plugin-profiles-list"); } setDids(dids) { const value = Array.isArray(dids) ? dids.join(",") : String(dids ?? ""); this.el.setAttr("dids", value); return this; } setEmptyMessage(message) { this.el.setAttr("empty-message", message); return this; }};var PostsFeedComponent = class { constructor(containerEl) { this.el = containerEl.createEl("plugin-posts-feed"); } setUris(uris) { const value = Array.isArray(uris) ? uris.join(",") : String(uris ?? ""); this.el.setAttr("uris", value); return this; } setEmptyMessage(message) { this.el.setAttr("empty-message", message); return this; }};var VirtualEl = class _VirtualEl { constructor(tag) { this.tag = tag; this.attrs = {}; this.text = null; this.children = []; this.events = {}; } onClick(fn) { const handlerId = uuid.create(); callHandlers.set(handlerId, fn); this.events.click = handlerId; return this; } onChange(fn) { const handlerId = uuid.create(); callHandlers.set(handlerId, fn); this.events.change = handlerId; return this; } onInput(fn) { const handlerId = uuid.create(); callHandlers.set(handlerId, fn); this.events.input = handlerId; return this; } setText(text) { this.text = text; this.children = []; return this; } empty() { this.text = null; this.children = []; return this; } addClass(cls) { this.attrs.class = this.attrs.class ? `${this.attrs.class} ${cls}` : cls; return this; } setAttr(name, value) { this.attrs[name] = value === void 0 ? "" : value; return this; } createEl(tag, options = {}, callback) { const child = new _VirtualEl(tag); if (options.text != null) child.text = options.text; if (options.cls) { child.attrs.class = Array.isArray(options.cls) ? options.cls.join(" ") : options.cls; } if (options.attr) Object.assign(child.attrs, options.attr); this.children.push(child); if (typeof callback === "function") callback(child); return child; } createDiv(options = {}, callback) { return this.createEl("div", options, callback); } createSpan(options = {}, callback) { return this.createEl("span", options, callback); } createProfilesList(callback) { const component = new ProfilesListComponent(this); if (typeof callback === "function") callback(component); return component; } createPostsFeed(callback) { const component = new PostsFeedComponent(this); if (typeof callback === "function") callback(component); return component; } createIcon(callback) { const component = new IconComponent(this); if (typeof callback === "function") callback(component); return component; } _serialize() { return { tag: this.tag, attrs: this.attrs, text: this.text, children: this.children.map((child) => child._serialize()), events: this.events }; }};self.onmessage = async (event) => { const message = event.data; if (!message || typeof message !== "object") return; if (message.type === "call") { const fn = callHandlers.get(message.handlerId); if (!fn) { self.postMessage({ type: "result", callId: message.callId, error: `unknown handler ${message.handlerId}` }); return; } try { const value = await fn(...message.args); self.postMessage({ type: "result", callId: message.callId, value }); } catch (error) { self.postMessage({ type: "result", callId: message.callId, error: error.message ?? String(error) }); } return; } if (message.type === "hostResult") { const pending = pendingHostCalls.get(message.hostCallId); if (!pending) return; pendingHostCalls.delete(message.hostCallId); if (message.error) pending.reject(new Error(message.error)); else pending.resolve(message.value); return; } if (message.type === "event") { switch (message.event) { case "modalDismissed": { const modal = openModals.get(message.data.modalId); if (modal) { openModals.delete(message.data.modalId); modal.onClose(); } return; } } return; }};
// src/main.jsvar DEFAULT_SETTINGS = { greeting: "Hello", loud: false, buttonStyle: "primary"};var SampleModal = class extends Modal { constructor(settings) { super(); this.settings = settings; } onOpen() { this.titleEl.setText("Sample modal"); const message = this.settings.loud ? `${this.settings.greeting}!!!` : `${this.settings.greeting}.`; this.contentEl.createEl("p", { text: message, cls: "modal-dialog-message" }); const buttons = this.contentEl.createDiv({ cls: "modal-dialog-buttons" }); buttons.createEl("button", { text: "OK", cls: `modal-dialog-button ${this.settings.buttonStyle}-button` }).onClick(() => this.close()); } onClose() { this.titleEl.empty(); this.contentEl.empty(); }};var SampleSettingTab = class extends PluginSettingTab { constructor() { super(); this.setName("Impro Sample Plugin"); } display() { new Setting(this.containerEl).setName("Greeting").setDesc("Text shown in the sample modal").addText( (text) => text.setPlaceholder("Hello").setValue(this.plugin.settings.greeting).onChange(async (value) => { this.plugin.settings.greeting = value; await this.plugin.saveData(this.plugin.settings); }) ); new Setting(this.containerEl).setName("Loud mode").setDesc("Adds exclamation marks to the greeting").addToggle( (toggle) => toggle.setValue(this.plugin.settings.loud).onChange(async (value) => { this.plugin.settings.loud = value; await this.plugin.saveData(this.plugin.settings); }) ); new Setting(this.containerEl).setName("Button style").setDesc("Modal button style").addDropdown( (dropdown) => dropdown.addOptions({ primary: "Primary", danger: "Danger" }).setValue(this.plugin.settings.buttonStyle).onChange(async (value) => { this.plugin.settings.buttonStyle = value; await this.plugin.saveData(this.plugin.settings); }) ); new Setting(this.containerEl).setName("Reset settings").setDesc("Reset all settings to defaults").addButton( (button) => button.setButtonText("Reset").onClick(async () => { this.plugin.settings = { ...DEFAULT_SETTINGS }; await this.plugin.saveData(this.plugin.settings); this.refresh({ reset: true }); }) ); }};var ImproSamplePlugin = class extends Plugin { async onload() { const saved = await this.loadData(); this.settings = { ...DEFAULT_SETTINGS, ...saved ?? {} }; this.addSettingTab(new SampleSettingTab()); this.addSidebarItem("lightning-bolt-line", "Open modal", () => { new SampleModal(this.settings).open(); }); this.app.on("post-context-menu", (menu) => { menu.addItem( (item) => item.setTitle("Open modal").setIcon("lightning-bolt").onClick(() => { new SampleModal(this.settings).open(); }) ); }); }};