diff --git a/src/mechanics/mechanics-blocks.ts b/src/mechanics/mechanics-blocks.ts
index 9178a996..5a1d6258 100644
--- a/src/mechanics/mechanics-blocks.ts
+++ b/src/mechanics/mechanics-blocks.ts
@@ -1,5 +1,11 @@
-import { Node as KdlNodeBare, Document as KdlDocument, parse } from "kdljs";
-import { ButtonComponent, MarkdownPostProcessorContext } from "obsidian";
+import { Node as KdlNodeBare, parse, format } from "kdljs";
+import {
+ ButtonComponent,
+ MarkdownPostProcessorContext,
+ MarkdownPreviewView,
+ MarkdownView,
+ Menu,
+} from "obsidian";
import { render, html, TemplateResult } from "lit-html";
import { styleMap } from "lit-html/directives/style-map.js";
import { ref } from "lit-html/directives/ref.js";
@@ -8,6 +14,7 @@ import { ProgressTrack } from "tracks/progress";
import IronVaultPlugin from "../index";
import { md } from "utils/ui/directives";
import { map } from "lit-html/directives/map.js";
+import { node } from "utils/kdl";
interface KdlNode extends KdlNodeBare {
parent?: KdlNode;
@@ -22,12 +29,7 @@ function makeHandler(plugin: IronVaultPlugin) {
// We can't render blocks until datastore is ready.
await plugin.datastore.waitForReady;
if (!el.mechanicsRenderer) {
- el.mechanicsRenderer = new MechanicsRenderer(
- el,
- source,
- plugin,
- ctx.sourcePath,
- );
+ el.mechanicsRenderer = new MechanicsRenderer(el, source, plugin, ctx);
}
el.mechanicsRenderer.render();
};
@@ -46,27 +48,26 @@ interface MechanicsContainerEl extends HTMLElement {
}
export class MechanicsRenderer {
- plugin: IronVaultPlugin;
sourcePath: string;
lastRoll: KdlNode | undefined;
moveEl: HTMLElement | undefined;
- doc?: KdlDocument;
+ doc?: KdlNode;
details: string[] = [];
- contentEl: HTMLElement;
- source: string;
mechNode?: HTMLElement;
hideMechanics = false;
constructor(
- contentEl: HTMLElement,
- source: string,
- plugin: IronVaultPlugin,
- sourcePath: string,
+ public contentEl: HTMLElement,
+ public source: string,
+ public plugin: IronVaultPlugin,
+ public ctx: MarkdownPostProcessorContext,
) {
- this.contentEl = contentEl;
- this.source = source;
- this.plugin = plugin;
- this.sourcePath = sourcePath;
+ this.sourcePath = ctx.sourcePath;
+ const res = parse(this.source);
+ if (res.output) {
+ this.doc = node("doc", { children: res.output });
+ this.fillParents(this.doc.children, this.doc);
+ }
plugin.register(
plugin.settings.on("change", ({ key, oldValue, newValue }) => {
if (
@@ -93,8 +94,7 @@ export class MechanicsRenderer {
render(html``, this.contentEl);
return;
}
- const res = parse(this.source);
- if (!res.output) {
+ if (!this.doc) {
// TODO: give line/column information for errors.
render(
html`
@@ -105,16 +105,14 @@ See https://kdl.dev for syntax.
(this.mechNode = el as HTMLElement | undefined)}
class="iron-vault-mechanics"
style=${styleMap({
"--vs1-color": this.plugin.settings.challengeDie1Color,
"--vs2-color": this.plugin.settings.challengeDie2Color,
})}
+ @contextmenu=${this.makeMenuHandler(this.doc)}
${ref((el?: Element) => {
if (
!el ||
@@ -138,12 +136,91 @@ See https://kdl.dev for syntax.
- ${this.hideMechanics ? null : this.renderChildren(this.doc)}
+ ${this.hideMechanics ? null : this.renderChildren(this.doc.children)}
`,
this.contentEl,
);
}
+ makeMenuHandler(node: KdlNode) {
+ return (ev: MouseEvent) => {
+ const view = this.plugin.app.workspace.getActiveViewOfType(MarkdownView);
+ if (!view || view.currentMode instanceof MarkdownPreviewView) {
+ return;
+ }
+
+ ev.preventDefault();
+ ev.stopPropagation();
+
+ const menu = new Menu();
+
+ menu.addItem((item) => {
+ item
+ .setTitle(
+ `Delete ${node.name === "-" ? "comment" : node === this.doc ? "mechanics block" : node.name}`,
+ )
+ .setIcon("trash")
+ .onClick(() => {
+ if (node.parent) {
+ const idx = node.parent.children.indexOf(node);
+ if (idx >= 0) {
+ node.parent.children.splice(idx, 1);
+ this.updateBlock();
+ }
+ } else if (node === this.doc) {
+ // We probably don't need this if, but just to make sure.
+ node.children = [];
+ this.updateBlock();
+ }
+ });
+ });
+
+ menu.showAtMouseEvent(ev);
+ };
+ }
+
+ updateBlock() {
+ const view = this.plugin.app.workspace.getActiveViewOfType(MarkdownView);
+ const editor = this.plugin.app.workspace.activeEditor?.editor;
+ const sectionInfo = this.ctx.getSectionInfo(this.contentEl as HTMLElement);
+ if (
+ !editor ||
+ !sectionInfo ||
+ !this.doc ||
+ !view ||
+ view.currentMode instanceof MarkdownPreviewView
+ ) {
+ return;
+ }
+ const editorRange = {
+ from: {
+ ch: 0,
+ line: sectionInfo.lineStart,
+ },
+ to: {
+ ch: 0,
+ line: sectionInfo.lineEnd,
+ },
+ };
+ const to = {
+ line: editorRange.to.line,
+ ch:
+ editor.getLine(editorRange.to.line).length +
+ (this.doc.children.length ? 0 : 1),
+ };
+ editor.replaceRange(
+ this.doc.children.length
+ ? `\`\`\`iron-vault-mechanics\n${format(this.doc.children)}\`\`\``
+ : "",
+ editorRange.from,
+ to,
+ );
+ editor.focus();
+ const moveTo = editorRange.from.line - (this.doc.children.length ? 1 : 0);
+ // NB(@zkat): This prevents the editor jumping around.
+ editor.setCursor({ ch: 0, line: moveTo >= 0 ? moveTo : 0 });
+ }
+
renderChildren(nodes: KdlNode[]): TemplateResult {
return html`${map(
nodes.reduce((acc, node) => {
@@ -179,7 +256,6 @@ See https://kdl.dev for syntax. (this.moveEl = el as HTMLElement | undefined))}
>
${md(this.plugin, moveName)}
@@ -268,7 +343,10 @@ See https://kdl.dev for syntax.
+ return html``;
}
@@ -286,7 +364,7 @@ See https://kdl.dev for syntax.
- ${this.renderDlist("oracle", data)}
+ return html`
+ ${this.renderDlist("oracle", data, node)}
${node.children.length
? html`
${this.renderChildren(node.children)}
`
: null}
@@ -486,7 +595,10 @@ ${result.error.toString()}
+ return html`
${md(this.plugin, name)}
${node.children.length
? html`${this.renderChildren(node.children)}
`
@@ -522,7 +634,7 @@ ${result.error.toString()}
+ return html`
${this.renderChildren(node.children)}
`;
}
- renderUnknown(name: string) {
- return html`Unknown node: "${name}"
`;
+ renderUnknown(node: KdlNode) {
+ return html`
+ Unknown node: "${node.name}"
+
`;
}
- renderDlist(cls: string, data: DataList): TemplateResult {
- return html`
+ renderDlist(cls: string, data: DataList, node: KdlNode): TemplateResult {
+ return html`
${map(
Object.entries(data),
([key, { cls, value, dataProp, md: renderMd }]) => {