feat(code): adjustable default tab width

This commit is contained in:
Elian Doran
2026-04-14 23:33:32 +03:00
parent f7a36fc997
commit 54a6e3d9a1
9 changed files with 53 additions and 9 deletions

View File

@@ -1268,7 +1268,9 @@
"unit": "characters"
},
"code-editor-options": {
"title": "Editor"
"title": "Editor",
"tab_width": "Tab width",
"tab_width_unit": "spaces"
},
"code_mime_types": {
"title": "Available MIME types in the dropdown",

View File

@@ -146,6 +146,7 @@ export function CodeEditor({ parentComponent, ntxId, containerRef: externalConta
const initialized = useRef($.Deferred());
const [ codeLineWrapEnabled ] = useTriliumOptionBool("codeLineWrapEnabled");
const [ codeNoteTheme ] = useTriliumOption("codeNoteTheme");
const [ codeNoteTabWidth ] = useTriliumOption("codeNoteTabWidth");
// React to background color.
const [ backgroundColor, setBackgroundColor ] = useState<string>();
@@ -200,6 +201,7 @@ export function CodeEditor({ parentComponent, ntxId, containerRef: externalConta
editorRef={codeEditorRef}
containerRef={containerRef}
lineWrapping={lineWrapping ?? codeLineWrapEnabled}
indentSize={parseInt(codeNoteTabWidth) || 4}
onInitialized={() => {
if (externalContainerRef && containerRef.current) {
externalContainerRef.current = containerRef.current;

View File

@@ -49,6 +49,13 @@ export default function CodeMirror({ className, content, mime, editorRef: extern
// React to line wrapping.
useEffect(() => codeEditorRef.current?.setLineWrapping(!!lineWrapping), [ lineWrapping ]);
// React to indent size changes.
useEffect(() => {
if (extraOpts.indentSize != null) {
codeEditorRef.current?.setIndentSize(extraOpts.indentSize);
}
}, [ extraOpts.indentSize ]);
return (
<pre ref={parentRef} className={className} />
)

View File

@@ -21,11 +21,12 @@ const SAMPLE_MIME = "application/typescript";
export default function CodeNoteSettings() {
const [codeLineWrapEnabled, setCodeLineWrapEnabled] = useTriliumOptionBool("codeLineWrapEnabled");
const [codeNoteTabWidth] = useTriliumOption("codeNoteTabWidth");
return (
<>
<Editor wordWrapping={codeLineWrapEnabled} setWordWrapping={setCodeLineWrapEnabled} />
<Appearance wordWrapping={codeLineWrapEnabled} />
<Appearance wordWrapping={codeLineWrapEnabled} indentSize={parseInt(codeNoteTabWidth) || 4} />
<CodeMimeTypes />
</>
);
@@ -39,6 +40,7 @@ interface EditorProps {
function Editor({ wordWrapping, setWordWrapping }: EditorProps) {
const [vimKeymapEnabled, setVimKeymapEnabled] = useTriliumOptionBool("vimKeymapEnabled");
const [autoReadonlySize, setAutoReadonlySize] = useTriliumOption("autoReadonlySizeCode");
const [codeNoteTabWidth, setCodeNoteTabWidth] = useTriliumOption("codeNoteTabWidth");
return (
<OptionsSection title={t("code-editor-options.title")}>
@@ -49,6 +51,17 @@ function Editor({ wordWrapping, setWordWrapping }: EditorProps) {
onChange={setWordWrapping}
/>
{/* Avoid using "code" in the name of numeric inputs to prevent KeepassXC from triggering. */}
<OptionsRow name="editor-tab-width" label={t("code-editor-options.tab_width")}>
<FormTextBoxWithUnit
type="number" min={1} max={16} step={1}
unit={t("code-editor-options.tab_width_unit")}
currentValue={codeNoteTabWidth}
onChange={setCodeNoteTabWidth}
onBlur={setCodeNoteTabWidth}
/>
</OptionsRow>
<OptionsRow name="source-readonly-threshold" label={t("code_auto_read_only_size.label")} description={t("text_auto_read_only_size.description")}>
<FormTextBoxWithUnit
type="number" min={0}
@@ -71,9 +84,10 @@ function Editor({ wordWrapping, setWordWrapping }: EditorProps) {
interface AppearanceProps {
wordWrapping: boolean;
indentSize: number;
}
function Appearance({ wordWrapping }: AppearanceProps) {
function Appearance({ wordWrapping, indentSize }: AppearanceProps) {
const [codeNoteTheme, setCodeNoteTheme] = useTriliumOption("codeNoteTheme");
const themes = useMemo(() => {
@@ -93,12 +107,12 @@ function Appearance({ wordWrapping }: AppearanceProps) {
/>
</OptionsRow>
<CodeNotePreview wordWrapping={wordWrapping} themeName={codeNoteTheme} />
<CodeNotePreview wordWrapping={wordWrapping} themeName={codeNoteTheme} indentSize={indentSize} />
</OptionsSection>
);
}
function CodeNotePreview({ themeName, wordWrapping }: { themeName: string, wordWrapping: boolean }) {
function CodeNotePreview({ themeName, wordWrapping, indentSize }: { themeName: string, wordWrapping: boolean, indentSize: number }) {
const editorRef = useRef<CodeMirror>(null);
const containerRef = useRef<HTMLDivElement>(null);
@@ -124,6 +138,10 @@ function CodeNotePreview({ themeName, wordWrapping }: { themeName: string, wordW
editorRef.current?.setLineWrapping(wordWrapping);
}, [ wordWrapping ]);
useEffect(() => {
editorRef.current?.setIndentSize(indentSize);
}, [ indentSize ]);
useEffect(() => {
if (themeName?.startsWith(DEFAULT_PREFIX)) {
const theme = getThemeById(themeName.substring(DEFAULT_PREFIX.length));

View File

@@ -286,12 +286,14 @@ function CodeBlockStyle() {
onChange={setCodeBlockWordWrap}
/>
<OptionsRow name="code-block-tab-width" label={t("code_block.tab_width")}>
{/* Avoid using "code" in the name of numeric inputs to prevent KeepassXC from triggering. */}
<OptionsRow name="block-tab-width" label={t("code_block.tab_width")}>
<FormTextBoxWithUnit
type="number" min={1} max={16} step={1}
unit={t("code_block.tab_width_unit")}
currentValue={codeBlockTabWidth}
onChange={setCodeBlockTabWidth}
onBlur={setCodeBlockTabWidth}
/>
</OptionsRow>
@@ -336,7 +338,7 @@ function CodeBlockPreview({ theme, wordWrap, tabWidth }: { theme: string, wordWr
}
}, [theme]);
const codeStyle = useMemo<CSSProperties>(() => {
const codeStyle: CSSProperties = useMemo(() => {
return {
whiteSpace: wordWrap ? "pre-wrap" : "pre",
tabSize: tabWidth ?? "4"
@@ -416,4 +418,3 @@ export function HighlightsListOptions() {
</>
);
}

View File

@@ -32,6 +32,7 @@ const ALLOWED_OPTIONS = new Set<OptionNames>([
"codeBlockWordWrap",
"codeBlockTabWidth",
"codeNoteTheme",
"codeNoteTabWidth",
"syncServerHost",
"syncServerTimeout",
"syncServerTimeoutTimeScale",

View File

@@ -131,6 +131,7 @@ const defaultOptions: DefaultOption[] = [
{ name: "autoFixConsistencyIssues", value: "true", isSynced: false },
{ name: "vimKeymapEnabled", value: "false", isSynced: false },
{ name: "codeLineWrapEnabled", value: "true", isSynced: false },
{ name: "codeNoteTabWidth", value: "4", isSynced: true },
{
name: "codeNotesMimeTypes",
value: '["text/x-csrc","text/x-c++src","text/x-csharp","text/css","text/x-elixir","text/x-go","text/x-groovy","text/x-haskell","text/html","message/http","text/x-java","application/javascript;env=frontend","application/javascript;env=backend","application/json","text/x-kotlin","text/x-markdown","text/x-perl","text/x-php","text/x-python","text/x-ruby",null,"text/x-sql","text/x-sqlite;schema=trilium","text/x-swift","text/xml","text/x-yaml","text/x-sh","application/typescript"]',

View File

@@ -34,6 +34,8 @@ export interface EditorConfig {
/** Disables some of the nice-to-have features (bracket matching, syntax highlighting, indentation markers) in order to improve performance. */
preferPerformance?: boolean;
tabIndex?: number;
/** The number of spaces used for indentation. Defaults to 4. */
indentSize?: number;
onContentChanged?: ContentChangedListener;
}
@@ -44,6 +46,7 @@ export default class CodeMirror extends EditorView {
private historyCompartment: Compartment;
private themeCompartment: Compartment;
private lineWrappingCompartment: Compartment;
private indentUnitCompartment: Compartment;
private searchHighlightCompartment: Compartment;
private searchPlugin?: SearchHighlighter | null;
@@ -52,6 +55,7 @@ export default class CodeMirror extends EditorView {
const historyCompartment = new Compartment();
const themeCompartment = new Compartment();
const lineWrappingCompartment = new Compartment();
const indentUnitCompartment = new Compartment();
const searchHighlightCompartment = new Compartment();
let extensions: Extension[] = [];
@@ -68,7 +72,7 @@ export default class CodeMirror extends EditorView {
searchHighlightCompartment.of([]),
highlightActiveLine(),
lineNumbers(),
indentUnit.of(" ".repeat(4)),
indentUnitCompartment.of(indentUnit.of(" ".repeat(config.indentSize ?? 4))),
keymap.of([
...preventCtrlEnterKeymap,
...defaultKeymap,
@@ -121,6 +125,7 @@ export default class CodeMirror extends EditorView {
this.historyCompartment = historyCompartment;
this.themeCompartment = themeCompartment;
this.lineWrappingCompartment = lineWrappingCompartment;
this.indentUnitCompartment = indentUnitCompartment;
this.searchHighlightCompartment = searchHighlightCompartment;
}
@@ -168,6 +173,12 @@ export default class CodeMirror extends EditorView {
});
}
setIndentSize(size: number) {
this.dispatch({
effects: [ this.indentUnitCompartment.reconfigure(indentUnit.of(" ".repeat(size))) ]
});
}
/**
* Clears the history of undo/redo. Generally useful when changing to a new document.
*/

View File

@@ -161,6 +161,7 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions<KeyboardActi
editedNotesOpenInRibbon: boolean;
codeBlockWordWrap: boolean;
codeBlockTabWidth: number;
codeNoteTabWidth: number;
textNoteEditorMultilineToolbar: boolean;
/** Whether keyboard auto-completion for emojis is triggered when typing `:`. */
textNoteEmojiCompletionEnabled: boolean;