diff --git a/css/rulesAssistant/activationConditions.css b/css/rulesAssistant/activationConditions.css
index 0c7a4217e2f9ee5cc026f3a949f0103ee20a569c..2acfd18cb8dbd87a79e6d275fb3c96e26fd45511 100644
--- a/css/rulesAssistant/activationConditions.css
+++ b/css/rulesAssistant/activationConditions.css
@@ -1,16 +1,39 @@
+.rule-builder {
+    display: grid;
+    grid-template-columns: auto max-content;
+    grid-column-gap: 1em;
+}
+
 .rule-group {
     margin: 5px;
     border: #00cb7a 5px solid;
-    cursor: grab;
 }
+
 .rule-condition {
     margin: 5px;
     border: red 5px solid;
-    cursor: grab;
 }
+
 .rule-drop-location {
     display: inline-block;
     background-color: orange;
     min-width: 5em;
     min-height: 1em;
 }
+
+.rule-draggable {
+    cursor: grab;
+}
+
+.rule-drag-element {
+    display: inline-block;
+    background-color: darkgray;
+    min-width: 2em;
+    min-height: 1em;
+}
+
+.rule-trash {
+    background-color: palevioletred;
+    min-width: 10em;
+    min-height: 2em;
+}
diff --git a/src/js/rulesAssistantActivationCondition.js b/src/js/rulesAssistantActivationCondition.js
index 65fba9621e01366616d1401bf5a9aeb6702ede42..8fbc98b3ae68f9826708c666f30eb3b26c21b40b 100644
--- a/src/js/rulesAssistantActivationCondition.js
+++ b/src/js/rulesAssistantActivationCondition.js
@@ -34,6 +34,12 @@ App.RA.Activation.Editor = (function() {
 		return editorNode;
 	}
 
+	function refreshEditor() {
+		if (editorNode !== null) {
+			$(editorNode).empty().append(buildEditor());
+		}
+	}
+
 	/**
 	 * TODO: Run when leaving the passage
 	 * TODO: save to correct time
@@ -48,26 +54,46 @@ App.RA.Activation.Editor = (function() {
 	}
 
 	/**
-	 * @returns {DocumentFragment}
+	 * @returns {HTMLElement}
 	 */
 	function buildEditor() {
-		const f = new DocumentFragment();
+		const outerDiv = document.createElement("div");
+		outerDiv.classList.add("rule-builder");
+
+		const ruleDiv = document.createElement("div");
 		const errors = [];
 		if (currentRule.validate(errors) === "error") {
-			f.append("Rule has errors:");
+			ruleDiv.append("Rule has errors:");
 			for (const error of errors) {
-				f.append(" ", error);
+				ruleDiv.append(" ", error);
 			}
 		}
-		f.append(currentRule.render());
+		ruleDiv.append(currentRule.render());
+		outerDiv.append(ruleDiv);
 
-		return f;
+		outerDiv.append(buildPartBrowser());
+
+		return outerDiv;
 	}
 
-	function refreshEditor() {
-		if (editorNode !== null) {
-			$(editorNode).empty().append(buildEditor());
-		}
+	/**
+	 * @returns {HTMLDivElement}
+	 */
+	function buildPartBrowser() {
+		const div = document.createElement("div");
+		App.UI.DOM.appendNewElement("h3", div, "Part Browser");
+		div.append(new RulePartProvider(() => new RuleGroup("and")).render());
+		div.append(new RulePartProvider(() => new RuleGroup("add")).render());
+		div.append(new RulePartProvider(() => new RulePair("eq")).render());
+		div.append(new RulePartProvider(() => new RuleNegate()).render());
+		div.append(new RulePartProvider(() => new RuleMapCheck(App.RA.Activation.getterManager.booleanDefault)).render());
+		div.append(new RulePartProvider(() => new RuleMapCheck(App.RA.Activation.getterManager.numberDefault)).render());
+		div.append(new RulePartProvider(() => new RuleMapCheck(App.RA.Activation.getterManager.stringDefault)).render());
+		div.append(new RulePartProvider(() => new RuleConstant(0)).render());
+		div.append(new RulePartProvider(() => new RuleConstant("string")).render());
+		div.append(new RulePartProvider(() => new RuleBooleanConstant(true)).render());
+		div.append(new RulePartTrash().render());
+		return div;
 	}
 
 	/**
@@ -142,6 +168,76 @@ App.RA.Activation.Editor = (function() {
 		}
 	}
 
+	class RulePartProvider extends RuleContainer {
+		/**
+		 * @param {()=>RulePart} partFactory
+		 */
+		constructor(partFactory) {
+			super();
+			this._partFactory = partFactory;
+		}
+
+		render() {
+			const div = document.createElement("div");
+			const part = this._partFactory();
+			part.parent = this;
+			div.append(part.render());
+			return div;
+		}
+
+		/**
+		 * @returns {"error"}
+		 */
+		validate(errors) {
+			return "error";
+		}
+
+		removeChild(rulePart) {
+		}
+
+		isParent(maybeChild) {
+			return false;
+		}
+	}
+
+	class RulePartTrash extends RuleContainer {
+		render() {
+			const div = document.createElement("div");
+			div.classList.add("rule-trash");
+			div.append("Trash");
+			div.ondragover = ev => {
+				// show that it can be dropped
+				ev.preventDefault();
+				// stop groups further out from capturing the event.
+				ev.stopPropagation();
+			};
+			div.ondrop = ev => {
+				ev.preventDefault();
+				// stop groups further out from capturing the event.
+				ev.stopPropagation();
+				const rulePartID = ev.dataTransfer.getData("text/plain");
+				const rulePart = rulePartMap.get(rulePartID);
+				rulePart.parent.removeChild(rulePart);
+				refreshEditor();
+			};
+			return div;
+		}
+
+		/**
+		 * @returns {"error"}
+		 */
+		validate(errors) {
+			return "error";
+		}
+
+		removeChild(rulePart) {
+		}
+
+		isParent(maybeChild) {
+			return false;
+		}
+	}
+
 	class RuleGroup extends RuleContainer {
 		/**
 		 * @param {"and"|"or"|"add"|"mul"} mode
@@ -160,6 +256,19 @@ App.RA.Activation.Editor = (function() {
 			const div = document.createElement("div");
 			div.classList.add("rule-group");
 			div.append(this.mode);
+			const button = App.UI.DOM.appendNewElement("button", div, "<->");
+			button.onclick = () => {
+				if (this.mode === "and") {
+					this.mode = "or";
+				} else if (this.mode === "or") {
+					this.mode = "and";
+				} else if (this.mode === "add") {
+					this.mode = "mul";
+				} else {
+					this.mode = "add";
+				}
+				refreshEditor();
+			};
 			for (const rulePart of this._children) {
 				div.append(rulePart.render());
 			}
@@ -259,6 +368,10 @@ App.RA.Activation.Editor = (function() {
 			div.append("not");
 			if (this._child != null) {
 				div.append(this._child.render());
+				div.ondragover = ev => {
+					// stop groups further out from capturing the event.
+					ev.stopPropagation();
+				};
 			} else {
 				div.ondragover = ev => {
 					if (canDrop(ev, this)) {
@@ -277,10 +390,7 @@ App.RA.Activation.Editor = (function() {
 					refreshEditor();
 				};
 			}
-			if (this.parent !== null) {
-				// if null, it's the outermost and that can't be draggable
-				makeDraggable(div, this);
-			}
+			makeDraggable(div, this);
 			return div;
 		}
 
@@ -336,9 +446,27 @@ App.RA.Activation.Editor = (function() {
 		}
 	}
 
+	/**
+	 * @typedef {"sub" | "div" | "eq" | "neq" | "gt" | "gte" | "lt" | "lte"| "contain"} RulePairComparators
+	 */
+	/**
+	 * @type {Map<RulePairComparators, string>}
+	 */
+	const rulePairComparators = new Map([
+		["eq", "=="],
+		["neq", "!="],
+		["lt", "<"],
+		["gt", ">"],
+		["lte", "<="],
+		["gte", ">="],
+		["sub", "-"],
+		["div", "/"],
+		["contain", "contains"],
+	]);
+
 	class RulePair extends RuleContainer {
 		/**
-		 * @param {"sub" | "div" | "eq" | "neq" | "gt" | "gte" | "lt" | "lte"| "contain"} mode
+		 * @param {RulePairComparators} mode
 		 */
 		constructor(mode) {
 			super();
@@ -356,10 +484,9 @@ App.RA.Activation.Editor = (function() {
 		render() {
 			const div = document.createElement("div");
 			div.classList.add("rule-group");
-			if (this.parent !== null) {
-				// if null, it's the outermost and that can't be draggable
-				makeDraggable(div, this);
-			}
+			// drag element
+			makeNotDraggable(div);
+			div.append(createDragElement(this));
 			// element 1
 			let span = document.createElement("span");
 			span.classList.add("rule-drop-location");
@@ -385,7 +512,31 @@ App.RA.Activation.Editor = (function() {
 			}
 			div.append(span);
 			// operator
-			div.append(this.mode);
+			let matchFound = false;
+			let select = document.createElement("select");
+
+			for (const [key, name] of rulePairComparators) {
+				let el = document.createElement("option");
+				el.value = key;
+				el.textContent = name;
+				if (this.mode === key) {
+					el.selected = true;
+					matchFound = true;
+				}
+				select.append(el);
+			}
+			if (!matchFound) {
+				select.selectedIndex = -1;
+			}
+			select.onchange = () => {
+				/** @type {HTMLSelectElement} */
+					// @ts-ignore
+				const option = select.children.item(select.selectedIndex);
+				// @ts-ignore
+				this.mode = option.value;
+				refreshEditor();
+			};
+			div.append(select);
 			// element 2
 			span = document.createElement("span");
 			span.classList.add("rule-drop-location");
@@ -536,12 +687,30 @@ App.RA.Activation.Editor = (function() {
 		constructor(value) {
 			super();
 			this.value = value;
+			this._stringMode = typeof value === "string";
 		}
 
 		render() {
-			// TODO make editable
-			const div = App.UI.DOM.makeElement("div", this.value, ["rule-condition"]);
-			makeDraggable(div, this);
+			const div = App.UI.DOM.makeElement("div", createDragElement(this), ["rule-condition"]);
+			const button = document.createElement("button");
+			button.append(this._stringMode ? "String" : "Number");
+			button.onclick = () => {
+				this._stringMode = !this._stringMode;
+				if (this._stringMode) {
+					this.value = String(this.value);
+				} else {
+					this.value = Number(this.value);
+					if (Number.isNaN(this.value)) {
+						this.value = 0;
+					}
+				}
+				refreshEditor();
+			};
+			div.append(button);
+			div.append(App.UI.DOM.makeTextBox(this.value, (v) => {
+				this.value = v;
+				refreshEditor();
+			}, !this._stringMode));
 			return div;
 		}
 
@@ -549,7 +718,7 @@ App.RA.Activation.Editor = (function() {
 		 * @returns {"number"|"string"}
 		 */
 		validate() {
-			return typeof this.value === "string" ? "string" : "number";
+			return this._stringMode ? "string" : "number";
 		}
 	}
 
@@ -620,18 +789,38 @@ App.RA.Activation.Editor = (function() {
 		}
 	}
 
+	/**
+	 * @param {RulePart} rulePart
+	 */
+	function createDragElement(rulePart) {
+		const span = document.createElement("span");
+		span.classList.add("rule-drag-element");
+		makeDraggable(span, rulePart);
+		return span;
+	}
+
 	/**
 	 * @param {HTMLElement} node
 	 * @param {RulePart} rulePart
 	 */
 	function makeDraggable(node, rulePart) {
 		node.draggable = true;
+		node.classList.add("rule-draggable");
 		node.ondragstart = ev => {
 			ev.stopPropagation();
 			ev.dataTransfer.setData("text/plain", rulePart.id);
 		};
 	}
 
+	/**
+	 * @param {HTMLElement} node
+	 */
+	function makeNotDraggable(node) {
+		node.ondragstart = ev => {
+			ev.stopPropagation();
+		};
+	}
+
 	/**
 	 * @param {DragEvent} event
 	 * @param {RuleContainer} targetPart
@@ -665,7 +854,7 @@ App.RA.Activation.Editor = (function() {
 		 */
 		function makeGroup(mode) {
 			const length = stack.popNumber();
-			const group = new RuleGroup("and");
+			const group = new RuleGroup(mode);
 			const children = [];
 			for (let i = 0; i < length; i++) {
 				children.unshift(stack.popRulePart());
@@ -826,5 +1015,5 @@ App.RA.Activation.Editor = (function() {
  * @type {FC.RA.PostFixRule}
  */
 App.RA.Activation.testRule = [
-	"vsex", "!XX", "eqstr", "not", "vdevotion", 90, "gte", "vlabel", "!Whore", "contain", false, 4, "and"
+	"vsex", "!XX", "eqstr", "not", "vdevotion", 90, "gte", "vlabel", "!Whore", "contain", false, true, 2, "or", 4, "and"
 ];
diff --git a/src/js/rulesAssistantActivationEvaluation.js b/src/js/rulesAssistantActivationEvaluation.js
index 56ec28669d33375a3b58fae2a4edb00f64657009..e94871d55a878ee666cf42980151fb004f3d58cb 100644
--- a/src/js/rulesAssistantActivationEvaluation.js
+++ b/src/js/rulesAssistantActivationEvaluation.js
@@ -120,6 +120,27 @@ App.RA.Activation.getterManager = (function() {
 			return this._stringGetter;
 		}
 
+		/**
+		 * @returns {string}
+		 */
+		get booleanDefault() {
+			return this._booleanGetter.keys().next().value;
+		}
+
+		/**
+		 * @returns {string}
+		 */
+		get numberDefault() {
+			return this._numberGetter.keys().next().value;
+		}
+
+		/**
+		 * @returns {string}
+		 */
+		get stringDefault() {
+			return this._stringGetter.keys().next().value;
+		}
+
 		/**
 		 * @param {App.RA.Activation.Stack} stack
 		 * @param {App.Entity.SlaveState} slave
@@ -151,6 +172,7 @@ App.RA.Activation.getterManager = (function() {
 
 App.RA.Activation.populateGetters = function() {
 	const gm = App.RA.Activation.getterManager;
+	// Note: The first value of each type being added is taken as the default.
 	gm.addBoolean("isfertile", {name: "Is Fertile?", val: isFertile});
 	gm.addBoolean("isamputee", {name: "Is Amputee?", val: isAmputee});
 	gm.addNumber("devotion", {name: "Devotion", val: s => s.devotion});