mirror of
				https://github.com/flynx/pWiki.git
				synced 2025-10-31 11:00:08 +00:00 
			
		
		
		
	undo working -- all primary functionality seems to be done...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
		
							parent
							
								
									d21d233b07
								
							
						
					
					
						commit
						9e8557ebc3
					
				| @ -27,7 +27,7 @@ | |||||||
| 	font-size: var(--font-size); | 	font-size: var(--font-size); | ||||||
| 
 | 
 | ||||||
| 	/*text-size-adjust: none;*/ | 	/*text-size-adjust: none;*/ | ||||||
| 	text-size-adjust: 150%; | 	text-size-adjust: 180%; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .editor { | .editor { | ||||||
|  | |||||||
| @ -673,7 +673,6 @@ var JSONOutline = { | |||||||
| 	at: function(index){}, | 	at: function(index){}, | ||||||
| 
 | 
 | ||||||
| 	indent: function(){}, | 	indent: function(){}, | ||||||
| 	deindent: function(){}, |  | ||||||
| 	shift: function(){}, | 	shift: function(){}, | ||||||
| 	show: function(){}, | 	show: function(){}, | ||||||
| 	toggleCollapse: function(){}, | 	toggleCollapse: function(){}, | ||||||
| @ -692,6 +691,7 @@ var JSONOutline = { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| // XXX experiment with a concatinative model...
 | // XXX experiment with a concatinative model...
 | ||||||
| // 		.get(..) -> Outline (view)
 | // 		.get(..) -> Outline (view)
 | ||||||
| var Outline = { | var Outline = { | ||||||
| @ -1035,9 +1035,9 @@ var Outline = { | |||||||
| 		return node }, | 		return node }, | ||||||
| 
 | 
 | ||||||
| 	// edit...
 | 	// edit...
 | ||||||
| 	indent: function(node='focused', indent=true){ | 	indent: function(node='focused', indent='in'){ | ||||||
| 		// .indent(<indent>)
 | 		// .indent(<indent>)
 | ||||||
| 		if(node === true || node === false){ | 		if(node === 'in' || node === 'out'){ | ||||||
| 			indent = node | 			indent = node | ||||||
| 			node = 'focused' } | 			node = 'focused' } | ||||||
| 		var cur = this.get(node)  | 		var cur = this.get(node)  | ||||||
| @ -1045,7 +1045,7 @@ var Outline = { | |||||||
| 			return } | 			return } | ||||||
| 		var siblings = this.get(node, 'siblings') | 		var siblings = this.get(node, 'siblings') | ||||||
| 		// deindent...
 | 		// deindent...
 | ||||||
| 		if(!indent){ | 		if(indent == 'out'){ | ||||||
| 			var parent = this.get(node, 'parent') | 			var parent = this.get(node, 'parent') | ||||||
| 			if(parent != this.outline){ | 			if(parent != this.outline){ | ||||||
| 				var children = siblings | 				var children = siblings | ||||||
| @ -1053,21 +1053,29 @@ var Outline = { | |||||||
| 				parent.after(cur) | 				parent.after(cur) | ||||||
| 				children.length > 0 | 				children.length > 0 | ||||||
| 					&& cur.lastChild.append(...children)  | 					&& cur.lastChild.append(...children)  | ||||||
|  | 				this.setUndo( | ||||||
|  | 					this.path(cur),  | ||||||
|  | 					'indent',  | ||||||
|  | 					['in']) | ||||||
| 				this.__change__() } | 				this.__change__() } | ||||||
| 		// indent...
 | 		// indent...
 | ||||||
| 		} else { | 		} else { | ||||||
| 			var parent = siblings[siblings.indexOf(cur) - 1] | 			var parent = siblings[siblings.indexOf(cur) - 1] | ||||||
| 			if(parent){ | 			if(parent){ | ||||||
| 				parent.lastChild.append(cur)  | 				parent.lastChild.append(cur)  | ||||||
|  | 			this.setUndo( | ||||||
|  | 				this.path(cur),  | ||||||
|  | 				'indent',  | ||||||
|  | 				['out']) | ||||||
| 				this.__change__()} }  | 				this.__change__()} }  | ||||||
| 		return cur }, | 		return cur }, | ||||||
| 	deindent: function(node='focused', indent=false){ |  | ||||||
| 		return this.indent(node, indent) }, |  | ||||||
| 	shift: function(node='focused', direction){ | 	shift: function(node='focused', direction){ | ||||||
| 		if(node == 'up' || node == 'down'){ | 		if(node == 'up' || node == 'down'){ | ||||||
| 			direction = node | 			direction = node | ||||||
| 			node = 'focused' } | 			node = 'focused' } | ||||||
| 		if(direction == null){ | 		if(direction == null  | ||||||
|  | 				|| (direction !== 'up'  | ||||||
|  | 					&& direction != 'down')){ | ||||||
| 			return } | 			return } | ||||||
| 		node = this.get(node) | 		node = this.get(node) | ||||||
| 		var focused = node.classList.contains('focused') | 		var focused = node.classList.contains('focused') | ||||||
| @ -1083,22 +1091,46 @@ var Outline = { | |||||||
| 			siblings[i+1].after(node)  | 			siblings[i+1].after(node)  | ||||||
| 			focused  | 			focused  | ||||||
| 				&& this.focus() } | 				&& this.focus() } | ||||||
|  | 		this.setUndo( | ||||||
|  | 			this.path(node),  | ||||||
|  | 			'shift',  | ||||||
|  | 			[direction == 'up' ?  | ||||||
|  | 				'down'  | ||||||
|  | 				: 'up']) | ||||||
| 		this.__change__() | 		this.__change__() | ||||||
| 		return this }, | 		return this }, | ||||||
| 	show: function(node='focused', offset){ | 	// XXX make undo a bit more refined...
 | ||||||
| 		var node = this.get(...arguments) | 	remove: function(node='focused'){ | ||||||
| 		var outline = this.outline | 		var elem = this.get(...arguments) | ||||||
| 		var parent = node | 		// XXX HACK...
 | ||||||
| 		var changes = false | 		var data = this.json() | ||||||
| 		do{ | 		var next  | ||||||
| 			parent = parent.parentElement | 		if(elem.classList.contains('focused')){ | ||||||
| 			changes = changes  | 			// XXX need to be able to get the next elem on same level...
 | ||||||
| 				|| parent.getAttribute('collapsed') == '' | 			this.toggleCollapse(elem, true) | ||||||
| 			parent.removeAttribute('collapsed') | 			next = elem === this.get(-1) ? | ||||||
| 		} while(parent !== outline) | 				this.get(elem, 'prev')  | ||||||
| 		changes | 				: this.get(elem, 'next') } | ||||||
| 			&& this.__change__() | 		elem?.remove() | ||||||
| 		return node }, | 		next  | ||||||
|  | 			&& this.focus(next) | ||||||
|  | 		// XXX HACK...
 | ||||||
|  | 		this.setUndo( | ||||||
|  | 			undefined,  | ||||||
|  | 			'load',  | ||||||
|  | 			[data]) | ||||||
|  | 		this.__change__() | ||||||
|  | 		return this }, | ||||||
|  | 	clear: function(){ | ||||||
|  | 		this.setUndo( | ||||||
|  | 			undefined,  | ||||||
|  | 			'load',  | ||||||
|  | 			[this.json()]) | ||||||
|  | 		this.outline.innerText = '' | ||||||
|  | 		this.__change__() | ||||||
|  | 		return this }, | ||||||
|  | 
 | ||||||
|  | 	// expand/collapse...
 | ||||||
| 	toggleCollapse: function(node='focused', state='next'){ | 	toggleCollapse: function(node='focused', state='next'){ | ||||||
| 		var that = this | 		var that = this | ||||||
| 		if(node == 'all'){ | 		if(node == 'all'){ | ||||||
| @ -1125,24 +1157,20 @@ var Outline = { | |||||||
| 				elem.updateSize() } } | 				elem.updateSize() } } | ||||||
| 		this.__change__() | 		this.__change__() | ||||||
| 		return node }, | 		return node }, | ||||||
| 	remove: function(node='focused', offset){ | 	show: function(node='focused', offset){ | ||||||
| 		var elem = this.get(...arguments) | 		var node = this.get(...arguments) | ||||||
| 		var next  | 		var outline = this.outline | ||||||
| 		if(elem.classList.contains('focused')){ | 		var parent = node | ||||||
| 			// XXX need to be able to get the next elem on same level...
 | 		var changes = false | ||||||
| 			this.toggleCollapse(elem, true) | 		do{ | ||||||
| 			next = elem === this.get(-1) ? | 			parent = parent.parentElement | ||||||
| 				this.get(elem, 'prev')  | 			changes = changes  | ||||||
| 				: this.get(elem, 'next') } | 				|| parent.getAttribute('collapsed') == '' | ||||||
| 		elem?.remove() | 			parent.removeAttribute('collapsed') | ||||||
| 		next  | 		} while(parent !== outline) | ||||||
| 			&& this.focus(next) | 		changes | ||||||
| 		this.__change__() | 			&& this.__change__() | ||||||
| 		return this }, | 		return node }, | ||||||
| 	clear: function(){ |  | ||||||
| 		this.outline.innerText = '' |  | ||||||
| 		this.__change__() |  | ||||||
| 		return this }, |  | ||||||
| 
 | 
 | ||||||
| 	// crop...
 | 	// crop...
 | ||||||
| 	crop: function(node='focused'){ | 	crop: function(node='focused'){ | ||||||
| @ -1150,6 +1178,7 @@ var Outline = { | |||||||
| 		for(var block of [...this.outline.querySelectorAll('[cropped]')]){ | 		for(var block of [...this.outline.querySelectorAll('[cropped]')]){ | ||||||
| 			block.removeAttribute('cropped') } | 			block.removeAttribute('cropped') } | ||||||
| 		this.get(...arguments).setAttribute('cropped', '') | 		this.get(...arguments).setAttribute('cropped', '') | ||||||
|  | 		// build header path...
 | ||||||
| 		this.header.innerHTML =  | 		this.header.innerHTML =  | ||||||
| 			`<span class="path-item" onclick="editor.uncrop('all')">/</span> `  | 			`<span class="path-item" onclick="editor.uncrop('all')">/</span> `  | ||||||
| 				+ this.path(...arguments, 'text') | 				+ this.path(...arguments, 'text') | ||||||
| @ -1158,27 +1187,63 @@ var Outline = { | |||||||
| 						return `<span class="path-item" onclick="editor.uncrop(${ length-i })">${s}</span> ` }) | 						return `<span class="path-item" onclick="editor.uncrop(${ length-i })">${s}</span> ` }) | ||||||
| 					.join(' / ') | 					.join(' / ') | ||||||
| 		return this }, | 		return this }, | ||||||
| 	uncrop: function(mode=1){ | 	uncrop: function(count=1){ | ||||||
| 		var outline = this.outline | 		var outline = this.outline | ||||||
| 		var top = this.get(0) | 		var top = this.get(0) | ||||||
| 		for(var block of [...this.outline.querySelectorAll('[cropped]')]){ | 		for(var block of [...this.outline.querySelectorAll('[cropped]')]){ | ||||||
| 			block.removeAttribute('cropped') } | 			block.removeAttribute('cropped') } | ||||||
| 		// crop parent if available...
 | 		// crop parent if available...
 | ||||||
| 		while(mode != 'all'  | 		while(count != 'all'  | ||||||
| 				&& mode > 0  | 				&& count > 0  | ||||||
| 				&& top !== outline){ | 				&& top !== outline){ | ||||||
| 			top = this.get(top, 'parent') | 			top = this.get(top, 'parent') | ||||||
| 			mode-- } | 			count-- } | ||||||
| 		if(mode == 'all' || top === outline){ | 		if(count == 'all' || top === outline){ | ||||||
| 			this.dom.classList.remove('crop') | 			this.dom.classList.remove('crop') | ||||||
| 			this.header.innerHTML = ''  | 			this.header.innerHTML = ''  | ||||||
| 		} else { | 		} else { | ||||||
| 			this.crop(top) } | 			this.crop(top) } | ||||||
| 		return this }, | 		return this }, | ||||||
| 
 | 
 | ||||||
|  | 	// undo...
 | ||||||
|  | 	// NOTE: calling .setUndo(..) will drop the redo stack, but this does 
 | ||||||
|  | 	// 		not happen when calling a method via .undo(..)/.redo(..) as we
 | ||||||
|  | 	// 		are reassigning the stacks manually.
 | ||||||
|  | 	__undo_stack: undefined, | ||||||
|  | 	__redo_stack: undefined, | ||||||
|  | 	setUndo: function(path, action, args){ | ||||||
|  | 		;(this.__undo_stack ??= []).push([path, action, args]) | ||||||
|  | 		this.__redo_stack = undefined | ||||||
|  | 		return this }, | ||||||
|  | 	clearUndo: function(){ | ||||||
|  | 		this.__undo_stack = undefined | ||||||
|  | 		this.__redo_stack = undefined | ||||||
|  | 		return this }, | ||||||
|  | 	__undo: function(from, to){ | ||||||
|  | 		if(from == null  | ||||||
|  | 				|| from.length == 0){ | ||||||
|  | 			return [from, to] } | ||||||
|  | 		var [path, action, args] = from.pop() | ||||||
|  | 		var l = from.length | ||||||
|  | 		path != null | ||||||
|  | 			&& this.focus(path) | ||||||
|  | 		this[action](...args) | ||||||
|  | 		if(l < from.length){ | ||||||
|  | 			to ??= [] | ||||||
|  | 			to.push( | ||||||
|  | 				...from.splice(l, from.length)) } | ||||||
|  | 		if(from.length == 0){ | ||||||
|  | 			from = undefined } | ||||||
|  | 		return [from, to] }, | ||||||
|  | 	undo: function(){ | ||||||
|  | 		;[this.__undo_stack, this.__redo_stack] =  | ||||||
|  | 			this.__undo(this.__undo_stack, this.__redo_stack) | ||||||
|  | 		return this }, | ||||||
|  | 	redo: function(){ | ||||||
|  | 		;[this.__redo_stack] = this.__undo(this.__redo_stack) | ||||||
|  | 		return this }, | ||||||
|  | 
 | ||||||
| 	// block render...
 | 	// block render...
 | ||||||
| 	// XXX need a way to filter input text...
 |  | ||||||
| 	// 		use-case: hidden attributes...
 |  | ||||||
| 	// NOTE: this is auto-populated by .__code2html__(..)
 | 	// NOTE: this is auto-populated by .__code2html__(..)
 | ||||||
| 	__styles: undefined, | 	__styles: undefined, | ||||||
| 	__code2html__: function(code, elem={}){ | 	__code2html__: function(code, elem={}){ | ||||||
| @ -1454,7 +1519,7 @@ var Outline = { | |||||||
| 			.clear() | 			.clear() | ||||||
| 			.outline | 			.outline | ||||||
| 				.append(...level(data)) | 				.append(...level(data)) | ||||||
| 		//* XXX do we actually need this???
 | 		/* XXX do we actually need this??? | ||||||
| 		// update sizes of all the textareas (transparent)...
 | 		// update sizes of all the textareas (transparent)...
 | ||||||
| 		setTimeout(function(){ | 		setTimeout(function(){ | ||||||
| 			for(var e of [...that.outline.querySelectorAll('textarea')]){ | 			for(var e of [...that.outline.querySelectorAll('textarea')]){ | ||||||
| @ -1609,8 +1674,7 @@ var Outline = { | |||||||
| 			this.focus(-1) }, | 			this.focus(-1) }, | ||||||
| 		PageUp: function(evt){ | 		PageUp: function(evt){ | ||||||
| 			var that = this | 			var that = this | ||||||
| 			var edited = this.get('edited') | 			if(this.get('edited')){ | ||||||
| 			if(edited){ |  | ||||||
| 				return } | 				return } | ||||||
| 			if(evt.shiftKey  | 			if(evt.shiftKey  | ||||||
| 					|| evt.ctrlKey){ | 					|| evt.ctrlKey){ | ||||||
| @ -1624,8 +1688,7 @@ var Outline = { | |||||||
| 						viewport[0], 'prev') } }, | 						viewport[0], 'prev') } }, | ||||||
| 		PageDown: function(evt){ | 		PageDown: function(evt){ | ||||||
| 			var that = this | 			var that = this | ||||||
| 			var edited = this.get('edited') | 			if(this.get('edited')){ | ||||||
| 			if(edited){ |  | ||||||
| 				return } | 				return } | ||||||
| 			if(evt.shiftKey  | 			if(evt.shiftKey  | ||||||
| 					|| evt.ctrlKey){ | 					|| evt.ctrlKey){ | ||||||
| @ -1643,7 +1706,9 @@ var Outline = { | |||||||
| 			evt.preventDefault() | 			evt.preventDefault() | ||||||
| 			var edited = this.get('edited') | 			var edited = this.get('edited') | ||||||
| 			var node = this.show( | 			var node = this.show( | ||||||
| 				this.indent(!evt.shiftKey)) | 				this.indent(evt.shiftKey ?  | ||||||
|  | 					'out'  | ||||||
|  | 					: 'in')) | ||||||
| 			// keep focus in node...
 | 			// keep focus in node...
 | ||||||
| 			;(edited ? | 			;(edited ? | ||||||
| 				edited | 				edited | ||||||
| @ -1686,12 +1751,29 @@ var Outline = { | |||||||
| 			if(this.get('edited')){ | 			if(this.get('edited')){ | ||||||
| 				this.focus()  | 				this.focus()  | ||||||
| 			} else { | 			} else { | ||||||
| 				evt.shiftKey ? | 				this.uncrop() } }, | ||||||
| 					this.uncrop('all') | 		s_Escape: function(evt){ | ||||||
| 					: this.uncrop() } }, | 			if(this.get('edited')){ | ||||||
|  | 				this.focus()  | ||||||
|  | 			} else { | ||||||
|  | 				this.uncrop('all') } }, | ||||||
| 		c: function(evt){ | 		c: function(evt){ | ||||||
| 			if(!this.get('edited')){ | 			if(!this.get('edited')){ | ||||||
| 				this.crop() } }, | 				this.crop() } }, | ||||||
|  | 			c_z: function(evt){ | ||||||
|  | 				if(!this.get('edited')){ | ||||||
|  | 					evt.preventDefault() | ||||||
|  | 					this.undo() } }, | ||||||
|  | 			c_s_z: function(evt){ | ||||||
|  | 				if(!this.get('edited')){ | ||||||
|  | 					evt.preventDefault() | ||||||
|  | 					this.redo() } }, | ||||||
|  | 			U: function(evt){ | ||||||
|  | 				if(!this.get('edited')){ | ||||||
|  | 					this.redo() } }, | ||||||
|  | 			u: function(evt){ | ||||||
|  | 				if(!this.get('edited')){ | ||||||
|  | 					this.undo() } }, | ||||||
| 
 | 
 | ||||||
| 		Delete: function(evt){ | 		Delete: function(evt){ | ||||||
| 			var edited = this.get('edited') | 			var edited = this.get('edited') | ||||||
| @ -1731,11 +1813,10 @@ var Outline = { | |||||||
| 				this.remove(edited) | 				this.remove(edited) | ||||||
| 				return } }, | 				return } }, | ||||||
| 
 | 
 | ||||||
| 		d: function(evt){ | 		c_d: function(evt){ | ||||||
| 			// toggle done...
 | 			// toggle done...
 | ||||||
| 			if(evt.ctrlKey){ | 			evt.preventDefault() | ||||||
| 				evt.preventDefault() | 			tasks.toggleDone(this) }, | ||||||
| 				tasks.toggleDone(this) } }, |  | ||||||
| 
 | 
 | ||||||
| 		// toggle checkbox...
 | 		// toggle checkbox...
 | ||||||
| 		' ': function(evt){ | 		' ': function(evt){ | ||||||
| @ -1835,8 +1916,26 @@ var Outline = { | |||||||
| 				if(that.runPlugins('__keydown__', evt, that, evt.target) !== true){ | 				if(that.runPlugins('__keydown__', evt, that, evt.target) !== true){ | ||||||
| 					return } | 					return } | ||||||
| 				// handle keyboard...
 | 				// handle keyboard...
 | ||||||
| 				evt.key in that.keyboard  | 				var keys = [] | ||||||
| 					&& that.keyboard[evt.key].call(that, evt) }) | 				evt.ctrlKey | ||||||
|  | 					&& keys.push('c_' + evt.key) | ||||||
|  | 				evt.ctrlKey && evt.altKey  | ||||||
|  | 					&& keys.push('c_a_' + evt.key) | ||||||
|  | 				evt.ctrlKey && evt.shiftKey | ||||||
|  | 					&& keys.push('c_s_' + evt.key) | ||||||
|  | 				evt.altKey && evt.ctrlKey && evt.shiftKey | ||||||
|  | 					&& keys.push('c_a_s_' + evt.key) | ||||||
|  | 				evt.altKey | ||||||
|  | 					&& keys.push('a_' + evt.key) | ||||||
|  | 				evt.altKey && evt.shiftKey | ||||||
|  | 					&& keys.push('a_s_' + evt.key) | ||||||
|  | 				evt.shiftKey | ||||||
|  | 					&& keys.push('s_' + evt.key) | ||||||
|  | 				keys.push(evt.key) | ||||||
|  | 				for(var k of keys){ | ||||||
|  | 					if(k in that.keyboard){ | ||||||
|  | 						that.keyboard[k].call(that, evt) | ||||||
|  | 						break } } }) | ||||||
| 		// update code block...
 | 		// update code block...
 | ||||||
| 		outline.addEventListener('keyup',  | 		outline.addEventListener('keyup',  | ||||||
| 			function(evt){ | 			function(evt){ | ||||||
| @ -1866,6 +1965,7 @@ var Outline = { | |||||||
| 					that.get('focused')?.classList?.add('focused') } | 					that.get('focused')?.classList?.add('focused') } | ||||||
| 				// textarea...
 | 				// textarea...
 | ||||||
| 				if(elem.classList.contains('code')){ | 				if(elem.classList.contains('code')){ | ||||||
|  | 					elem.dataset.original = elem.value | ||||||
| 					elem.updateSize() }  | 					elem.updateSize() }  | ||||||
| 
 | 
 | ||||||
| 				// XXX do we need this???
 | 				// XXX do we need this???
 | ||||||
| @ -1875,10 +1975,20 @@ var Outline = { | |||||||
| 				var elem = evt.target | 				var elem = evt.target | ||||||
| 				// update code...
 | 				// update code...
 | ||||||
| 				if(elem.classList.contains('code')){ | 				if(elem.classList.contains('code')){ | ||||||
| 					var block = elem.parentElement | 					var block = that.get(elem) | ||||||
| 					// clean out attrs...
 | 					// clean out attrs...
 | ||||||
| 					elem.value = that.parseBlockAttrs(elem.value).text | 					elem.value = that.parseBlockAttrs(elem.value).text | ||||||
| 					that.update(block)  | 					that.update(block)  | ||||||
|  | 					// undo...
 | ||||||
|  | 					if(elem.value != elem.dataset.original){ | ||||||
|  | 						that.setUndo( | ||||||
|  | 							that.path(elem), | ||||||
|  | 							'update', | ||||||
|  | 							[that.path(elem), { | ||||||
|  | 								...that.data(elem),  | ||||||
|  | 								text: elem.dataset.original, | ||||||
|  | 							}]) | ||||||
|  | 						delete elem.dataset.original } | ||||||
| 					// give the browser a chance to update the DOM...
 | 					// give the browser a chance to update the DOM...
 | ||||||
| 					// XXX revise...
 | 					// XXX revise...
 | ||||||
| 					setTimeout(function(){ | 					setTimeout(function(){ | ||||||
| @ -1917,6 +2027,7 @@ var Outline = { | |||||||
| 				.replace(/</g, '<') | 				.replace(/</g, '<') | ||||||
| 				.replace(/>/g, '>'))  | 				.replace(/>/g, '>'))  | ||||||
| 			console.log(`Parse: ${Date.now() - t}ms`) } | 			console.log(`Parse: ${Date.now() - t}ms`) } | ||||||
|  | 		this.clearUndo() | ||||||
| 
 | 
 | ||||||
| 		this.runPlugins('__setup__', this) | 		this.runPlugins('__setup__', this) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -51,9 +51,8 @@ var setup = function(){ | |||||||
|   - BUG: mobile browsers behave quite chaotically ignoring parts of the styling... |   - BUG: mobile browsers behave quite chaotically ignoring parts of the styling... | ||||||
|   - |   - | ||||||
| - ## ToDo: | - ## ToDo: | ||||||
|   - undo  |   - copy/paste nodes/trees | ||||||
|     collapsed:: true |   - FEATURE: read-only mode | ||||||
| 	- edit stack (position, action, ...) |  | ||||||
|   - auto-shift done blocks to the end of siblings... (option?) |   - auto-shift done blocks to the end of siblings... (option?) | ||||||
|     - ...or should this be `sort:: done` -- i.e. sort children by done status?? |     - ...or should this be `sort:: done` -- i.e. sort children by done status?? | ||||||
|   - codeblock as a block |   - codeblock as a block | ||||||
| @ -64,8 +63,6 @@ var setup = function(){ | |||||||
|         code |         code | ||||||
|         ``` |         ``` | ||||||
|       - _bullet should be either in the middle of the block or at the first line of code (preferred)..._ |       - _bullet should be either in the middle of the block or at the first line of code (preferred)..._ | ||||||
|   - copy/paste nodes/trees |  | ||||||
|   - FEATURE: read-only mode |  | ||||||
|   - export html |   - export html | ||||||
|     - embed css |     - embed css | ||||||
|     - cleanup html |     - cleanup html | ||||||
| @ -90,10 +87,6 @@ var setup = function(){ | |||||||
|       - each child node will copy the template and allow editing of only fields |       - each child node will copy the template and allow editing of only fields | ||||||
|       - not clear how to handle template changes... |       - not clear how to handle template changes... | ||||||
|   - JSON API |   - JSON API | ||||||
|     - `.path(..)` |  | ||||||
|     - `.get(..)` |  | ||||||
|     - `.at(..)` |  | ||||||
|     - ... |  | ||||||
|   - cli |   - cli | ||||||
|   - Q: do we use \\t for indent? (option???) |   - Q: do we use \\t for indent? (option???) | ||||||
|   - Q: persistent empty first/last node (a button to create a new node)? |   - Q: persistent empty first/last node (a button to create a new node)? | ||||||
| @ -111,6 +104,7 @@ var setup = function(){ | |||||||
|   - empty item height is a bit off... |   - empty item height is a bit off... | ||||||
|   - search? |   - search? | ||||||
|     - _...not sure if search should be internal or external yet..._ |     - _...not sure if search should be internal or external yet..._ | ||||||
|  |   - DONE undo  | ||||||
|   - DONE crop: make path clickable |   - DONE crop: make path clickable | ||||||
|   - DONE Q: crop: should we control crop via "crop-in"/"crop-out" instead of crop/uncrop?? |   - DONE Q: crop: should we control crop via "crop-in"/"crop-out" instead of crop/uncrop?? | ||||||
|     - _crop-in/crop-out seems more natural..._ |     - _crop-in/crop-out seems more natural..._ | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user