plugin infrastruktur
This commit is contained in:
1 parent
1545b90f39
commit
3f4ba3c0f4
35 files changed
+1303
-195
No files matched your search
@@ -0,0 +1,135 @@
|
||||
// ── hyprcrd Plugin API ──
|
||||
//
|
||||
// Loaded synchronously BEFORE any module script. Plugins and core both
|
||||
// register against window.hyprcrd. Plugins MUST NOT import from core
|
||||
// modules — use this global API instead so they remain decoupled.
|
||||
//
|
||||
// Available namespaces:
|
||||
// hyprcrd.hooks — generic event bus (on/emit)
|
||||
// hyprcrd.editor — toolbar command registry
|
||||
// hyprcrd.markdown — fenced block + post-processing registry
|
||||
// hyprcrd.util — DOM helpers exposed for plugin use
|
||||
//
|
||||
// Plugins are loaded as <script type="module"> tags after this script
|
||||
// but before app.js. They self-register on module evaluation; the core
|
||||
// then queries the registries when needed.
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
// ── Generic event bus ───────────────────────────────────────────
|
||||
const hookListeners = Object.create(null);
|
||||
const hooks = {
|
||||
on(name, fn) {
|
||||
if (typeof fn !== 'function') return;
|
||||
(hookListeners[name] ||= []).push(fn);
|
||||
},
|
||||
off(name, fn) {
|
||||
const arr = hookListeners[name];
|
||||
if (!arr) return;
|
||||
const idx = arr.indexOf(fn);
|
||||
if (idx >= 0) arr.splice(idx, 1);
|
||||
},
|
||||
emit(name, data) {
|
||||
const arr = hookListeners[name];
|
||||
if (!arr) return data;
|
||||
for (const fn of arr.slice()) {
|
||||
try {
|
||||
const r = fn(data);
|
||||
if (r !== undefined) data = r;
|
||||
} catch (err) {
|
||||
console.error(`[hyprcrd hook ${name}]`, err);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
},
|
||||
};
|
||||
|
||||
// ── Editor command registry ─────────────────────────────────────
|
||||
// Toolbar buttons with `data-md="<name>"` dispatch to handlers
|
||||
// registered here. Plugins inject buttons + register handlers.
|
||||
const editorCommands = Object.create(null);
|
||||
const editor = {
|
||||
registerCommand(name, fn) {
|
||||
if (typeof fn !== 'function') return;
|
||||
editorCommands[name] = fn;
|
||||
},
|
||||
unregisterCommand(name) {
|
||||
delete editorCommands[name];
|
||||
},
|
||||
getCommand(name) {
|
||||
return editorCommands[name];
|
||||
},
|
||||
// Set by editor.js after init — gives plugins access to
|
||||
// insertMd(before, after, placeholder) and the textarea body
|
||||
// without forcing an import dependency.
|
||||
api: null,
|
||||
};
|
||||
|
||||
// ── Markdown fenced block registry ──────────────────────────────
|
||||
// Each entry: { lang: string, handler: (content) => string }
|
||||
// The handler receives the raw text inside ```<lang> ... ``` and
|
||||
// must return safe HTML (it will pass through DOMPurify after).
|
||||
const fencedBlocks = [];
|
||||
// Pre/post processors run on the full source text, before/after
|
||||
// marked.parse(). Useful for non-fenced syntax extensions.
|
||||
const preProcessors = [];
|
||||
const postProcessors = [];
|
||||
const markdown = {
|
||||
registerFencedBlock(lang, handler) {
|
||||
if (typeof handler !== 'function') return;
|
||||
fencedBlocks.push({ lang, handler });
|
||||
},
|
||||
getFencedBlocks() {
|
||||
return fencedBlocks.slice();
|
||||
},
|
||||
registerPreProcessor(fn) {
|
||||
if (typeof fn === 'function') preProcessors.push(fn);
|
||||
},
|
||||
getPreProcessors() {
|
||||
return preProcessors.slice();
|
||||
},
|
||||
registerPostProcessor(fn) {
|
||||
if (typeof fn === 'function') postProcessors.push(fn);
|
||||
},
|
||||
getPostProcessors() {
|
||||
return postProcessors.slice();
|
||||
},
|
||||
};
|
||||
|
||||
// ── Util helpers (mirror of internal util.js) ───────────────────
|
||||
const util = {
|
||||
$(idOrSel) {
|
||||
if (typeof idOrSel !== 'string') return null;
|
||||
if (idOrSel.startsWith('#') || idOrSel.includes(' ') || idOrSel.includes('.') || idOrSel.includes('[')) {
|
||||
return document.querySelector(idOrSel);
|
||||
}
|
||||
return document.getElementById(idOrSel);
|
||||
},
|
||||
$$(sel) {
|
||||
return Array.from(document.querySelectorAll(sel));
|
||||
},
|
||||
esc(s) {
|
||||
return String(s ?? '').replace(/[&<>"']/g, c => ({
|
||||
'&': '&', '<': '<', '>': '>', '"': '"', "'": '''
|
||||
}[c]));
|
||||
},
|
||||
};
|
||||
|
||||
// ── Plugin metadata registry ────────────────────────────────────
|
||||
// Plugins can register themselves here for introspection / debug.
|
||||
const plugins = [];
|
||||
const registerPlugin = (meta) => {
|
||||
if (meta && meta.name) plugins.push(meta);
|
||||
};
|
||||
|
||||
window.hyprcrd = {
|
||||
hooks,
|
||||
editor,
|
||||
markdown,
|
||||
util,
|
||||
plugins,
|
||||
registerPlugin,
|
||||
version: '1.0',
|
||||
};
|
||||
})();
|
||||
Reference in new issue
Block a user