Compare commits

...

5 Commits

Author SHA1 Message Date
97d6f86ab4 minor fix...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2023-10-15 01:16:24 +03:00
94f3d912c4 notes...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2023-10-15 00:50:39 +03:00
e313a1a7e3 tweak...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2023-10-15 00:40:04 +03:00
ef666e91d0 even more refactoring + test editing is now mostly POLS-complete! =)
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2023-10-15 00:03:22 +03:00
7b755b413c refactoring + lots of tweaks and fixes...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2023-10-14 22:49:02 +03:00
3 changed files with 314 additions and 139 deletions

View File

@ -23,6 +23,20 @@ var atLine = function(elem, index){
return false } return false }
/*
function clickPoint(x,y){
document
.elementFromPoint(x, y)
.dispatchEvent(
new MouseEvent( 'click', {
view: window,
bubbles: true,
cancelable: true,
screenX: x,
screenY: y,
} )) }
//*/
//--------------------------------------------------------------------- //---------------------------------------------------------------------
@ -147,6 +161,39 @@ var quoted = {
return text return text
.replace(this.pre_pattern, this.pre.bind(this)) .replace(this.pre_pattern, this.pre.bind(this))
.replace(this.quote_pattern, this.quote.bind(this)) }, .replace(this.quote_pattern, this.quote.bind(this)) },
// XXX is this a good strategy???
__state: undefined,
__keydown__: function(evt, editor, elem){
// code editing...
if(elem.nodeName == 'CODE'
&& elem.getAttribute('contenteditable') == 'true'){
// XXX can keydown and keyup be triggered from different elements???
this.__state = elem.innerText
return false } },
// defined <plugin>.__editedview__(..) handler
__keyup__: function(evt, editor, elem){
var elem = evt.target
if(elem.nodeName == 'CODE'
&& elem.getAttribute('contenteditable') == 'true'){
// trigger if state actually changed..
this.__state != elem.innerText
&& editor.runPlugins('__editedview__', evt, editor, elem) } },
__focusout__: function(){
this.__state = undefined },
__editedview__: function(evt, editor, elem){
// editable code...
var block = editor.get(elem)
var code = block.querySelector('.code')
var update = elem.innerText
var i = [...block
.querySelectorAll('.view code[contenteditable=true]')]
.indexOf(elem)
// update element content...
code.value = quoted.replace(code.value, i, update)
return this },
} }
@ -164,8 +211,22 @@ var syntax = {
__setup__: function(editor){ __setup__: function(editor){
return this.update() }, return this.update() },
// XXX make a local update... // XXX make a local update...
__changed__: function(evt, editor, node){ __editedcode__: function(evt, editor, elem){
return this.update() }, return this.update(elem) },
__editedview__: function(evt, editor, elem){
// XXX should we also clear the syntax???
delete elem.dataset.highlighted
return this },
// XXX this removes highlighting, can we make it update live???
__focusin__: function(evt, editor, elem){
if(elem.nodeName == 'CODE'
&& elem.getAttribute('contenteditable') == 'true'){
elem.classList.remove('hljs') } },
__focusout__: function(evt, editor, elem){
if(elem.nodeName == 'CODE'
&& elem.getAttribute('contenteditable') == 'true'){
this.update(elem) }
return this },
} }
@ -194,7 +255,7 @@ var styling = {
__parse__: function(text, editor, elem){ __parse__: function(text, editor, elem){
return text return text
// markers... // markers...
.replace(/(\s*)(?<!\\)(FEATURE:|Q:|Question:|Note:)(\s*)/gm, .replace(/(\s*)(?<!\\)(FEATURE[:?]|Q:|Question:|Note:)(\s*)/gm,
'$1<b class="$2">$2</b>$3') '$1<b class="$2">$2</b>$3')
.replace(/(\s*)(?<!\\)(ASAP|BUG|FIX|HACK|STUB|WARNING|CAUTION)(\s*)/gm, .replace(/(\s*)(?<!\\)(ASAP|BUG|FIX|HACK|STUB|WARNING|CAUTION)(\s*)/gm,
'$1<span class="highlight $2">$2</span>$3') '$1<span class="highlight $2">$2</span>$3')
@ -301,7 +362,7 @@ var tasks = {
// completion... // completion...
// XXX add support for being like a todo checkbox... // XXX add support for being like a todo checkbox...
.replace(/(?<!\\)\[[%]\]/gm, '<span class="completion"></span>') }, .replace(/(?<!\\)\[[%]\]/gm, '<span class="completion"></span>') },
__changed__: function(evt, editor, node){ __editedcode__: function(evt, editor, node){
return this.updateBranch(editor, node) }, return this.updateBranch(editor, node) },
__click__: function(evt, editor, elem){ __click__: function(evt, editor, elem){
// toggle checkbox... // toggle checkbox...
@ -341,6 +402,9 @@ var Outline = {
carot_jump_edge_then_block: false, carot_jump_edge_then_block: false,
// The order of plugins can be significant in the following cases:
// - parsing
// - event dropping
plugins: [ plugins: [
attributes, attributes,
blocks, blocks,
@ -355,11 +419,14 @@ var Outline = {
// XXX revise -- should this be external??? // XXX revise -- should this be external???
escaping, escaping,
], ],
// NOTE: if a handler returns false it will break plugin execution...
// XXX is this the right way to go???
runPlugins: function(method, ...args){ runPlugins: function(method, ...args){
for(var plugin of this.plugins){ for(var plugin of this.plugins){
method in plugin if(method in plugin){
&& plugin[method](...args) } if(plugin[method](...args) === false){
return this }, return false } } }
return true },
threadPlugins: function(method, value, ...args){ threadPlugins: function(method, value, ...args){
for(var plugin of this.plugins){ for(var plugin of this.plugins){
method in plugin method in plugin
@ -577,6 +644,27 @@ var Outline = {
return cur }, return cur },
deindent: function(node='focused', indent=false){ deindent: function(node='focused', indent=false){
return this.indent(node, indent) }, return this.indent(node, indent) },
shift: function(node='focused', direction){
if(node == 'up' || node == 'down'){
direction = node
node = 'focused' }
if(direction == null){
return }
node = this.get(node)
var focused = node.classList.contains('focused')
var siblings = this.get(node, 'siblings')
var i = siblings.indexOf(node)
if(direction == 'up'
&& i > 0){
siblings[i-1].before(node)
focused
&& this.focus()
} else if(direction == 'down'
&& i < siblings.length-1){
siblings[i+1].after(node)
focused
&& this.focus() }
return this },
show: function(node='focused', offset){ show: function(node='focused', offset){
var node = this.get(...arguments) var node = this.get(...arguments)
var outline = this.outline var outline = this.outline
@ -629,7 +717,6 @@ var Outline = {
return this }, return this },
// block serialization... // block serialization...
// XXX split this up into a generic handler + plugins...
// XXX need a way to filter input text... // XXX need a way to filter input text...
// use-case: hidden attributes... // use-case: hidden attributes...
// NOTE: this is auto-populated by .__code2html__(..) // NOTE: this is auto-populated by .__code2html__(..)
@ -684,20 +771,13 @@ var Outline = {
elem.text = run('post', text) elem.text = run('post', text)
return elem }, return elem },
// XXX essentially here we need to remove service stuff like some // output format...
// attributes (collapsed, id, ...)...
// XXX also need to quote leading '- ' in block text here...
// e.g.
// - block
// some text
// - text in the above block ('-' needs to be quoted)
// - next block
__code2text__: function(code){ __code2text__: function(code){
// XXX return code
}, .replace(/(\n\s*)-/g, '$1\\-') },
__text2code__: function(text){ __text2code__: function(text){
// XXX return text
}, .replace(/(\n\s*)\\-/g, '$1-') },
// serialization... // serialization...
data: function(elem, deep=true){ data: function(elem, deep=true){
@ -711,8 +791,10 @@ var Outline = {
} }, } },
json: function(node){ json: function(node){
var that = this var that = this
node ??= this.outline var children = [...(node ?
return [...node.lastChild.children] node.lastChild.children
: this.outline.children)]
return children
.map(function(elem){ .map(function(elem){
return that.data(elem) }) }, return that.data(elem) }) },
// XXX add option to customize indent size... // XXX add option to customize indent size...
@ -727,7 +809,7 @@ var Outline = {
for(var elem of node){ for(var elem of node){
text.push( text.push(
level +'- ' level +'- '
+ elem.text + this.__code2text__(elem.text)
.replace(/\n/g, '\n'+ level +' ') .replace(/\n/g, '\n'+ level +' ')
+ (elem.collapsed ? + (elem.collapsed ?
'\n'+level+' ' + 'collapsed:: true' '\n'+level+' ' + 'collapsed:: true'
@ -741,6 +823,7 @@ var Outline = {
.join('\n') }, .join('\n') },
parse: function(text){ parse: function(text){
var that = this
text = text text = text
.replace(/^\s*\n/, '') .replace(/^\s*\n/, '')
text = ('\n' + text) text = ('\n' + text)
@ -765,10 +848,10 @@ var Outline = {
collapsed = value == 'true' collapsed = value == 'true'
return '' }) return '' })
parent.push({ parent.push({
text: block text: that.__text2code__(block
// normalize indent... // normalize indent...
.split(new RegExp('\n'+sep+' ', 'g')) .split(new RegExp('\n'+sep+' ', 'g'))
.join('\n'), .join('\n')),
collapsed, collapsed,
children: [], children: [],
}) })
@ -850,6 +933,18 @@ var Outline = {
&& (code.innerHTML = this.text()) && (code.innerHTML = this.text())
return this }, return this },
// Actions...
prev: function(){},
next: function(){},
above: function(){},
below: function(){},
up: function(){},
down: function(){},
left: function(){},
right: function(){},
// XXX move the code here into methods/actions... // XXX move the code here into methods/actions...
// XXX add scrollIntoView(..) to nav... // XXX add scrollIntoView(..) to nav...
// XXX use keyboard.js... // XXX use keyboard.js...
@ -862,16 +957,10 @@ var Outline = {
var that = this var that = this
var edited = this.get('edited') var edited = this.get('edited')
if(edited){ if(edited){
var c = edited.selectionStart var line = edited.getTextGeometry().line
var jump = function(){ if(line == 0){
if(edited.selectionStart == 0){ evt.preventDefault()
// needed to remember the position... that.focus('edited', 'prev') }
edited.selectionStart = c
edited.selectionEnd = c
that.focus('edited', -1) } }
this.carot_jump_edge_then_block ?
jump()
: setTimeout(jump, 0)
} else { } else {
evt.preventDefault() evt.preventDefault()
this.focus('focused', -1) } }, this.focus('focused', -1) } },
@ -879,16 +968,10 @@ var Outline = {
var that = this var that = this
var edited = this.get('edited') var edited = this.get('edited')
if(edited){ if(edited){
var c = edited.selectionStart var {line, lines} = edited.getTextGeometry()
var jump = function(){ if(line == lines -1){
if(edited.selectionStart == edited.value.length){ evt.preventDefault()
// needed to remember the position... that.focus('edited', 'next') }
edited.selectionStart = c
edited.selectionEnd = c
that.focus('edited', 1) } }
this.carot_jump_edge_then_block ?
jump()
: setTimeout(jump, 0)
} else { } else {
evt.preventDefault() evt.preventDefault()
this.focus('focused', 1) } }, this.focus('focused', 1) } },
@ -930,6 +1013,21 @@ var Outline = {
this.toggleCollapse(false) this.toggleCollapse(false)
: this.focus('next') } }, : this.focus('next') } },
PageUp: function(evt){
var edited = this.get('edited')
if(!edited
&& (evt.shiftKey
|| evt.ctrlKey)){
evt.preventDefault()
this.shift('up') } },
PageDown: function(evt){
var edited = this.get('edited')
if(!edited
&& (evt.shiftKey
|| evt.ctrlKey)){
evt.preventDefault()
this.shift('down') } },
// indent... // indent...
Tab: function(evt){ Tab: function(evt){
evt.preventDefault() evt.preventDefault()
@ -943,37 +1041,78 @@ var Outline = {
// edit mode... // edit mode...
O: function(evt){ O: function(evt){
if(evt.target.nodeName != 'TEXTAREA'){ if(!this.get('edited')){
evt.preventDefault() evt.preventDefault()
this.Block('before') this.edit(
?.querySelector('textarea') this.Block('before')) } },
?.focus() } },
o: function(evt){ o: function(evt){
if(evt.target.nodeName != 'TEXTAREA'){ if(!this.get('edited')){
evt.preventDefault() evt.preventDefault()
this.Block('next') this.edit(
?.querySelector('textarea') this.Block('next')) } },
?.focus() } },
Enter: function(evt){ Enter: function(evt){
var edited = this.get('edited')
// edit -> split text...
if(edited){
if(evt.ctrlKey if(evt.ctrlKey
|| evt.shiftKey){ || evt.shiftKey){
return } return }
evt.preventDefault() evt.preventDefault()
evt.target.nodeName == 'TEXTAREA' ? var a = edited.selectionStart
var b = edited.selectionEnd
var prev = edited.value.slice(0, a)
var next = edited.value.slice(b)
edited.value = prev
this.Block('next') this.Block('next')
?.querySelector('textarea') edited = this.edit('next')
?.focus() edited.value = next
: this.get() edited.selectionStart = 0
?.querySelector('textarea') edited.selectionEnd = 0
?.focus() }, return }
// view -> edit...
evt.preventDefault()
this.edit() },
Escape: function(evt){ Escape: function(evt){
this.outline.querySelector('textarea:focus') this.focus() },
?.parentElement
?.focus() },
Delete: function(evt){ Delete: function(evt){
if(this.get('edited')){ var edited = this.get('edited')
if(edited){
if(edited.selectionStart == edited.value.length){
var next = this.get('edited', 'next')
// can't reclaim nested children...
if(this.get(next, 'children').length > 0){
return }
// do not delete past the top element...
if(this.get(0).querySelector('.code') === next){
return }
evt.preventDefault()
var i = edited.value.length
edited.value += next.value
edited.selectionStart = i
edited.selectionEnd = i
this.remove(next) }
return } return }
this.remove() }, this.remove() },
Backspace: function(evt){
var edited = this.get('edited')
if(edited
&& edited.selectionEnd == 0
// can't reclaim nested children...
&& this.get(edited, 'children').length == 0){
var prev = this.get('edited', 'prev')
// do not delete past the bottom element...
if(this.get(-1).querySelector('.code') === prev){
return }
evt.preventDefault()
var i = prev.value.length
prev.value += edited.value
this.edit(prev)
prev.selectionStart = i
prev.selectionEnd = i
this.remove(edited)
return } },
// select... // select...
// XXX add: // XXX add:
@ -989,9 +1128,6 @@ var Outline = {
: focused.setAttribute('selected', '') }, : focused.setAttribute('selected', '') },
}, },
// XXX might be a good idea to defer specific actions to event-like
// handlers...
// e.g. clicking left if block -> .blockleft(..) ... etc.
setup: function(dom){ setup: function(dom){
var that = this var that = this
this.dom = dom this.dom = dom
@ -1020,8 +1156,7 @@ var Outline = {
return } return }
// expand/collapse // expand/collapse
if(elem.classList.contains('view') if(elem.classList.contains('view')){
&& elem.parentElement.getAttribute('tabindex')){
// click: left of elem (outside) // click: left of elem (outside)
if(evt.offsetX < 0){ if(evt.offsetX < 0){
// XXX item menu? // XXX item menu?
@ -1038,7 +1173,7 @@ var Outline = {
// edit of focus... // edit of focus...
// NOTE: this is usefull if element text is hidden but the // NOTE: this is usefull if element text is hidden but the
// frame is still visible... // frame is still visible...
if(elem.getAttribute('tabindex')){ if(elem.classList.contains('block')){
elem.querySelector('.code').focus() } elem.querySelector('.code').focus() }
that.runPlugins('__click__', evt, that, elem) }) that.runPlugins('__click__', evt, that, elem) })
@ -1046,12 +1181,10 @@ var Outline = {
outline.addEventListener('keydown', outline.addEventListener('keydown',
function(evt){ function(evt){
var elem = evt.target var elem = evt.target
// code editing... if(that.runPlugins('__keydown__', evt, that, evt.target) !== true){
if(elem.nodeName == 'CODE'
&& elem.getAttribute('contenteditable') == 'true'){
return } return }
// update element state... // update element state...
if(elem.nodeName == 'TEXTAREA'){ if(elem.classList.contains('code')){
setTimeout(function(){ setTimeout(function(){
that.update(elem.parentElement) that.update(elem.parentElement)
elem.updateSize() }, 0) } elem.updateSize() }, 0) }
@ -1061,30 +1194,14 @@ var Outline = {
// update code block... // update code block...
outline.addEventListener('keyup', outline.addEventListener('keyup',
function(evt){ function(evt){
var elem = evt.target that.runPlugins('__keyup__', evt, that, evt.target) })
// editable code...
if(elem.nodeName == 'CODE'
&& elem.getAttribute('contenteditable') == 'true'){
// XXX should we clear the syntax???
// XXX do this only if things changed...
delete elem.dataset.highlighted
var block = that.get(elem)
var code = block.querySelector('.code')
var update = elem.innerText
var i = [...block
.querySelectorAll('.view code[contenteditable=true]')]
.indexOf(elem)
// update element content...
code.value = quoted.replace(code.value, i, update)
return } })
// toggle view/code of nodes... // toggle view/code of nodes...
outline.addEventListener('focusin', outline.addEventListener('focusin',
function(evt){ function(evt){
var elem = evt.target var elem = evt.target
// ignore children container...
if(elem.classList.contains('children')){ if(elem.classList.contains('children')){
return } return }
@ -1103,15 +1220,18 @@ var Outline = {
behavior: 'smooth', behavior: 'smooth',
}) })
//*/ //*/
})
// XXX do we need this???
that.runPlugins('__focusin__', evt, that, elem) })
outline.addEventListener('focusout', outline.addEventListener('focusout',
function(evt){ function(evt){
var node = evt.target var elem = evt.target
if(node.nodeName == 'TEXTAREA' if(elem.classList.contains('code')){
&& node?.nextElementSibling?.nodeName == 'SPAN'){ var block = elem.parentElement
var block = node.parentElement that.update(block, { text: elem.value })
that.update(block, { text: node.value }) that.runPlugins('__editedcode__', evt, that, elem) }
that.runPlugins('__changed__', evt, that, node) } })
that.runPlugins('__focusout__', evt, that, elem) })
// update .code... // update .code...
var update_code_timeout var update_code_timeout
outline.addEventListener('change', outline.addEventListener('change',
@ -1121,7 +1241,8 @@ var Outline = {
update_code_timeout = setTimeout( update_code_timeout = setTimeout(
function(){ function(){
update_code_timeout = undefined update_code_timeout = undefined
that.sync() }, that.sync()
that.runPlugins('__change__', evt, that) },
that.code_update_interval || 5000) }) that.code_update_interval || 5000) })
// toolbar... // toolbar...

View File

@ -14,6 +14,59 @@ HTMLTextAreaElement.prototype.autoUpdateSize = function(){
function(evt){ function(evt){
that.updateSize() }) that.updateSize() })
return this } return this }
HTMLTextAreaElement.prototype.getTextGeometry = function(){
var offset = this.selectionStart
var text = this.value
// get the relevant styles...
var style = getComputedStyle(this)
var s = {}
for(var i=0; i < style.length; i++){
var k = style[i]
if(k.startsWith('font')
|| k.startsWith('line')
|| k.startsWith('white-space')){
s[k] = style[k] } }
var carret = document.createElement('span')
carret.innerText = '|'
carret.style.margin = '0px'
carret.style.padding = '0px'
var span = document.createElement('span')
span.innerText = text.slice(0, offset)
Object.assign(span.style, {
...s,
position: 'fixed',
display: 'block',
top: '-100%',
left: '-100%',
width: this.offsetWidth + 'px',
height: this.scrollHeight + 'px',
padding: style.padding,
outline: 'solid 1px red',
pointerEvents: 'none',
})
span.append(carret)
document.body.append(span)
var res = {
length: text.length,
lines: Math.floor(this.offsetHeight / carret.offsetHeight),
line: Math.floor(carret.offsetTop / carret.offsetHeight),
offset: offset,
offsetLeft: carret.offsetLeft,
offsetTop: carret.offsetTop,
}
span.remove()
return res }
// calculate number of lines in text area (both wrapped and actual lines) // calculate number of lines in text area (both wrapped and actual lines)
Object.defineProperty(HTMLTextAreaElement.prototype, 'heightLines', { Object.defineProperty(HTMLTextAreaElement.prototype, 'heightLines', {

View File

@ -44,24 +44,16 @@ var setup = function(){
- // Seems that I unintentionally implemented quite a chunk of the markdown spec ;) - // Seems that I unintentionally implemented quite a chunk of the markdown spec ;)
- -
- ## Bugs: - ## Bugs:
- BUG: ASAP: editor: `-` at start of line is interpreted as block marker... - BUG: last node seems to get trash tags added to it's end...
- need to either:
- quote the `-` in .text() -- _preferreed_
- split the lines starting with `-` into nested nodes (a-la .load())
- BUG? pressing down from a longer line will jump over a shorter line
- to reproduce\:
- here is the line to jump from, for example from here
an we'll not get here...
- -
- ## ToDo: - ## ToDo:
- ASAP: editor: backsapce/del at start/end of a block should join it with prev/next
- ASAP: editor: pressing enter in text edit mode should split text into two blocks
- ASAP: editor: shifting nodes up/down
- ASAP: scroll into view is bad... - ASAP: scroll into view is bad...
- ASAP: need to reach checkboxes via keyboard - ASAP: need to reach checkboxes via keyboard
- FEATURE: read-only mode - FEATURE: read-only mode
- FEATURE: `collapse-children:: true` block option -- when loading collapse all immediate children - FEATURE: `collapse-children:: true` block option -- when loading collapse all immediate children
- FF: figure out a way to draw expand/collapse bullets without the use of CSS' `:has(..)` - FF: figure out a way to draw expand/collapse bullets without the use of CSS' `:has(..)`
- a way to make a block monospace (???)
- show list bullet if node is empty but edited... (???)
- Code blocks and bullets: - Code blocks and bullets:
- ``` - ```
code code
@ -76,6 +68,7 @@ var setup = function(){
- delete node - delete node
- indent/deindent - indent/deindent
- edit node - edit node
- auto-shift done blocks to the end of siblings... (option?)
- FEATURE? block templates... - FEATURE? block templates...
collapsed:: true collapsed:: true
- something like: `TPL: [_] <editable/> -- <editable/>` - something like: `TPL: [_] <editable/> -- <editable/>`
@ -92,7 +85,7 @@ var setup = function(){
- #### Click in this line and see where the cursor goes - #### Click in this line and see where the cursor goes
- _not sure how..._ - _not sure how..._
- 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)?
- Q: should bullets be on the same level as nodes or offset?? - Q: should list bullets be on the same level as nodes or offset??
collapsed:: true collapsed:: true
- A) justified to bullet: - A) justified to bullet:
* list item * list item
@ -104,6 +97,9 @@ var setup = function(){
block text block text
- NOTE: this is only a problem if making list-items manually -- disable??? - NOTE: this is only a problem if making list-items manually -- disable???
- empty item height is a bit off... - empty item height is a bit off...
- DONE editor: backsapce/del at start/end of a block should join it with prev/next
- DONE editor: pressing enter in text edit mode should split text into two blocks
- DONE editor: shifting nodes up/down
- DONE Q: can we edit code in a code block directly? (a-la Logseq) - DONE Q: can we edit code in a code block directly? (a-la Logseq)
- DONE "percentage complete" in parent blocks with todo's nested - DONE "percentage complete" in parent blocks with todo's nested
- DONE `.editor .outline:empty` view and behavior... - DONE `.editor .outline:empty` view and behavior...
@ -122,14 +118,16 @@ var setup = function(){
- -
- ## Refactoring: - ## Refactoring:
- Plugin architecture - Plugin architecture
- Item parser (`.__code2html__(..)`) - DONE basic structure
- ~split out~ - plugin handler sequencing (see: `.setup(..)`)
- ~define~/doc api - plugin handler canceling
- ~define a way to extend/stack parsers~ - DONE Item parser (`.__code2html__(..)`)
- DONE split out
- DONE define a way to extend/stack parsers
- Format parser/generator - Format parser/generator
- split out - split out
- define api - define api
- experiment with clean markdown as format - experiment with clean _markdown_ as format
- CSS - CSS
- separate out theming - separate out theming
- separate out settings - separate out settings
@ -139,8 +137,25 @@ var setup = function(){
- Q: do we need `features.js` and/or `actions.js` - Q: do we need `features.js` and/or `actions.js`
- Q: do we need a concatenative API?? - Q: do we need a concatenative API??
- `<block>.get() -> <block>` - `<block>.get() -> <block>`
- Docs
- -
- ## TEST - ## TEST
- ### Controls
- ASAP: these need updating...
- | Key | Action |
| up | focus node above |
| down | focus node below |
| left | focus parent node |
| right | focus first child node |
| tab | indent node |
| s-tab | deindent node |
| s-pgup | shift node up |
| s-pgdown | shift node down |
| s-left | collapse node |
| s-right | expand node |
| enter | normal mode: edit node |
| | edit mode: create node below |
| esc | exit edit mode |
- ### Formatting: - ### Formatting:
- Styles - Styles
- # Heading 1 - # Heading 1
@ -235,7 +250,8 @@ var setup = function(){
- This is a line of text - This is a line of text
- This is a set - This is a set
text lines text lines
- Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text </pre> - Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text
- </pre>
<!-- outline --> <!-- outline -->
<div class="outline"></div> <div class="outline"></div>
<!-- toolbar (optional) --> <!-- toolbar (optional) -->
@ -248,28 +264,13 @@ var setup = function(){
<button onclick="editor.toggleCollapse()?.focus()">&#709;&#708;</button> <button onclick="editor.toggleCollapse()?.focus()">&#709;&#708;</button>
<button onclick="editor.remove()">&times;</button> <button onclick="editor.remove()">&times;</button>
</div--> </div-->
<span class="__textarea"></span>
</div> </div>
<hr> <hr>
<button onclick="editor.dom.classList.toggle('show-click-zones')">show/hide click zones</button> <button onclick="editor.dom.classList.toggle('show-click-zones')">show/hide click zones</button>
<pre>
Controls:
up - focus node above
down - focus node below
left - focus parent node
right - focus first child node
tab - indent node
s-tab - deindent node
s-left - collapse node
s-right - expand node
enter - normal mode: edit node
- edit mode: create node below
esc - exit edit mode
</pre>
</body> </body>
</html> </html>