added SHIFT + TAB behavior to decrease indent

This commit is contained in:
Gamosoft 2026-05-04 19:44:16 +02:00
parent bf48d80fd6
commit c80e164581
2 changed files with 101 additions and 4 deletions

View File

@ -33,7 +33,7 @@
- **Rename anything** - Files and folders, instantly
- **Visual tree view** - Expandable/collapsible navigation
- **Hide system folders** - Toggle to hide `_attachments`, `_templates` and other underscore-prefixed folders from sidebar
- **Tab inserts tab** - Toggle to make Tab key insert a tab character in the editor instead of changing focus
- **Tab inserts tab** - Toggle so Tab inserts a literal tab in the editor instead of changing focus; when this is on, Shift+Tab outdents (one leading tab or up to four leading spaces per line in the selection), and if nothing is removed, Shift+Tab still moves focus backward
- **List & quote continuation (Enter)** - At the end of a line, Enter continues blockquotes (`>`), bullets (`-` / `*` / `+`), ordered lists (`1.` / `1)`), and task lists (`- [ ]` / `- [x]`); pressing Enter again on an empty continued line exits the list or quote. Disabled inside fenced code blocks and when using Shift+Enter (plain newline)
### Export & Print

View File

@ -162,6 +162,89 @@ const FilenameValidator = {
}
};
/** Max leading spaces Shift+Tab removes per line when matching one “tab indent” vs \t inserts. */
const EDITOR_TAB_OUTDENT_MAX_SPACES = 4;
function editorIndentRemoveLen(lineSegment) {
if (!lineSegment || lineSegment.length === 0) return 0;
if (lineSegment.charCodeAt(0) === 9 /* \t */) return 1;
let i = 0;
const max = Math.min(EDITOR_TAB_OUTDENT_MAX_SPACES, lineSegment.length);
while (i < max && lineSegment[i] === ' ') i++;
return i;
}
function editorOutdentChunk(chunk) {
if (chunk.length === 0) return { newChunk: chunk, changed: false };
const lines = chunk.split('\n');
const newLines = lines.map((line) => {
const n = editorIndentRemoveLen(line);
return n > 0 ? line.slice(n) : line;
});
const newChunk = newLines.join('\n');
return { newChunk, changed: newChunk !== chunk };
}
function editorRemovedBeforeInChunk(chunk, rel) {
const target = Math.min(Math.max(rel, 0), chunk.length);
let off = 0;
let removed = 0;
while (off < chunk.length && off < target) {
const nl = chunk.indexOf('\n', off);
const lineEnd = nl === -1 ? chunk.length : nl;
const lineSlice = chunk.slice(off, lineEnd);
const n = editorIndentRemoveLen(lineSlice);
if (target >= lineEnd) {
removed += n;
if (nl === -1) break;
off = lineEnd + 1;
} else {
const col = target - off;
removed += Math.min(col, n);
break;
}
}
return removed;
}
function editorMapCaretAfterOutdent(chunk, newChunk, rel) {
const r = Math.max(0, rel);
if (r >= chunk.length) return newChunk.length;
return r - editorRemovedBeforeInChunk(chunk, r);
}
/**
* Shift+Tab outdent while tab inserts tab is enabled: remove one leading \t or up to 4 spaces per affected line.
* @returns {{ changed: boolean, text: string, selStart: number, selEnd: number }}
*/
function applyEditorOutdent(text, selStart, selEnd) {
const a = Math.min(selStart, selEnd);
const b = Math.max(selStart, selEnd);
const blockStart = text.lastIndexOf('\n', Math.max(0, a - 1)) + 1;
const ref = b > a ? b - 1 : a;
let blockEnd = text.indexOf('\n', ref);
if (blockEnd === -1) blockEnd = text.length;
const chunk = text.slice(blockStart, blockEnd);
const { newChunk, changed } = editorOutdentChunk(chunk);
if (!changed) {
return { changed: false, text, selStart, selEnd };
}
const newText = text.slice(0, blockStart) + newChunk + text.slice(blockEnd);
const anchorRel = a - blockStart;
const focusRel = b - blockStart;
const newA = blockStart + editorMapCaretAfterOutdent(chunk, newChunk, anchorRel);
const newB = blockStart + editorMapCaretAfterOutdent(chunk, newChunk, focusRel);
return {
changed: true,
text: newText,
selStart: Math.min(newA, newB),
selEnd: Math.max(newA, newB),
};
}
function noteApp() {
return {
// App state
@ -947,14 +1030,28 @@ function noteApp() {
localStorage.setItem('tabInsertsTab', this.tabInsertsTab);
},
// Handle Tab key in editor (inserts tab if setting enabled)
// Handle Tab key in editor (inserts tab if setting enabled; Shift+Tab outdents matching lines)
handleTabKey(event) {
if (!this.tabInsertsTab) return;
event.preventDefault();
const textarea = event.target;
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
if (event.shiftKey) {
const result = applyEditorOutdent(this.noteContent, start, end);
if (!result.changed) return;
event.preventDefault();
this.noteContent = result.text;
this.$nextTick(() => {
textarea.selectionStart = result.selStart;
textarea.selectionEnd = result.selEnd;
});
this.autoSave();
return;
}
event.preventDefault();
this.noteContent = this.noteContent.substring(0, start) + '\t' + this.noteContent.substring(end);
this.$nextTick(() => {
textarea.selectionStart = textarea.selectionEnd = start + 1;