- # Outline editor prototype - An outline-based markdown editor experiment - ### Infuences:: - Logseq - Conboy (Nokia N900's Tomboy clone) - Bonsai (on PalmOS) - Google Keep (editor) - - // Seems that I unintentionally implemented quite a chunk of the markdown spec ;) - ### Index - TOC - - ## Bugs: focused:: true - BUG: refreshes under FF sometimes produce partial/inconsistent views... - BUG: need to account for hidden elements when cursoring between elements - to reproduce:: - XXX move from/to this element - abcdefghijklmnopqrstuvwxyz - [ ] and to/from this (this produces a small shift compared the the above) - there's a simple and a complex solutions to this: - simple: avoid hiding stuff -- not sure if this is achivable - complex: estimate size of elements, including hidden ones, and account for them - BUG: to level headings style may not be correct... - # H1 - # H2 - some text on same level - # H2 (Not H3) - this is not H3 because it is not under H2 (not in sub-tree) though it is on a deeper level... - this is not intuitive at first glance, but is logical... - Q: should we account for this?? (XXX) - BUG: indenting items in/out results in items below jumping down/up... (this only happens when in edit mode) - a - indent this line in out... - c - BUG: styling error... - this _seems `to` work_ - `while` _this `does` not_ - _seems to be connected with quoting..._ - BUG: mobile browsers behave quite chaotically ignoring parts of the styling... - FF: - zooming on edited field - normal textarea is not sized correctly - General: - bullets are too close to text - side margins are a bit too large - still need to account for toolbar to the right - left side does not need to be as wide - BUG? `.code` is currently over-extended and covers the whole element area (incl. `.children`) - this currently has no side-effects but is not pretty (???) - BUG? should the following render to the save view? (currently not) - text<hr>text - text <hr> text (extra newline rendered) - text --- text - DONE BUG: caret positioning broken (ASAP CLEANUP: GETTEXT) collapsed:: true - Strategies to test:: - ASAP use `getText(..)` to build the input text instead of `.innerText` - DONE adds 1 char offset per block element - this greatly simplifies things... - normalize `.innerText` to remove duplicate `"\n"`'s _(will break placement on empty lines... ???)_ - try and build offset maps on parse _(potentially too complicated)_ - *TODO*:: - *DONE*:: collapsed:: true - ``` text text text ``` text text text (a click here is offset right) collapsed:: true - the offset's amount depends on where in the text we click after the code block, the farther right the greater the offset... - `getCharOffset(..)` produces correct results, the problem is in `getMarkdownOffset(..)` - text text text <div> block element </div> this line, and above placement of completely broken collapsed:: true - the odd thing is that a nested (bug) qupted text below also breaks... - _this seems to be an issue with: `.getMarkdownOffset(..)`_ - ``` m = `text text text <div> block element </div> this line, and above placement of completely broken` t = 'text text text\n\n\nblock element\n\n\nthis line, and above placement of completely broken ' getMarkdownOffset(m, t, 26) ``` this returns `69` while it should return `5` _...replacing `\n\n\n` with `\n\n` seems to fix the issue (also works with spaces)_ (BUG also the above line is not italic -- can't reproduce) - text text text - text text text text text text text text text - text text text text & text & text text text text - M M can't place cursor before first char M - text text text ``` text text text ``` text text text - text text text _text text text_ text text text - _text text text_ text text text - ``` text text text ``` text text text - |text|text|text| |text|text|text| |text|text|text| - - ## ToDo: - ASAP Basic standalone client (file-oriented) - _The main idea is to provide a minimal usable app with syncable (externally) data_ - _Use to write/maintain docs for self_ - Levels:: - ASAP PWA (shared api) - ASAP FileSystemAPI (???) - see:: - https://developer.chrome.com/docs/capabilities/web-apis/file-system-access - is this usable??? _...this boils down to if we need to ask for access every run or once per install, if it's the former this API is essentially unusable..._ - [%] ASAP test/prototype:: - [ ] basic https file server (node) - [ ] get access / handle - [ ] test access / handle - [ ] minimal list/show/change/save code (or even simpler?) - [ ] store handles in IndexDB - ASAP localStorage _local/unsaved state_ - ASAP Conflict resolution _(line-based LCS a-la diff)_ - merge two versions - identify conflicts - Nextcloud / WebDAV (???) - _how expensive is it to do a nextcloud plugin???_ - Desktop / electron app - fs - Mobile / cordova app (???) - fs - Item templates: - ASAP keyboard actuation (a-la checkboxes) - inline DONE collapsed:: true - TEMPLATE text [cursor] - TEMPLATE title text [cursor] - sub-tree DONE collapsed:: true - TEMPLATE title - text [cursor] - TEMPLATE title *boo* - text [cursor] - TEMPLATE title [new] - text [cursor] - multi-template DONE collapsed:: true - TEMPLATE [A] [B] [C] - aaa - bbb - ccc - TEMPLATE title A text [cursor] - TEMPLATE^ title B - text [cursor] - renders as: - <button>title</button> - <select> <option>title A</option> <option>title B</option> </select><button>new</button> - action: - duplicate template text / subtree (w.o. markers) - select / place cursor at cursor marker - TODO: - nested templates?? - global templates -- or should this be an external macro??? - revise syntax - templated lists -- a list that uses a template for it's items - something like: - or here there is an explicit item template - TEMPLATE new - [ ] [cursor] - [ ] A - [ ] B - [ ] C - and a shorthand for todo: - TODO in lists like this new items will be created with a todo template automatically (???) - [ ] A - [ ] B - [ ] C - Need to ignore the template subtree in `[%]` calculation... DONE collapsed:: true - Example [%] (should be 50%) - TEMPLATE new - [ ] - [x] Done - [ ] Undone - Time to think about a standalone client -- at least to edit own notes as a test... - _also this would be a nice opportunity to start the move to a newer electron version_ - Deployment: - html - constructor - events/callbacks - basic storage adapters (LocalStorage/SessionStorage, FileAPI, IndexDB???) - _...Did I say that I hate how they crippled the FileAPI to the point that it is only usable for cool demos and not usable for any serious applications..._ - plugin API (???) - web component collapsed:: true - tag/attrs - resolve issues and unify text containmet: - `value=...` attr quote escaping (editing html/export/save) - tag body escaping html stuff escaping `</outline-editor>` in code - custom escaping api per input/output stream/source... - events - basic storage adapters - plugin API (???) - XXX load issues??? - file editor (electron/web) - notes (pWiki/electron/web) - custom element / web component - BUG: select via double/triple clicks does not work... - _looks like something is refocusing the element..._ - BUG/race: the non-value versions of custom elem seem to sometimes get loaded as empty... - DONE data interface: collapsed:: true - the "natural" way to pass data is to use the same mechanism as `<textarea>` the problem is that we can't extend `HTMLTextAreaElement` as it can not have shadow dom (reject?) - adding an explicit textarea element is an odd requirement (reject?) - seems that the least bad way to go is to use the `value` attribute - DONE API: directly mixin Outline? - DONE `.value` / `.code` should be both updated internally and also load new content when updated externally -- not yet sure how... - events - test nesting... - Q: can we get rid of the editor block??: - CSS breaks if we do... - need to figure out a way to handle autofocus for host/editor uniformly - - ASAP: FEATURE: images as blocks... - ASAP: expand sub-tree on follow link... - ASAP TEST: attributes: finalize base mechanics... - attributes: need to show/hide the attributes -- option? attr::value - DONE `.__parse_code__(..)`: add data attributes to code if missing... - DONE do we need `.__code_attrs__` / `.__view_attrs__`??? -- YES - DONE delete attr from code -- by setting it to `"null"` or `"undefined"`... - delete attr from code -- by removing it from attr list (when shown)... - DONE BUG? can't set `''` as attr value -- parser?? - TOC: tweaking: add args like depth, ... -- as attributes... - TOC: persistently generate as code (option) - ``` TOC ``` should turn into: ``` TOC \- [moo](#moo) \- [foo](#foo) ``` shown as: --- \- [moo](#moo) \- [foo](#foo) --- - ASAP check if we can add blocks during element handling... - the main problem here is where to get the data for the index? possible sources: - JSON -- preferred - DOM -- current - text - DOM/JSON would require a post-load generation stage... - export auto-headings as normal/manual markdown headings... - add plugin callback on `.text(..)` / ... - Might be a good idea to think how to avoid the constant selections on focus... - Focus of elements needs cleanup... - navigation up/down to a root element is a bit jumpy... - add loading spinner -- `.loading` class set on `.load(..)` and unset when done... - DONE set/unset class - add animation - make this extensible from client... - Q: When moving up/down between nodes do we want to place the caret relative to markdown (current) or to view? (see: `CARET_V_MOVE`) - Q: Should tables be text-based markdown or higher-level? collapsed:: true - for reference a normal table - | col 1 | col 2 | col 3 | | moo | foo | boo | | 1 | 2 | 3 | - block-based -- adjacent blocks in table format (a-la markdown) are treated as rows of one table... - here is an example - | col 1 | col 2 | col 3 | - | A | B | B | - | 1 | 2 | 3 | - - need to align columns -- not sure how (CSS preffered) - _in terms of syntax I like this the most..._ - _...but it can lead to implementing own table align algorithm (not sure I want to go this way!)..._ - block-children -- similar to how lists are done now - a demo - --table-- - | A | B | C | - | 1 | 2 | 3 | - | moo | foo | boo | - - the header may be used as:: - header row - caption text - both? - Q: how do we handle indenting a table row? - Q: how do we handle unmarked text? - edit mode needs work... - this feels a bit over-restricted... - - might be fun to make the general syntax (after removing "-") to be compatible with markdown... - might also be fun to auto-generate (template) new blocks within a table... - this would greatly simplify table navigation and creation - might be a good idea to fill the new elem with a row template... - creating a new line after this (line 2 as an example) - | A | B | C | - | | | | - - node selection - DONE multiple node selection (via shift+motion) - fixed state -- while `shift` pressed select or deselect only depending on first action (a-la FAR) - DONE double/triple click working... - delete - copy/cut/paste (???) - _idea: cope as code and parse on paste..._ - touch/mouse (???) - Q: should we select text through multiple nodes?? - _...should this turn into node selection as soon as node boundary is crossed???_ - copy/paste nodes/trees - numbered lists: add counters to a depth of 6-7... - _or find a way to make them repeat..._ - FEATURE: read-only mode - FEATURE: auto-shift done blocks to the end of siblings... (option?) - ...or should this be `sort:: done` -- i.e. sort children by done status?? - codeblock as a block _...if only whitespace before/after clear it and style whole block..._ _...might be a good idea to do this with codeblock at start/end of block..._ - Code blocks and bullets: - ``` code ``` - _bullet should be either in the middle of the block or at the first line of code (preferred)..._ - export html - embed css - cleanup html - generate ideomatic html (???) - style attrs (see: [attrs](#attributes)) - FF: figure out a way to draw expand/collapse bullets without the use of CSS' `:has(..)` - table inline editing a-la code editing -- click cell and edit directly... - smooth scrolling - _...this is more complicated than adding `behavior: "smooth"` to `.scrollIntoView(..)` as scrolling animation will get interrupted by next user input..._ - need to cancel animation if things are moving too fast... - make this generic (???) - JSON API - cli - Q: do we use \\t for indent? (option???) - Q: persistent empty first/last node (a button to create a new node)? - Q: search? - _seems that search should be external to the editor_ - empty item height is a bit off... - Nerd fonts (option???) - FEATURE: `collapse-children:: true` block option -- when loading collapse all immediate children - FEATURE? block templates... collapsed:: true - something like: `TPL: [_] <editable/> -- <editable/>` - `TPL:` -- template marker - `<editable/>` -- field marker - each child node will copy the template and allow editing of only fields - not clear how to handle template changes... - DONE attributes: might be a good idea to prevent setting `.text` and `.children` attrs... - DONE TOC: Q: should we have both manual and auto headings??? collapsed:: true - IMHO: no... - DONE attributes: need a way to remove attributes from editor -- `null` or `undefined` special values?? - DONE attributes: `.data(..)`: read in element attributes... collapsed:: true - TEST: this item has attr `moo` with value `"foo"` moo::foo - DONE TOC: headings that do not show up in toc -- `# ...` shows up while `@ ...` does not... - DONE \TOC global/local - DONE FEATURE dynamic headings -- level depends on number of headings above... - DONE Experimental syntax: ``` @ Heading ``` - Target syntax: ``` # Heading ``` - would be logical to (???): - only first top level heading is level 1 - subsequent top level headings all level 2 - DONE `.json(..)`, `.data(..)`, ... should be signature compatible with `.get(..)` (???) - DONE Q: Should we use `HTMLTextAreaElement.autoUpdateSize(..)` or handle it globally in setup??? collapsed:: true - _...I'm leaning towards the later..._ - currently solved passively... - DONE place the caret when moving between nodes - DONE selecting expanded code by _click-n-drag_ collapsed:: true - # this is a test string with some extra words - DONE `backspace`/`delete` in block contract the field with a delay... collapsed:: true - _...looks like we are updating size on keyup..._ - DONE Q: should list bullets be on the same level as nodes or offset?? collapsed:: true - A) justified to bullet: ``` * list item * list item block text ``` _This is impossible to create in the current implementation_ - B) justified to text _(current)_: ``` * list item * list item block text ``` - DONE add horizontal scroll to code blocks... collapsed:: true - ```html <outline-editor session-storage="outline-text" value="initial text"></outline-editor> ``` - DONE trailing whitespace is ignored in `.view`... collapsed:: true - demos: - leading whitespace... - trailing whitespace... - empty block\: - - _it seams that HTML ignores the last newline if it is not followed by anything_ - there are four ways to deal with this:# - trim whitespace on refocus (option) - show whitespace in both modes (option) - REJECT remove trailing whitespace completely on refocus (a-la logseq) - REJECT keep current behavior - I do not believe in keeping whitespace in edit and hiding it in view (POLS) - DONE add options to save to `.sessionStorage` / `.localStorage` - DONE might be a good idea to focus the prev (empty) node if pressing `Enter` at 0 position collapsed:: true - <- place cursor here and press enter - DONE make `---` block not show list bullets... - DONE: undo: checkboxes and DONE?? collapsed:: true _...this should be triggered by text change -- move current implementation to .__editedcode__()??..._ - DONE undo - DONE crop: make path clickable - DONE Q: crop: should we control crop via "crop-in"/"crop-out" instead of crop/uncrop?? collapsed:: true - _crop-in/crop-out seems more natural..._ - DONE crop: show crop path (and depth) - DONE over-travel pause -- when going fast over start/end stop... - DONE focus: collapsed:: true - DONE `<editor>.autofocus` - DONE `focused:: true` attr (`.text(..)`/`.json(..)`/`.load(..)`) - DONE focusing editor -> focus focused block - DONE identify a block: collapsed:: true - DONE index (flat) - DONE path (index) - DONE id - _the id attr is done, but we still need to get the node via id_ - DONE pgup/pgdown/home/end buttons - DONE FEATURE: "crop" -- view block tree separately... - DONE unify attr parsing collapsed:: true - _now duplicated in `.parse(..)` and `.__code2html__(..)`_ - might be a good idea to add a special text parse stage and use in on both branches... - DONE attrs in editor are not parsed correctly (see: [attrs](#attributes)) - DONE multiple attrs are not handled correctly (see: [attrs](#attributes)) - DONE call `.sync()` on all changes... - DONE show list bullet if node is empty but edited... collapsed:: true - _...not sure which is best, so both options are available, see: `editor.css`_ - DONE Q: can we get the caret line in a textarea??? collapsed:: true - _...this will fix a lot of issues with moving between blocks in edit mode..._ - DONE Q: can we place the cursor on item click where it was clicked before before the code expanded? collapsed:: true - for example - #### Click in this line and see where the cursor goes - _not sure how..._ - DONE click to select/edit node must retain click position in text... - DONE checkbox navigation via `alt-<arrow>` collapsed:: true - _might be a good idea to include also TODO/DONE navigation -- not yet sure how to mark undone blocks (i.e. the ones marked with TODO in Logseg)..._ - toggle with `space` - navigation auto-selects first checkbox - 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 "percentage complete" in parent blocks with todo's nested - DONE `.editor .outline:empty` view and behavior... - DONE editor: semi-live update styles - DONE do a better expand/collapse icons - DONE loading from DOM -- fill textarea - DONE focus management - DONE mouse/touch controls - DONE navigation - DONE expand/collapse subtree - DONE shift subtree up/down - DONE create node - DONE edit node - DONE serialize/deserialize - DONE add optional text styling to nodes - - ## Refactoring: - Q: Implementation: JSON-based, DOM-based (current) or both? - implement JSON-based - compare and decide... - Plugin architecture - DONE basic structure - plugin handler sequencing (see: `<editor>.setup(..)`) - DONE plugin handler canceling (see: `<editor>.runPlugins(..)`) - DONE Item parser (`.__code2html__(..)`) collapsed:: true - DONE split out - DONE define a way to extend/stack parsers - DONE Format parser/generator collapsed:: true - DONE split out - DONE define api (see: `<editor>.__code2text__(..) / <editor>.__text2code__(..)`) - CSS - DONE nested rules (experiment) - separate out settings - separate out theming - Actions -- move user actions (code in `.keyboard`) into methods - Move to `keyboard.js` - Q: do we need a concatenative API?? - `<block>.get() -> <block>` - Docs - - ## Docs - ### Use - Minimal ```html <outline-editor></outline-editor> ``` - Session-stored ```html <outline-editor session-storage="outline-text" value="initial text"></outline-editor> ``` - #### Attributes: - `value` - `session-storage` - `local-storage` - ### 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 | | c-left | prev checkbox | | c-right | next checkbox | | space | toggle checkbox | | a-s | toggle status | | a-x | toggle status DONE | | a-r | toggle status REJECT | | c-z | normal: undo | | c-s-z | normal: redo | | c | normal: crop current node | | enter | normal: edit node | | | edit: create node below | | esc | crop: exit crop | | | edit: exit edit mode | - ### Formatting - The formatting mostly adheres to the markdown spec with a few minor differences - - # Node styles: - # Automatic Headings - Heading level is determined by topology - # Heading - # Heading - # Heading - ... - NOTE: Heading created via `@` will not show up in index. - # Manual Headings (disabled) collapsed:: true - @ Heading 1 - @@ Heading 2 - @@@ Heading 3 - @@@@ Heading 4 - @@@@@ Heading 5 - @@@@@@ Heading 6 - # Text - # Lists:: - bullet: - a: collapsed:: true - bullets: - in: - very: - deep: - list: - of: - items: - b - c - numbered# - a - b# - x# collapsed:: true - bullets# - in# - very# - deep# - list# - of# - items# - y - z - c - # Quoting - > quote - # Notes - NOTE: a note text - NOTE: - a root note can also be empty - click on the outer border to edit root - # Comments - // C-style comment - ; ASM-style comment - XXX Highlight - # Status - Status of any node can be toggled via: `alt-s` - DONE Done (explicitly toggled via: `alt-x`) - REJECT Reject (explicitly toggled via: `alt-r`) - # Inline styles: - Basic inline *bold*, _italic_ and ~striked~ - Marking ==text== - # Code: - Inline quoting `html <b>code</b>` - code blocks ```javascript var text = 'Hello, world!' console.log(text) ``` - # Line - --- - # Markers: - ASAP, TEST, BUG, FIX, HACK, STUB, WARNING, and CAUTION - # links - [link](about:blank) - [local links](#attributes) - https://example.com - ./path/to/file /path/to -- _not supported yet_ - # Tables - | a | b | c | | 1 | 2 | 3 | | 11 | 22 | 33 | - # Symbols - _should these be ligatures?_ - (i), (c), /!\, ... - -- and --- - # Table of content - # Global - TOC - # Local - toc - demo content collapsed:: true - @ Heading - @ Heading - @ Heading - @ Heading - @ Heading - # Basic task management - [%] Completion status - Inline [X] checkboxes [_] - To do items/blocks - [_] undone item _(clicking the checkbox updates the item)_ - [X] done item - [_] we can also add inline [x] checkboxes and states: [%] - Navigating checkboxes in view mode can be done via `ctrl-left` / `ctrl-right` and toggling is done via `space` - Setting `ignore:: true` will not count the nested checkboxes in the parent status (see child checkboxes) collapsed:: true ignore:: true - [X] these [ ] checkboxes are ignored from the above summery status. - # Templating: - # Inline - TEMPLATE [ ] - [ ] example item - # Nested - TEMPLATE creates [below] - [ ] - [ ] example item - TEMPLATE^ creates [above] - [ ] - # Multiple nested - TEMPLATE [\ASAP] [ToDo] [Note] - [ ] ASAP - [ ] - - Q: should we include button text in item??? - # Attributes: id:: attributes - collapsed collapsed:: true - a - b - c - id id:: node-with-id - combined id:: combined-several-ids collapsed:: true - a - b - c - - --- - ### Playground for testing - Empty line tests:: - leading - trailing - both - ``` block with trailing empty line ``` - A collapsed:: true - a - b - c - B - d - e - C - This is a line of text - This is a set 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 -