From d2fa2f35308ff905942302a0233bb4577592611a Mon Sep 17 00:00:00 2001 From: Mike Bloy Date: Wed, 23 Sep 2026 22:48:41 -0500 Subject: [PATCH] inline styles and actor embeds --- lang/en.json | 7 +++ scripts/mb-cypher2.js | 82 +++++++++++++++++++++++++++++++ scripts/module.js | 19 +++++++ styles/mb-cypher2.css | 29 +++++++++++ templates/embeds/actor-inline.hbs | 32 ++++++++++++ templates/embeds/actor.hbs | 0 6 files changed, 169 insertions(+) create mode 100644 scripts/module.js create mode 100644 templates/embeds/actor-inline.hbs create mode 100644 templates/embeds/actor.hbs diff --git a/lang/en.json b/lang/en.json index e69de29..bb81437 100644 --- a/lang/en.json +++ b/lang/en.json @@ -0,0 +1,7 @@ +{ + "mb-cypher2": { + "embed": { + "noPermission": "Unauthorized Actor Embed" + } + } +} diff --git a/scripts/mb-cypher2.js b/scripts/mb-cypher2.js index e69de29..3992715 100644 --- a/scripts/mb-cypher2.js +++ b/scripts/mb-cypher2.js @@ -0,0 +1,82 @@ +import { + preloadTemplatePaths, + normalizeTemplatePaths, + MODULE_ID, + log, +} from "./module.js"; + +const TEMPLATE_FILES = ["actor.hbs", "actor-inline.hbs"]; +const TEMPLATE_BASE = "embeds"; +const TEMPLATES = normalizeTemplatePaths(TEMPLATE_FILES, TEMPLATE_BASE); + +async function buildActorEmbedHTML(doc, content, config, options) { + // Types: community, pc, marker, npc, vehicle + // LIMITED access can see Name and Description + const noPermission = !doc.testUserPermission( + game.user, + CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED, + ); + if (noPermission) { + const broken = document.createElement("span"); + broken.classList.add("broken", "content-embed"); + const i = document.createElement("i"); + i.classList.add("fa-solid", "fa-circle-exclamation"); + i.inert = true; + broken.append( + i, + _loc(`${MODULE_ID}.embed.noPermission`, { uuid: doc.uuid }), + ); + return broken; + } + let icon = ""; + switch (doc.type) { + case "pc": + icon = "fa-solid fa-user"; + break; + case "npc": + icon = "fa-solid fa-user-secret"; + break; + case "community": + icon = "fa-solid fa-tree-city"; + break; + case "marker": + icon = "fa-solid fa-thumbtack"; + break; + case "vehicle": + icon = "fa-solid fa-car-side"; + break; + default: + icon = "fa-solid fa-person"; + } + const context = { + config: { ...config }, + actor: doc, + basic: doc.system.basic, + pools: doc.system.pools, + options, + viewFullDocument: doc.testUserPermission( + game.user, + CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER, + ), + icon, + user: game.user, + }; + const template = config.inline + ? TEMPLATES["actor-inline.hbs"] + : TEMPLATES["actor.hbs"]; + const el = document.createElement(config.inline ? "span" : "div"); + el.classList.add(`${MODULE_ID}-embed`, "content-embed"); + let innerHTML = await renderTemplate(template, context); + el.innerHTML = innerHTML; + return el; +} + +function initializeEmbeds() { + CONFIG.Actor.embedHandlers.push(buildActorEmbedHTML); +} + +Hooks.once("init", async () => { + log(`Initializing ${MODULE_ID}`); + initializeEmbeds(); + preloadTemplatePaths(TEMPLATES); +}); diff --git a/scripts/module.js b/scripts/module.js new file mode 100644 index 0000000..60cec2c --- /dev/null +++ b/scripts/module.js @@ -0,0 +1,19 @@ +export const MODULE_ID = "mb-cypher2"; + +export function log(...args) { + console.log(`${MODULE_ID} |`, ...args); +} + +export function normalizeTemplatePaths(paths, baseDir = "") { + const templates = {}; + log("finding templates", paths, baseDir); + const basePath = `modules/${MODULE_ID}/templates${baseDir ? "/" + baseDir : ""}`; + for (const file of paths) { + templates[file] = `${basePath}/${file}`; + } + return templates; +} + +export async function preloadTemplatePaths(normalizedPaths) { + return loadTemplates(Object.values(normalizedPaths)); +} diff --git a/styles/mb-cypher2.css b/styles/mb-cypher2.css index e69de29..a354c1f 100644 --- a/styles/mb-cypher2.css +++ b/styles/mb-cypher2.css @@ -0,0 +1,29 @@ +:root { + --mbcypher2-journal-embed-background: #ccccaa; + --mbcypher2-journal-embed-border-color: #aaaa88; + --mbcypher2-journal-embed-font-color: #000000; +} + +body.theme-dark { + --mbcypher2-journal-embed-background: #333300; + --mbcypher2-journal-embed-border-color: #666633; + --mbcypher2-journal-embed-font-color: #eeeecc; +} + +.mb-cypher2-embed.content-embed { + background: var(--mbcypher2-journal-embed-background); + border: 1px solid var(--mbcypher2-journal-embed-border-color); + border-radius: 2px; +} + +span.mb-cypher2-embed.content-embed { + margin: -1px 0; + padding: 1px 4px 0; + white-space: nowrap; + word-break: break-all; +} + +figure.content-embed figcaption cite { + margin-left: 0.5em; +} +} diff --git a/templates/embeds/actor-inline.hbs b/templates/embeds/actor-inline.hbs new file mode 100644 index 0000000..57f9ef8 --- /dev/null +++ b/templates/embeds/actor-inline.hbs @@ -0,0 +1,32 @@ + +{{actor.name}} +{{#if (eq actor.type "pc") }} + {{ localize "CYPHERSYSTEM.IsA" }} {{ basic.descriptor }} {{ basic.type }} + {{ localize "CYPHERSYSTEM.Who" }} {{ basic.focus }} {{ basic.additionalSentence }} +{{/if}} +{{#if viewFullDocument}} + {{#if basic.rank}} + {{ localize "CYPHERSYSTEM.Rank" }} {{ basic.rank }} + {{/if}} + {{#if basic.level}} + {{ localize "CYPHERSYSTEM.Level" }} {{ basic.level }} + {{/if}} + {{#if basic.tier}} + {{ localize "CYPHERSYSTEM.Tier" }} {{ basic.tier }} + {{/if}} + {{#if pools.health}} + {{ localize "CYPHERSYSTEM.Health" }} {{ pools.health.value }}/{{ pools.health.max}} + {{/if}} + {{#if pools.infrastructure}} + {{ localize "CYPHERSYSTEM.Infrastructure" }} {{ pools.infrastructure.value }}/{{ pools.infrastructure.max}} + {{/if}} + {{#if pools.might}} + {{ localize "CYPHERSYSTEM.Might" }} {{ pools.might.value }}/{{ pools.might.max}} + {{/if}} + {{#if pools.speed}} + {{ localize "CYPHERSYSTEM.Speed" }} {{ pools.speed.value }}/{{ pools.speed.max}} + {{/if}} + {{#if pools.intellect}} + {{ localize "CYPHERSYSTEM.Intellect" }} {{ pools.intellect.value }}/{{ pools.intellect.max}} + {{/if}} +{{/if}} diff --git a/templates/embeds/actor.hbs b/templates/embeds/actor.hbs new file mode 100644 index 0000000..e69de29