mirror of
https://github.com/andre482/O-tie.git
synced 2026-07-22 07:44:11 +00:00
Release 1.0.13: Mobile inspector dismiss and capitalised action labels.
Tap empty canvas on touch devices to close the inspector, and align inspector + buttons with toolbar capitalisation. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
b57d085945
commit
7c5f70a1f9
5 changed files with 69 additions and 29 deletions
|
|
@ -2,6 +2,15 @@
|
|||
|
||||
All notable changes to O-Tie are documented here.
|
||||
|
||||
## [1.0.13] — 2026-06-28
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Mobile inspector dismiss** — Tapping empty canvas on phone or tablet now closes the inspector bar, matching desktop click-away behaviour.
|
||||
- **Inspector action labels** — + Stack row, + Safeguard, + Barrier, and related buttons use capitalised labels consistent with the toolbar.
|
||||
|
||||
---
|
||||
|
||||
## [1.0.12] — 2026-06-27
|
||||
|
||||
### Fixed
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "o-tie",
|
||||
"name": "O-Tie",
|
||||
"version": "1.0.12",
|
||||
"version": "1.0.13",
|
||||
"minAppVersion": "1.4.0",
|
||||
"description": "Build risk bowtie diagrams in your vault: threats, prevention and mitigation barriers, escalation factors, barrier analysis stacks, and PNG export.",
|
||||
"author": "Andre482",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "o-tie",
|
||||
"version": "1.0.12",
|
||||
"version": "1.0.13",
|
||||
"description": "Obsidian plugin for building risk bowtie diagrams",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -436,6 +436,20 @@ export class BowtieView extends TextFileView {
|
|||
return btn;
|
||||
}
|
||||
|
||||
private createInspectorBtn(
|
||||
parent: HTMLElement,
|
||||
label: string,
|
||||
action: (e: MouseEvent) => void,
|
||||
opts: { primary?: boolean; warning?: boolean } = {}
|
||||
): HTMLButtonElement {
|
||||
const cls = ["mod-small"];
|
||||
if (opts.primary) cls.push("mod-cta");
|
||||
if (opts.warning) cls.push("mod-warning");
|
||||
const btn = parent.createEl("button", { cls: cls.join(" "), text: label });
|
||||
btn.addEventListener("click", action);
|
||||
return btn;
|
||||
}
|
||||
|
||||
private renderInspector(): void {
|
||||
this.inspectorEl.empty();
|
||||
if (!this.selectedRef) {
|
||||
|
|
@ -493,23 +507,20 @@ export class BowtieView extends TextFileView {
|
|||
this.selectedRef.kind === "mitigationBarrier" ||
|
||||
this.selectedRef.kind === "transitionBarrier"
|
||||
) {
|
||||
const stackBtn = actions.createEl("button", { text: "+ stack row", cls: "mod-small" });
|
||||
stackBtn.addEventListener("click", (e) => {
|
||||
const rect = stackBtn.getBoundingClientRect();
|
||||
this.createInspectorBtn(actions, "+ Stack row", (e) => {
|
||||
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
|
||||
this.showAddStackRowMenu(
|
||||
{ clientX: rect.left, clientY: rect.bottom } as MouseEvent,
|
||||
this.selectedRef!
|
||||
);
|
||||
});
|
||||
const degBtn = actions.createEl("button", { text: "⚡ Degradation factor", cls: "mod-small" });
|
||||
degBtn.addEventListener("click", () => {
|
||||
this.createInspectorBtn(actions, "⚡ Degradation factor", () => {
|
||||
this.addDegradationFactor(this.selectedRef!);
|
||||
});
|
||||
}
|
||||
|
||||
if (this.selectedRef.kind === "degradationFactor") {
|
||||
const sgBtn = actions.createEl("button", { text: "+ safeguard", cls: "mod-small" });
|
||||
sgBtn.addEventListener("click", () => {
|
||||
this.createInspectorBtn(actions, "+ Safeguard", () => {
|
||||
this.addSafeguard(this.selectedRef!);
|
||||
});
|
||||
}
|
||||
|
|
@ -517,34 +528,47 @@ export class BowtieView extends TextFileView {
|
|||
if (this.selectedRef.kind === "topEvent" && this.selectedRef.eventId) {
|
||||
const eventIndex = this.bowtie.events.findIndex((e) => e.id === this.selectedRef!.eventId);
|
||||
if (eventIndex >= 0 && eventIndex < this.bowtie.events.length - 1) {
|
||||
const barBtn = actions.createEl("button", {
|
||||
text: "+ barrier to next event",
|
||||
cls: "mod-cta mod-small",
|
||||
});
|
||||
barBtn.addEventListener("click", () => {
|
||||
this.addTransitionBarrier(this.selectedRef!.eventId!);
|
||||
});
|
||||
this.createInspectorBtn(
|
||||
actions,
|
||||
"+ Barrier to next event",
|
||||
() => {
|
||||
this.addTransitionBarrier(this.selectedRef!.eventId!);
|
||||
},
|
||||
{ primary: true }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.selectedRef.kind === "threat") {
|
||||
const barBtn = actions.createEl("button", { text: "+ prevention barrier", cls: "mod-cta mod-small" });
|
||||
barBtn.addEventListener("click", () => {
|
||||
this.addPreventionBarrier(this.selectedRef!.threatId!);
|
||||
});
|
||||
this.createInspectorBtn(
|
||||
actions,
|
||||
"+ Prevention Barrier",
|
||||
() => {
|
||||
this.addPreventionBarrier(this.selectedRef!.threatId!);
|
||||
},
|
||||
{ primary: true }
|
||||
);
|
||||
}
|
||||
|
||||
if (this.selectedRef.kind === "consequence") {
|
||||
const barBtn = actions.createEl("button", { text: "+ mitigation barrier", cls: "mod-cta mod-small" });
|
||||
barBtn.addEventListener("click", () => {
|
||||
this.addMitigationBarrier(this.selectedRef!.consequenceId!);
|
||||
});
|
||||
this.createInspectorBtn(
|
||||
actions,
|
||||
"+ Mitigation Barrier",
|
||||
() => {
|
||||
this.addMitigationBarrier(this.selectedRef!.consequenceId!);
|
||||
},
|
||||
{ primary: true }
|
||||
);
|
||||
}
|
||||
|
||||
const delBtn = actions.createEl("button", { text: "Delete", cls: "mod-warning mod-small" });
|
||||
delBtn.addEventListener("click", () => {
|
||||
this.deleteNode(this.selectedRef!);
|
||||
});
|
||||
this.createInspectorBtn(
|
||||
actions,
|
||||
"Delete",
|
||||
() => {
|
||||
this.deleteNode(this.selectedRef!);
|
||||
},
|
||||
{ warning: true }
|
||||
);
|
||||
}
|
||||
|
||||
private fitInspectorTextArea(textarea: HTMLTextAreaElement): void {
|
||||
|
|
@ -1442,11 +1466,17 @@ export class BowtieView extends TextFileView {
|
|||
}
|
||||
|
||||
if (e.pointerId === this.panPointerId) {
|
||||
const tappedEmptyCanvas = !this.gestureMoved && !this.panStartedOnNode;
|
||||
if (this.gestureMoved && this.panStartedOnNode) {
|
||||
this.suppressNextClick = true;
|
||||
}
|
||||
this.panStartedOnNode = false;
|
||||
this.endPan();
|
||||
// Touch taps on empty canvas call preventDefault on pointerdown, so no click
|
||||
// event fires to clear selection — dismiss the inspector on tap-up instead.
|
||||
if (tappedEmptyCanvas) {
|
||||
this.clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.activePointers.size === 1) {
|
||||
|
|
|
|||
|
|
@ -11,5 +11,6 @@
|
|||
"1.0.9": "1.4.0",
|
||||
"1.0.10": "1.4.0",
|
||||
"1.0.11": "1.4.0",
|
||||
"1.0.12": "1.4.0"
|
||||
"1.0.12": "1.4.0",
|
||||
"1.0.13": "1.4.0"
|
||||
}
|
||||
Loading…
Reference in a new issue