feat: add a new independent decorator mode

Resolve: #22
BREAKING CHANGE: adjust some settings, introduce a new decorator mode setting.
This commit is contained in:
dragonish 2026-01-09 13:04:07 +08:00
parent 9e98a8edea
commit c0bd1e15aa
No known key found for this signature in database
GPG key ID: 6F42FA9E807A5177
16 changed files with 2526 additions and 1555 deletions

View file

@ -1,5 +1,9 @@
module.exports = {
ui: "bdd",
spec: ["test/**/**.spec.ts"],
spec: [
"test/common/data.spec.ts",
"test/common/heading.spec.ts",
"test/**/**.spec.ts",
],
import: "tsx",
};

View file

@ -1,5 +1,9 @@
import type { BaseDecoratorOptions, DecoratorOptions } from "./data";
import { defaultHeadingTuple } from "./data";
import type {
OrderedDecoratorOptions,
IndependentDecoratorOptions,
IndependentDecoratorSettings,
} from "./data";
import { getOrderedCustomIdents } from "./data";
import * as presets from "@jsamr/counter-style/presets";
export class Querier {
@ -69,47 +73,12 @@ export class Querier {
}
}
export class Counter extends Querier {
private decoratorOptions: DecoratorOptions;
constructor(decoratorOptions?: BaseDecoratorOptions) {
export class OrderedCounter extends Querier implements Counter {
constructor(private decoratorOptions: OrderedDecoratorOptions) {
super(decoratorOptions?.allowZeroLevel, decoratorOptions?.maxRecLevel);
if (decoratorOptions) {
if (decoratorOptions.ordered) {
const {
styleType,
delimiter,
trailingDelimiter,
customTrailingDelimiter,
leadingDelimiter,
customLeadingDelimiter,
customIdents,
specifiedString,
ignoreTopLevel,
} = decoratorOptions;
this.decoratorOptions = {
ordered: true,
styleType: styleType || "decimal",
delimiter,
trailingDelimiter,
customTrailingDelimiter,
leadingDelimiter,
customLeadingDelimiter,
customIdents,
specifiedString,
ignoreTopLevel,
};
} else {
this.decoratorOptions = {
ordered: false,
levelHeadings: decoratorOptions.levelHeadings || [
...defaultHeadingTuple,
],
};
}
} else {
this.decoratorOptions = { ordered: true, styleType: "decimal" }; // Default to ordered if no options are provided
if (!decoratorOptions.styleType) {
this.decoratorOptions.styleType = "decimal"; // Default to ordered if no options are provided
}
}
@ -119,10 +88,9 @@ export class Counter extends Querier {
return result;
}
if (this.decoratorOptions.ordered) {
let results: string[] = [];
const {
styleType,
styleType = "decimal",
delimiter = ".",
trailingDelimiter = false,
customTrailingDelimiter = "",
@ -158,10 +126,122 @@ export class Counter extends Querier {
result +
(trailingDelimiter ? customTrailingDelimiter || delimiter : "");
}
return result;
}
}
export class IndependentCounter extends Querier implements Counter {
constructor(private decoratorOptions: IndependentDecoratorOptions) {
super(decoratorOptions?.allowZeroLevel, decoratorOptions?.maxRecLevel);
}
decorator(level: number): string {
let result = "";
if (level < 1 || level > this.maxRecLevel) {
return result;
}
let results: string[] = [];
let used: Partial<IndependentDecoratorSettings>;
const {
h1 = {},
h2 = {},
h3 = {},
h4 = {},
h5 = {},
h6 = {},
orderedRecLevel = 6,
ignoreTopLevel = 0,
} = this.decoratorOptions;
let levels = this.handler(level).slice(ignoreTopLevel);
if (levels.length === 0) {
return result;
}
const current = Math.min(level, orderedRecLevel);
switch (current) {
case 6:
used = h6;
break;
case 5:
used = h5;
break;
case 4:
used = h4;
break;
case 3:
used = h3;
break;
case 2:
used = h2;
break;
case 1:
default:
used = h1;
break;
}
if (level <= current) {
levels = levels.slice(-1);
} else {
result = this.decoratorOptions.levelHeadings[level - 1];
levels = levels.slice(current - ignoreTopLevel - 1);
}
const {
styleType = "decimal",
delimiter = ".",
trailingDelimiter = false,
customTrailingDelimiter = "",
leadingDelimiter = false,
customLeadingDelimiter = "",
customIdents = "",
specifiedString = "#",
} = used;
switch (styleType) {
case "customIdent":
results = levels.map((level) => {
const ci = getOrderedCustomIdents(customIdents);
return level > ci.length ? level.toString() : ci[level - 1];
});
break;
case "string":
results = levels.map(() => specifiedString.trim() || "#");
break;
default:
results = levels.map((level) =>
presets[styleType].renderCounter(level)
);
break;
}
result = results.join(delimiter);
if (result.length > 0) {
result =
(leadingDelimiter ? customLeadingDelimiter || delimiter : "") +
result +
(trailingDelimiter ? customTrailingDelimiter || delimiter : "");
}
return result;
}
}
export class UnorderedCounter implements Counter {
constructor(private levelHeadings: HeadingTuple, private maxRecLevel = 6) {}
decorator(level: number): string {
let result = "";
if (level < 1 || level > this.maxRecLevel) {
return result;
}
result = this.levelHeadings[level - 1];
return result;
}
handler(): void {
//* do nothing.
}
}

View file

@ -9,8 +9,7 @@ export type OrderedCounterStyleType =
| "customIdent"
| "string";
export interface BaseDecoratorOptions {
ordered: boolean;
export interface OrderedDecoratorOptions {
styleType?: OrderedCounterStyleType;
delimiter?: string;
trailingDelimiter?: boolean;
@ -20,41 +19,60 @@ export interface BaseDecoratorOptions {
customIdents?: string[];
specifiedString?: string;
ignoreTopLevel?: number;
levelHeadings?: HeadingTuple;
allowZeroLevel?: boolean;
maxRecLevel?: number;
}
interface OrderedDecoratorOptions {
ordered: true;
export interface IndependentDecoratorSettings {
styleType: OrderedCounterStyleType;
delimiter?: string;
trailingDelimiter?: boolean;
customTrailingDelimiter?: string;
leadingDelimiter?: boolean;
customLeadingDelimiter?: string;
customIdents?: string[];
specifiedString?: string;
delimiter: string;
trailingDelimiter: boolean;
customTrailingDelimiter: string;
leadingDelimiter: boolean;
customLeadingDelimiter: string;
customIdents: string;
specifiedString: string;
}
export interface IndependentDecoratorOptions {
maxRecLevel?: number;
allowZeroLevel?: boolean;
ignoreTopLevel?: number;
orderedRecLevel?: number;
h1?: Partial<IndependentDecoratorSettings>;
h2?: Partial<IndependentDecoratorSettings>;
h3?: Partial<IndependentDecoratorSettings>;
h4?: Partial<IndependentDecoratorSettings>;
h5?: Partial<IndependentDecoratorSettings>;
h6?: Partial<IndependentDecoratorSettings>;
}
interface UnorderedDecoratorOptions {
ordered: false;
levelHeadings: HeadingTuple;
}
interface IndependentSettings {
orderedRecLevel: number;
export type DecoratorOptions =
| OrderedDecoratorOptions
| UnorderedDecoratorOptions;
h1: IndependentDecoratorSettings;
h2: IndependentDecoratorSettings;
h3: IndependentDecoratorSettings;
h4: IndependentDecoratorSettings;
h5: IndependentDecoratorSettings;
h6: IndependentDecoratorSettings;
}
export interface HeadingDecoratorSettings {
enabledInEachNote?: boolean;
ordered: boolean;
decoratorMode?: DecoratorMode;
opacity: OpacityOptions;
position: PostionOptions;
maxRecLevel?: number;
orderedAllowZeroLevel?: boolean;
orderedBasedOnExisting?: boolean;
orderedAlwaysIgnore?: boolean;
orderedIgnoreMaximum?: number;
orderedIgnoreSingle: boolean;
orderedStyleType: OrderedCounterStyleType;
orderedDelimiter: string;
orderedTrailingDelimiter: boolean;
@ -63,11 +81,8 @@ export interface HeadingDecoratorSettings {
orderedCustomLeadingDelimiter?: string;
orderedCustomIdents: string;
orderedSpecifiedString: string;
orderedAllowZeroLevel?: boolean;
orderedBasedOnExisting?: boolean;
orderedAlwaysIgnore?: boolean;
orderedIgnoreSingle: boolean;
orderedIgnoreMaximum?: number;
independentSettings?: IndependentSettings;
unorderedLevelHeadings: string;
}
@ -131,6 +146,8 @@ export const className = {
after: "after-heading-decorator",
afterInside: "after-inside-heading-decorator",
hideSourceNumberSigns: "hide-source-number-signs",
settingContainer: "heading-decorator-setting-container",
settingItem: "heading-decorator-setting-item",
};
export const headingsSelector =
@ -143,6 +160,38 @@ export const defaultHeadingTuple: HeadingTuple = [
"H5",
"H6",
];
const defaultCustomidents =
"Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ";
function defaultIndependentDecoratorSettings(): IndependentDecoratorSettings {
return {
styleType: "decimal",
delimiter: ".",
trailingDelimiter: false,
customTrailingDelimiter: "",
leadingDelimiter: false,
customLeadingDelimiter: "",
customIdents: defaultCustomidents,
specifiedString: "#",
};
}
/**
* Defaults independent settings
*
* @returns independent settings
*/
export function defaultIndependentSettings(): IndependentSettings {
return {
orderedRecLevel: 6,
h1: defaultIndependentDecoratorSettings(),
h2: defaultIndependentDecoratorSettings(),
h3: defaultIndependentDecoratorSettings(),
h4: defaultIndependentDecoratorSettings(),
h5: defaultIndependentDecoratorSettings(),
h6: defaultIndependentDecoratorSettings(),
};
}
/**
* Default settings for heading decorator.
@ -155,20 +204,21 @@ export function defaultHeadingDecoratorSettings(): HeadingDecoratorSettings {
opacity: 20,
position: "before",
maxRecLevel: 6,
ordered: true,
decoratorMode: "orderd",
orderedDelimiter: ".",
orderedTrailingDelimiter: false,
orderedCustomTrailingDelimiter: "",
orderedLeadingDelimiter: false,
orderedCustomLeadingDelimiter: "",
orderedStyleType: "decimal",
orderedCustomIdents: "Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ",
orderedCustomIdents: defaultCustomidents,
orderedSpecifiedString: "#",
orderedAllowZeroLevel: false,
orderedBasedOnExisting: false,
orderedAlwaysIgnore: false,
orderedIgnoreSingle: false,
orderedIgnoreMaximum: 6,
independentSettings: defaultIndependentSettings(),
unorderedLevelHeadings: defaultHeadingTuple.join(" "),
};
}

View file

@ -15,7 +15,12 @@ import {
getOrderedCustomIdents,
findFirstCharacterIndex,
} from "../common/data";
import { Counter, Querier } from "../common/counter";
import {
Querier,
UnorderedCounter,
OrderedCounter,
IndependentCounter,
} from "../common/counter";
import { Heading } from "../common/heading";
/** A StateEffect for updating decorations */
@ -98,7 +103,7 @@ export class HeadingEditorViewPlugin implements PluginValue {
const doc = view.state.doc;
const {
ordered,
decoratorMode = "orderd",
position,
opacity,
maxRecLevel,
@ -116,12 +121,19 @@ export class HeadingEditorViewPlugin implements PluginValue {
orderedBasedOnExisting,
orderedAllowZeroLevel,
unorderedLevelHeadings,
independentSettings,
} = isLivePreviwMode
? pluginData.previewSettings
: pluginData.sourceSettings;
let counter: Counter;
if (decoratorMode === "unordered") {
counter = new UnorderedCounter(
getUnorderedLevelHeadings(unorderedLevelHeadings),
maxRecLevel
);
} else {
let ignoreTopLevel = 0;
if (ordered) {
const ignoreSingle = !orderedAlwaysIgnore && orderedIgnoreSingle;
const ignoreLimit = orderedAlwaysIgnore ? orderedIgnoreMaximum : 0;
if (ignoreSingle || orderedBasedOnExisting) {
@ -149,11 +161,25 @@ export class HeadingEditorViewPlugin implements PluginValue {
if (ignoreTopLevel < ignoreLimit) {
ignoreTopLevel = ignoreLimit;
}
}
const counter = new Counter({
ordered,
if (decoratorMode === "independent") {
counter = new IndependentCounter({
maxRecLevel,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
orderedRecLevel: independentSettings?.orderedRecLevel,
h1: independentSettings?.h1,
h2: independentSettings?.h2,
h3: independentSettings?.h3,
h4: independentSettings?.h4,
h5: independentSettings?.h5,
h6: independentSettings?.h6,
});
} else {
counter = new OrderedCounter({
maxRecLevel,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
styleType: orderedStyleType,
delimiter: orderedDelimiter,
trailingDelimiter: orderedTrailingDelimiter,
@ -162,10 +188,9 @@ export class HeadingEditorViewPlugin implements PluginValue {
customLeadingDelimiter: orderedCustomLeadingDelimiter,
customIdents: getOrderedCustomIdents(orderedCustomIdents),
specifiedString: orderedSpecifiedString,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
levelHeadings: getUnorderedLevelHeadings(unorderedLevelHeadings),
});
}
}
const heading = new Heading();
for (let lineIndex = 1; lineIndex <= doc.lines; lineIndex++) {

View file

@ -5,7 +5,12 @@ import {
getOrderedCustomIdents,
getUnorderedLevelHeadings,
} from "../common/data";
import { Querier, Counter } from "../common/counter";
import {
Querier,
UnorderedCounter,
OrderedCounter,
IndependentCounter,
} from "../common/counter";
/**
* File Explorer Heading Decorator.
@ -22,9 +27,9 @@ export function fileExplorerHandler(
cacheHeadings: HeadingCache[]
): void {
const {
decoratorMode = "orderd",
opacity,
position,
ordered,
maxRecLevel,
orderedDelimiter,
orderedTrailingDelimiter,
@ -40,12 +45,19 @@ export function fileExplorerHandler(
orderedBasedOnExisting,
orderedAllowZeroLevel,
unorderedLevelHeadings,
independentSettings,
} = settings;
container.classList.add(className.fileExplorerContainer);
let counter: Counter;
if (decoratorMode === "unordered") {
counter = new UnorderedCounter(
getUnorderedLevelHeadings(unorderedLevelHeadings),
maxRecLevel
);
} else {
let ignoreTopLevel = 0;
if (ordered) {
const ignoreSingle = !orderedAlwaysIgnore && orderedIgnoreSingle;
const ignoreLimit = orderedAlwaysIgnore ? orderedIgnoreMaximum : 0;
if (ignoreSingle || orderedBasedOnExisting) {
@ -61,11 +73,25 @@ export function fileExplorerHandler(
if (ignoreTopLevel < ignoreLimit) {
ignoreTopLevel = ignoreLimit;
}
}
const counter = new Counter({
ordered,
if (decoratorMode === "independent") {
counter = new IndependentCounter({
maxRecLevel,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
orderedRecLevel: independentSettings?.orderedRecLevel,
h1: independentSettings?.h1,
h2: independentSettings?.h2,
h3: independentSettings?.h3,
h4: independentSettings?.h4,
h5: independentSettings?.h5,
h6: independentSettings?.h6,
});
} else {
counter = new OrderedCounter({
maxRecLevel,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
delimiter: orderedDelimiter,
trailingDelimiter: orderedTrailingDelimiter,
customTrailingDelimiter: orderedCustomTrailingDelimiter,
@ -74,10 +100,9 @@ export function fileExplorerHandler(
styleType: orderedStyleType,
customIdents: getOrderedCustomIdents(orderedCustomIdents),
specifiedString: orderedSpecifiedString,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
levelHeadings: getUnorderedLevelHeadings(unorderedLevelHeadings),
});
}
}
const marginMultiplier =
parseInt(

View file

@ -7,7 +7,12 @@ import {
diffLevel,
compareMarkdownText,
} from "../common/data";
import { Querier, Counter } from "../common/counter";
import {
Querier,
UnorderedCounter,
OrderedCounter,
IndependentCounter,
} from "../common/counter";
/**
* Handle the outline rendering based on the given settings and heading elements.
@ -24,9 +29,9 @@ export function outlineHandler(
cacheHeadings: HeadingCache[]
): void {
const {
decoratorMode = "orderd",
opacity,
position,
ordered,
maxRecLevel,
orderedDelimiter,
orderedTrailingDelimiter,
@ -42,12 +47,19 @@ export function outlineHandler(
orderedBasedOnExisting,
orderedAllowZeroLevel,
unorderedLevelHeadings,
independentSettings,
} = settings;
container.classList.add(className.outlineContainer);
let counter: Counter;
if (decoratorMode === "unordered") {
counter = new UnorderedCounter(
getUnorderedLevelHeadings(unorderedLevelHeadings),
maxRecLevel
);
} else {
let ignoreTopLevel = 0;
if (ordered) {
const ignoreSingle = !orderedAlwaysIgnore && orderedIgnoreSingle;
const ignoreLimit = orderedAlwaysIgnore ? orderedIgnoreMaximum : 0;
if (ignoreSingle || orderedBasedOnExisting) {
@ -63,11 +75,25 @@ export function outlineHandler(
if (ignoreTopLevel < ignoreLimit) {
ignoreTopLevel = ignoreLimit;
}
}
const counter = new Counter({
ordered,
if (decoratorMode === "independent") {
counter = new IndependentCounter({
maxRecLevel,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
orderedRecLevel: independentSettings?.orderedRecLevel,
h1: independentSettings?.h1,
h2: independentSettings?.h2,
h3: independentSettings?.h3,
h4: independentSettings?.h4,
h5: independentSettings?.h5,
h6: independentSettings?.h6,
});
} else {
counter = new OrderedCounter({
maxRecLevel,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
delimiter: orderedDelimiter,
trailingDelimiter: orderedTrailingDelimiter,
customTrailingDelimiter: orderedCustomTrailingDelimiter,
@ -76,10 +102,9 @@ export function outlineHandler(
styleType: orderedStyleType,
customIdents: getOrderedCustomIdents(orderedCustomIdents),
specifiedString: orderedSpecifiedString,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
levelHeadings: getUnorderedLevelHeadings(unorderedLevelHeadings),
});
}
}
let lastCacheLevel = 0;
let lastReadLevel = 0;

View file

@ -214,8 +214,10 @@ export class HeadingPlugin extends Plugin {
return;
}
const { ordered } = readingSettings;
if (ordered) {
const { decoratorMode = "orderd" } = readingSettings;
if (decoratorMode === "unordered") {
readingUnorderedHandler(readingSettings, headingElements);
} else {
let sourceContent = fileData;
if (!sourceContent) {
const file = this.getActiveFile(cxt.sourcePath);
@ -238,8 +240,6 @@ export class HeadingPlugin extends Plugin {
headingElements,
sourceArr
);
} else {
readingUnorderedHandler(readingSettings, headingElements);
}
} else {
cancelHTMLDecorator(container);

View file

@ -4,7 +4,12 @@ import {
getOrderedCustomIdents,
getUnorderedLevelHeadings,
} from "../common/data";
import { Querier, Counter } from "../common/counter";
import {
Querier,
UnorderedCounter,
OrderedCounter,
IndependentCounter,
} from "../common/counter";
/**
* Handles the quiet outline for a given set of heading elements.
@ -19,9 +24,9 @@ export function quietOutlineHandler(
headingElements: NodeListOf<HTMLElement>
): void {
const {
decoratorMode = "orderd",
opacity,
position,
ordered,
maxRecLevel,
orderedDelimiter,
orderedTrailingDelimiter,
@ -37,12 +42,19 @@ export function quietOutlineHandler(
orderedBasedOnExisting,
orderedAllowZeroLevel,
unorderedLevelHeadings,
independentSettings,
} = settings;
container.classList.add(className.quietOutlineContainer);
let counter: Counter;
if (decoratorMode === "unordered") {
counter = new UnorderedCounter(
getUnorderedLevelHeadings(unorderedLevelHeadings),
maxRecLevel
);
} else {
let ignoreTopLevel = 0;
if (ordered) {
const ignoreSingle = !orderedAlwaysIgnore && orderedIgnoreSingle;
const ignoreLimit = orderedAlwaysIgnore ? orderedIgnoreMaximum : 0;
if (ignoreSingle || orderedBasedOnExisting) {
@ -61,11 +73,25 @@ export function quietOutlineHandler(
if (ignoreTopLevel < ignoreLimit) {
ignoreTopLevel = ignoreLimit;
}
}
const counter = new Counter({
ordered,
if (decoratorMode === "independent") {
counter = new IndependentCounter({
maxRecLevel,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
orderedRecLevel: independentSettings?.orderedRecLevel,
h1: independentSettings?.h1,
h2: independentSettings?.h2,
h3: independentSettings?.h3,
h4: independentSettings?.h4,
h5: independentSettings?.h5,
h6: independentSettings?.h6,
});
} else {
counter = new OrderedCounter({
maxRecLevel,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
delimiter: orderedDelimiter,
trailingDelimiter: orderedTrailingDelimiter,
customTrailingDelimiter: orderedCustomTrailingDelimiter,
@ -74,10 +100,9 @@ export function quietOutlineHandler(
styleType: orderedStyleType,
customIdents: getOrderedCustomIdents(orderedCustomIdents),
specifiedString: orderedSpecifiedString,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
levelHeadings: getUnorderedLevelHeadings(unorderedLevelHeadings),
});
}
}
headingElements.forEach((headingEle) => {
const level = queryHeadingLevelByQuietOutlineElement(headingEle);

View file

@ -7,7 +7,12 @@ import {
getUnorderedLevelHeadings,
} from "../common/data";
import { Heading } from "../common/heading";
import { Querier, Counter } from "../common/counter";
import {
Querier,
UnorderedCounter,
OrderedCounter,
IndependentCounter,
} from "../common/counter";
/**
* Handles ordered headings of reading view.
@ -24,6 +29,7 @@ export function readingOrderedHandler(
sourceArr: string[]
): void {
const {
decoratorMode = "orderd",
opacity,
position,
maxRecLevel,
@ -40,6 +46,7 @@ export function readingOrderedHandler(
orderedAlwaysIgnore,
orderedBasedOnExisting,
orderedAllowZeroLevel,
independentSettings,
} = settings;
let ignoreTopLevel = 0;
@ -70,9 +77,25 @@ export function readingOrderedHandler(
ignoreTopLevel = ignoreLimit;
}
const counter = new Counter({
ordered: true,
let counter: Counter;
if (decoratorMode === "independent") {
counter = new IndependentCounter({
maxRecLevel,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
orderedRecLevel: independentSettings?.orderedRecLevel,
h1: independentSettings?.h1,
h2: independentSettings?.h2,
h3: independentSettings?.h3,
h4: independentSettings?.h4,
h5: independentSettings?.h5,
h6: independentSettings?.h6,
});
} else {
counter = new OrderedCounter({
maxRecLevel,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
delimiter: orderedDelimiter,
trailingDelimiter: orderedTrailingDelimiter,
customTrailingDelimiter: orderedCustomTrailingDelimiter,
@ -81,9 +104,9 @@ export function readingOrderedHandler(
styleType: orderedStyleType,
customIdents: getOrderedCustomIdents(orderedCustomIdents),
specifiedString: orderedSpecifiedString,
ignoreTopLevel,
allowZeroLevel: orderedAllowZeroLevel,
});
}
const heading = new Heading();
let headingIndex = 1;
@ -141,11 +164,10 @@ export function readingUnorderedHandler(
): void {
const { opacity, position, maxRecLevel, unorderedLevelHeadings } = settings;
const counter = new Counter({
ordered: false,
maxRecLevel,
levelHeadings: getUnorderedLevelHeadings(unorderedLevelHeadings),
});
const counter = new UnorderedCounter(
getUnorderedLevelHeadings(unorderedLevelHeadings),
maxRecLevel
);
headingElements.forEach((headingElement) => {
const level = queryHeadingLevelByElement(headingElement);

View file

@ -1,6 +1,11 @@
import { App, ButtonComponent, PluginSettingTab, Setting } from "obsidian";
import type { HeadingPlugin } from "./plugin";
import type { OrderedCounterStyleType } from "../common/data";
import type {
OrderedCounterStyleType,
HeadingDecoratorSettings,
IndependentDecoratorSettings,
} from "../common/data";
import { className, defaultIndependentSettings } from "../common/data";
import { orderedStyleTypeOptions } from "../common/options";
import { FolderSuggest } from "./suggest";
@ -70,12 +75,19 @@ export class HeadingSettingTab extends PluginSettingTab {
.addToggle((toggle) =>
toggle.setValue(settings.enabledInReading).onChange((value) => {
settings.enabledInReading = value;
value
? readingConfigContainerEl.show()
: readingConfigContainerEl.hide();
this.plugin.saveSettings();
})
);
const readingConfigContainerEl = containerEl.createDiv(
className.settingItem
);
let readingConfigBtn: ButtonOrUndefined;
new Setting(containerEl)
new Setting(readingConfigContainerEl)
.setName(i18n.t("setting.enabledInReadingConfig"))
.setDesc(i18n.t("setting.enabledInReadingConfigDesc"))
.addToggle((toggle) => {
@ -96,7 +108,7 @@ export class HeadingSettingTab extends PluginSettingTab {
});
//* readingRenderPolicy
new Setting(containerEl)
new Setting(readingConfigContainerEl)
.setName(i18n.t("setting.renderPolicy"))
.setDesc(i18n.t("setting.renderPolicyDesc"))
.addDropdown((dropdown) => {
@ -114,6 +126,10 @@ export class HeadingSettingTab extends PluginSettingTab {
});
});
if (!settings.enabledInReading) {
readingConfigContainerEl.hide();
}
new Setting(containerEl)
.setName(i18n.t("setting.livePreview"))
.setHeading();
@ -125,12 +141,19 @@ export class HeadingSettingTab extends PluginSettingTab {
.addToggle((toggle) =>
toggle.setValue(settings.enabledInPreview).onChange((value) => {
settings.enabledInPreview = value;
value
? previewConfigContainerEl.show()
: previewConfigContainerEl.hide();
this.plugin.saveSettings();
})
);
const previewConfigContainerEl = containerEl.createDiv(
className.settingItem
);
let previewConfigBtn: ButtonOrUndefined;
new Setting(containerEl)
new Setting(previewConfigContainerEl)
.setName(i18n.t("setting.enabledInPreviewConfig"))
.setDesc(i18n.t("setting.enabledInPreviewConfigDesc"))
.addToggle((toggle) => {
@ -150,6 +173,10 @@ export class HeadingSettingTab extends PluginSettingTab {
.setDisabled(!settings.enabledPreviewSettings);
});
if (!settings.enabledInPreview) {
previewConfigContainerEl.hide();
}
new Setting(containerEl).setName(i18n.t("setting.sourceMode")).setHeading();
//* enabledInSource
@ -159,12 +186,19 @@ export class HeadingSettingTab extends PluginSettingTab {
.addToggle((toggle) =>
toggle.setValue(settings.enabledInSource).onChange((value) => {
settings.enabledInSource = value;
value
? sourceConfigContainerEl.show()
: sourceConfigContainerEl.hide();
this.plugin.saveSettings();
})
);
const sourceConfigContainerEl = containerEl.createDiv(
className.settingItem
);
let sourceConfigBtn: ButtonOrUndefined;
new Setting(containerEl)
new Setting(sourceConfigContainerEl)
.setName(i18n.t("setting.enabledInSourceConfig"))
.setDesc(i18n.t("setting.enabledInSourceConfigDesc"))
.addToggle((toggle) => {
@ -185,7 +219,7 @@ export class HeadingSettingTab extends PluginSettingTab {
});
//* sourceHideNumberSigns
new Setting(containerEl)
new Setting(sourceConfigContainerEl)
.setName(i18n.t("setting.hideNumberSigns"))
.setDesc(i18n.t("setting.hideNumberSignsDesc"))
.addToggle((toggle) => {
@ -197,6 +231,10 @@ export class HeadingSettingTab extends PluginSettingTab {
});
});
if (!settings.enabledInSource) {
sourceConfigContainerEl.hide();
}
new Setting(containerEl).setName(i18n.t("setting.outline")).setHeading();
//* enabledInOutline
@ -206,12 +244,19 @@ export class HeadingSettingTab extends PluginSettingTab {
.addToggle((toggle) =>
toggle.setValue(settings.enabledInOutline).onChange((value) => {
settings.enabledInOutline = value;
value
? outlineConfigContainerEl.show()
: outlineConfigContainerEl.hide();
this.plugin.saveSettings();
})
);
const outlineConfigContainerEl = containerEl.createDiv(
className.settingItem
);
let outlineConfigBtn: ButtonOrUndefined;
new Setting(containerEl)
new Setting(outlineConfigContainerEl)
.setName(i18n.t("setting.enabledInOutlineConfig"))
.setDesc(i18n.t("setting.enabledInOutlineConfigDesc"))
.addToggle((toggle) => {
@ -231,6 +276,10 @@ export class HeadingSettingTab extends PluginSettingTab {
.setDisabled(!settings.enabledOutlineSettings);
});
if (!settings.enabledInOutline) {
outlineConfigContainerEl.hide();
}
new Setting(containerEl)
.setName(i18n.t("setting.quietOutline"))
.setHeading();
@ -241,6 +290,9 @@ export class HeadingSettingTab extends PluginSettingTab {
.addToggle((toggle) =>
toggle.setValue(settings.enabledInQuietOutline).onChange((value) => {
settings.enabledInQuietOutline = value;
value
? quietOutlineConfigContainerEl.show()
: quietOutlineConfigContainerEl.hide();
this.plugin.saveSettings();
})
);
@ -259,8 +311,12 @@ export class HeadingSettingTab extends PluginSettingTab {
);
enabledInQuietOutlineSetting.descEl.appendChild(enabledInQuietOutlineDesc);
const quietOutlineConfigContainerEl = containerEl.createDiv(
className.settingItem
);
let quietOutlineConfigBtn: ButtonOrUndefined;
new Setting(containerEl)
new Setting(quietOutlineConfigContainerEl)
.setName(i18n.t("setting.enabledInQuietOutlineConfig"))
.setDesc(i18n.t("setting.enabledInQuietOutlineConfigDesc"))
.addToggle((toggle) => {
@ -282,6 +338,10 @@ export class HeadingSettingTab extends PluginSettingTab {
.setDisabled(!settings.enabledQuietOutlineSettings);
});
if (!settings.enabledInQuietOutline) {
quietOutlineConfigContainerEl.hide();
}
new Setting(containerEl)
.setName(i18n.t("setting.fileExplorer"))
.setHeading();
@ -292,6 +352,9 @@ export class HeadingSettingTab extends PluginSettingTab {
.addToggle((toggle) =>
toggle.setValue(settings.enabledInFileExplorer).onChange((value) => {
settings.enabledInFileExplorer = value;
value
? fileExplorerConfigContainerEl.show()
: fileExplorerConfigContainerEl.hide();
this.plugin.saveSettings();
})
);
@ -310,8 +373,12 @@ export class HeadingSettingTab extends PluginSettingTab {
);
enabledInFileExplorerSetting.descEl.appendChild(enabledInFileExplorerDesc);
const fileExplorerConfigContainerEl = containerEl.createDiv(
className.settingItem
);
let fileExplorerConfigBtn: ButtonOrUndefined;
new Setting(containerEl)
new Setting(fileExplorerConfigContainerEl)
.setName(i18n.t("setting.enabledInFileExplorerConfig"))
.setDesc(i18n.t("setting.enabledInFileExplorerConfigDesc"))
.addToggle((toggle) => {
@ -333,6 +400,10 @@ export class HeadingSettingTab extends PluginSettingTab {
.setDisabled(!settings.enabledFileExplorerSettings);
});
if (!settings.enabledInFileExplorer) {
fileExplorerConfigContainerEl.hide();
}
new Setting(containerEl).setName(i18n.t("setting.blocklist")).setHeading();
//* folderBlacklist
@ -361,6 +432,10 @@ export class HeadingSettingTab extends PluginSettingTab {
return false;
}
private isDecoratorModeValue(value: string): value is DecoratorMode {
return ["orderd", "independent", "unordered"].includes(value);
}
private isPositionValue(value: string): value is PostionOptions {
return ["before", "after", "before-inside", "after-inside"].includes(value);
}
@ -428,16 +503,50 @@ export class HeadingSettingTab extends PluginSettingTab {
new Setting(containerEl).setName(i18n.t("setting.effect")).setHeading();
//* ordered
//* decoratorMode
new Setting(containerEl)
.setName(i18n.t("setting.ordered"))
.setDesc(i18n.t("setting.orderedDesc"))
.addToggle((toggle) =>
toggle.setValue(settings[settingsType].ordered).onChange((value) => {
settings[settingsType].ordered = value;
.setName(i18n.t("setting.decoratorMode"))
.setDesc(i18n.t("setting.decoratorModeDesc"))
.addDropdown((dropdown) => {
const options: Record<DecoratorMode, string> = {
orderd: i18n.t("setting.ordered"),
independent: i18n.t("setting.independent"),
unordered: i18n.t("setting.unordered"),
};
dropdown
.addOptions(options)
.setValue(settings[settingsType].decoratorMode || "orderd")
.onChange((value) => {
settings[settingsType].decoratorMode = this.isDecoratorModeValue(
value
)
? value
: "orderd";
switch (settings[settingsType].decoratorMode) {
case "orderd":
orderedContainerEl.show();
logicContainerEl.show();
independentContainerEl.hide();
unorderedContainerEl.hide();
break;
case "independent":
independentContainerEl.show();
logicContainerEl.show();
orderedContainerEl.hide();
unorderedContainerEl.hide();
break;
case "unordered":
unorderedContainerEl.show();
orderedContainerEl.hide();
independentContainerEl.hide();
logicContainerEl.hide();
break;
}
this.plugin.saveSettings();
})
);
});
});
//* opacity
new Setting(containerEl)
@ -502,6 +611,51 @@ export class HeadingSettingTab extends PluginSettingTab {
.setDynamicTooltip();
});
const orderedContainerEl = containerEl.createDiv(
className.settingContainer
);
this.orderedSettings(orderedContainerEl, settings[settingsType]);
if (
settings[settingsType].decoratorMode &&
settings[settingsType].decoratorMode !== "orderd"
) {
orderedContainerEl.hide();
}
const independentContainerEl = containerEl.createDiv(
className.settingContainer
);
this.independentSettings(independentContainerEl, settings[settingsType]);
if (settings[settingsType].decoratorMode !== "independent") {
independentContainerEl.hide();
}
const unorderedContainerEl = containerEl.createDiv(
className.settingContainer
);
this.unorderedSettings(unorderedContainerEl, settings[settingsType]);
if (settings[settingsType].decoratorMode !== "unordered") {
unorderedContainerEl.hide();
}
const logicContainerEl = containerEl.createDiv(className.settingContainer);
this.logicSettings(logicContainerEl, settings[settingsType]);
if (settings[settingsType].decoratorMode === "unordered") {
logicContainerEl.hide();
}
//* Scroll back to the top
containerEl.scrollTo({ top: 0, behavior: "smooth" });
}
private orderedSettings(
containerEl: HTMLElement,
settings: HeadingDecoratorSettings
) {
const {
plugin: { i18n },
} = this;
new Setting(containerEl).setName(i18n.t("setting.ordered")).setHeading();
//* orderedStyleType
@ -511,10 +665,23 @@ export class HeadingSettingTab extends PluginSettingTab {
.addDropdown((dropdown) =>
dropdown
.addOptions(orderedStyleTypeOptions)
.setValue(settings[settingsType].orderedStyleType)
.onChange((value) => {
settings[settingsType].orderedStyleType =
value as OrderedCounterStyleType;
.setValue(settings.orderedStyleType)
.onChange((value: OrderedCounterStyleType) => {
settings.orderedStyleType = value;
switch (value) {
case "customIdent":
orderedCustomIdentsContainerEl.show();
orderedSpecifiedStringContainerEl.hide();
break;
case "string":
orderedSpecifiedStringContainerEl.show();
orderedCustomIdentsContainerEl.hide();
break;
default:
orderedCustomIdentsContainerEl.hide();
orderedSpecifiedStringContainerEl.hide();
break;
}
this.plugin.saveSettings();
})
);
@ -524,10 +691,8 @@ export class HeadingSettingTab extends PluginSettingTab {
.setName(i18n.t("setting.orderedDelimiter"))
.setDesc(i18n.t("setting.orderedDelimiterDesc"))
.addText((text) =>
text
.setValue(settings[settingsType].orderedDelimiter)
.onChange((value) => {
settings[settingsType].orderedDelimiter = value;
text.setValue(settings.orderedDelimiter).onChange((value) => {
settings.orderedDelimiter = value;
this.plugin.saveSettings();
})
);
@ -537,88 +702,227 @@ export class HeadingSettingTab extends PluginSettingTab {
.setName(i18n.t("setting.orderedTrailingDelimiter"))
.setDesc(i18n.t("setting.orderedTrailingDelimiterDesc"))
.addToggle((toggle) =>
toggle
.setValue(settings[settingsType].orderedTrailingDelimiter)
.onChange((value) => {
settings[settingsType].orderedTrailingDelimiter = value;
toggle.setValue(settings.orderedTrailingDelimiter).onChange((value) => {
settings.orderedTrailingDelimiter = value;
value
? orderedCustomTrailingDelimiterContainerEl.show()
: orderedCustomTrailingDelimiterContainerEl.hide();
this.plugin.saveSettings();
})
);
const orderedCustomTrailingDelimiterContainerEl = containerEl.createDiv(
className.settingItem
);
//* orderedCustomTrailingDelimiter
new Setting(containerEl)
new Setting(orderedCustomTrailingDelimiterContainerEl)
.setName(i18n.t("setting.orderedCustomTrailingDelimiter"))
.setDesc(i18n.t("setting.orderedCustomTrailingDelimiterDesc"))
.addText((text) => {
text
.setValue(settings[settingsType].orderedCustomTrailingDelimiter || "")
.setValue(settings.orderedCustomTrailingDelimiter || "")
.onChange((value) => {
settings[settingsType].orderedCustomTrailingDelimiter = value;
settings.orderedCustomTrailingDelimiter = value;
this.plugin.saveSettings();
});
});
if (!settings.orderedTrailingDelimiter) {
orderedCustomTrailingDelimiterContainerEl.hide();
}
//* orderedLeadingDelimiter
new Setting(containerEl)
.setName(i18n.t("setting.orderedLeadingDelimiter"))
.setDesc(i18n.t("setting.orderedLeadingDelimiterDesc"))
.addToggle((toggle) =>
toggle
.setValue(settings[settingsType].orderedLeadingDelimiter || false)
.setValue(settings.orderedLeadingDelimiter || false)
.onChange((value) => {
settings[settingsType].orderedLeadingDelimiter = value;
settings.orderedLeadingDelimiter = value;
value
? orderedCustomLeadingDelimiterContainerEl.show()
: orderedCustomLeadingDelimiterContainerEl.hide();
this.plugin.saveSettings();
})
);
const orderedCustomLeadingDelimiterContainerEl = containerEl.createDiv(
className.settingItem
);
//* orderedCustomLeadingDelimiter
new Setting(containerEl)
new Setting(orderedCustomLeadingDelimiterContainerEl)
.setName(i18n.t("setting.orderedCustomLeadingDelimiter"))
.setDesc(i18n.t("setting.orderedCustomLeadingDelimiterDesc"))
.addText((text) => {
text
.setValue(settings[settingsType].orderedCustomLeadingDelimiter || "")
.setValue(settings.orderedCustomLeadingDelimiter || "")
.onChange((value) => {
settings[settingsType].orderedCustomLeadingDelimiter = value;
settings.orderedCustomLeadingDelimiter = value;
this.plugin.saveSettings();
});
});
if (!settings.orderedLeadingDelimiter) {
orderedCustomLeadingDelimiterContainerEl.hide();
}
const orderedCustomIdentsContainerEl = containerEl.createDiv(
className.settingItem
);
//* orderedCustomIdents
new Setting(containerEl)
new Setting(orderedCustomIdentsContainerEl)
.setName(i18n.t("setting.orderedCustomIdents"))
.setDesc(i18n.t("setting.orderedCustomIdentsDesc"))
.addText((text) =>
text
.setValue(settings[settingsType].orderedCustomIdents)
.onChange((value) => {
settings[settingsType].orderedCustomIdents = value;
text.setValue(settings.orderedCustomIdents).onChange((value) => {
settings.orderedCustomIdents = value;
this.plugin.saveSettings();
})
);
if (settings.orderedStyleType !== "customIdent") {
orderedCustomIdentsContainerEl.hide();
}
const orderedSpecifiedStringContainerEl = containerEl.createDiv(
className.settingItem
);
//* orderedSpecifiedString
new Setting(containerEl)
new Setting(orderedSpecifiedStringContainerEl)
.setName(i18n.t("setting.orderedSpecifiedString"))
.setDesc(i18n.t("setting.orderedSpecifiedStringDesc"))
.addText((text) =>
text
.setValue(settings[settingsType].orderedSpecifiedString)
.onChange((value) => {
settings[settingsType].orderedSpecifiedString = value;
text.setValue(settings.orderedSpecifiedString).onChange((value) => {
settings.orderedSpecifiedString = value;
this.plugin.saveSettings();
})
);
if (settings.orderedStyleType !== "string") {
orderedSpecifiedStringContainerEl.hide();
}
}
private independentSettings(
containerEl: HTMLElement,
settings: HeadingDecoratorSettings
) {
const {
plugin: { i18n },
} = this;
if (!settings.independentSettings) {
settings.independentSettings = defaultIndependentSettings();
}
new Setting(containerEl)
.setName(i18n.t("setting.independent"))
.setHeading();
//* orderedRecLevel
new Setting(containerEl)
.setName(i18n.t("setting.orderedRecLevel"))
.setDesc(i18n.t("setting.orderedRecLevelDesc"))
.addSlider((slider) => {
slider
.setLimits(2, 6, 1)
.setValue(settings.independentSettings!.orderedRecLevel)
.onChange((value) => {
settings.independentSettings!.orderedRecLevel = value;
this.plugin.saveSettings();
})
.setDynamicTooltip();
});
//* h1
new Setting(containerEl).setName(i18n.t("setting.h1")).setHeading();
this.independentDecoratorSettings(
containerEl,
settings.independentSettings!.h1
);
//* h2
new Setting(containerEl).setName(i18n.t("setting.h2")).setHeading();
this.independentDecoratorSettings(
containerEl,
settings.independentSettings!.h2
);
//* h3
new Setting(containerEl).setName(i18n.t("setting.h3")).setHeading();
this.independentDecoratorSettings(
containerEl,
settings.independentSettings!.h3
);
//* h4
new Setting(containerEl).setName(i18n.t("setting.h4")).setHeading();
this.independentDecoratorSettings(
containerEl,
settings.independentSettings!.h4
);
//* h5
new Setting(containerEl).setName(i18n.t("setting.h5")).setHeading();
this.independentDecoratorSettings(
containerEl,
settings.independentSettings!.h5
);
//* h6
new Setting(containerEl).setName(i18n.t("setting.h6")).setHeading();
this.independentDecoratorSettings(
containerEl,
settings.independentSettings!.h6
);
}
private unorderedSettings(
containerEl: HTMLElement,
settings: HeadingDecoratorSettings
) {
const {
plugin: { i18n },
} = this;
new Setting(containerEl).setName(i18n.t("setting.unordered")).setHeading();
//* unorderedLevelHeadings
new Setting(containerEl)
.setName(i18n.t("setting.unorderedLevelHeadings"))
.setDesc(i18n.t("setting.unorderedLevelHeadingsDesc"))
.addText((text) =>
text.setValue(settings.unorderedLevelHeadings).onChange((value) => {
settings.unorderedLevelHeadings = value;
this.plugin.saveSettings();
})
);
}
private logicSettings(
containerEl: HTMLElement,
settings: HeadingDecoratorSettings
) {
const {
plugin: { i18n },
} = this;
new Setting(containerEl).setName(i18n.t("setting.logic")).setHeading();
//* orderedAllowZeroLevel
new Setting(containerEl)
.setName(i18n.t("setting.orderedAllowZeroLevel"))
.setDesc(i18n.t("setting.orderedAllowZeroLevelDesc"))
.addToggle((toggle) =>
toggle
.setValue(settings[settingsType].orderedAllowZeroLevel ?? false)
.setValue(settings.orderedAllowZeroLevel ?? false)
.onChange((value) => {
settings[settingsType].orderedAllowZeroLevel = value;
settings.orderedAllowZeroLevel = value;
this.plugin.saveSettings();
})
);
@ -629,9 +933,9 @@ export class HeadingSettingTab extends PluginSettingTab {
.setDesc(i18n.t("setting.orderedBasedOnExistingDesc"))
.addToggle((toggle) =>
toggle
.setValue(settings[settingsType].orderedBasedOnExisting ?? false)
.setValue(settings.orderedBasedOnExisting ?? false)
.onChange((value) => {
settings[settingsType].orderedBasedOnExisting = value;
settings.orderedBasedOnExisting = value;
this.plugin.saveSettings();
})
);
@ -642,9 +946,9 @@ export class HeadingSettingTab extends PluginSettingTab {
.setDesc(i18n.t("setting.orderedAlwaysIgnoreDesc"))
.addToggle((toggle) =>
toggle
.setValue(settings[settingsType].orderedAlwaysIgnore ?? false)
.setValue(settings.orderedAlwaysIgnore ?? false)
.onChange((value) => {
settings[settingsType].orderedAlwaysIgnore = value;
settings.orderedAlwaysIgnore = value;
this.plugin.saveSettings();
})
);
@ -654,10 +958,8 @@ export class HeadingSettingTab extends PluginSettingTab {
.setName(i18n.t("setting.orderedIgnoreSingle"))
.setDesc(i18n.t("setting.orderedIgnoreSingleDesc"))
.addToggle((toggle) =>
toggle
.setValue(settings[settingsType].orderedIgnoreSingle)
.onChange((value) => {
settings[settingsType].orderedIgnoreSingle = value;
toggle.setValue(settings.orderedIgnoreSingle).onChange((value) => {
settings.orderedIgnoreSingle = value;
this.plugin.saveSettings();
})
);
@ -669,31 +971,13 @@ export class HeadingSettingTab extends PluginSettingTab {
.addSlider((slider) =>
slider
.setLimits(1, 6, 1)
.setValue(settings[settingsType].orderedIgnoreMaximum ?? 6)
.setValue(settings.orderedIgnoreMaximum ?? 6)
.onChange((value) => {
settings[settingsType].orderedIgnoreMaximum = value;
settings.orderedIgnoreMaximum = value;
this.plugin.saveSettings();
})
.setDynamicTooltip()
);
new Setting(containerEl).setName(i18n.t("setting.unordered")).setHeading();
//* unorderedLevelHeadings
new Setting(containerEl)
.setName(i18n.t("setting.unorderedLevelHeadings"))
.setDesc(i18n.t("setting.unorderedLevelHeadingsDesc"))
.addText((text) =>
text
.setValue(settings[settingsType].unorderedLevelHeadings)
.onChange((value) => {
settings[settingsType].unorderedLevelHeadings = value;
this.plugin.saveSettings();
})
);
//* Scroll back to the top
containerEl.scrollTo({ top: 0, behavior: "smooth" });
}
private manageFolderBlacklist(scrollToTop = false) {
@ -826,4 +1110,162 @@ export class HeadingSettingTab extends PluginSettingTab {
containerEl.scrollTo({ top: 0, behavior: "smooth" });
}
}
private independentDecoratorSettings(
containerEl: HTMLElement,
settings: IndependentDecoratorSettings
) {
const {
plugin: { i18n },
} = this;
//* styleType
new Setting(containerEl)
.setName(i18n.t("setting.orderedStyleType"))
.setDesc(i18n.t("setting.orderedStyleTypeDesc"))
.addDropdown((dropdown) =>
dropdown
.addOptions(orderedStyleTypeOptions)
.setValue(settings.styleType)
.onChange((value: OrderedCounterStyleType) => {
settings.styleType = value;
switch (value) {
case "customIdent":
customIdentsContainerEl.show();
specifiedStringContainerEl.hide();
break;
case "string":
specifiedStringContainerEl.show();
customIdentsContainerEl.hide();
break;
default:
customIdentsContainerEl.hide();
specifiedStringContainerEl.hide();
break;
}
this.plugin.saveSettings();
})
);
//* delimiter
new Setting(containerEl)
.setName(i18n.t("setting.orderedDelimiter"))
.setDesc(i18n.t("setting.orderedDelimiterDesc"))
.addText((text) =>
text.setValue(settings.delimiter).onChange((value) => {
settings.delimiter = value;
this.plugin.saveSettings();
})
);
//* trailingDelimiter
new Setting(containerEl)
.setName(i18n.t("setting.orderedTrailingDelimiter"))
.setDesc(i18n.t("setting.orderedTrailingDelimiterDesc"))
.addToggle((toggle) =>
toggle.setValue(settings.trailingDelimiter).onChange((value) => {
settings.trailingDelimiter = value;
value
? customTrailingDelimiterContainerEl.show()
: customTrailingDelimiterContainerEl.hide();
this.plugin.saveSettings();
})
);
const customTrailingDelimiterContainerEl = containerEl.createDiv(
className.settingItem
);
//* customTrailingDelimiter
new Setting(customTrailingDelimiterContainerEl)
.setName(i18n.t("setting.orderedCustomTrailingDelimiter"))
.setDesc(i18n.t("setting.orderedCustomTrailingDelimiterDesc"))
.addText((text) => {
text
.setValue(settings.customTrailingDelimiter || "")
.onChange((value) => {
settings.customTrailingDelimiter = value;
this.plugin.saveSettings();
});
});
if (!settings.trailingDelimiter) {
customTrailingDelimiterContainerEl.hide();
}
//* leadingDelimiter
new Setting(containerEl)
.setName(i18n.t("setting.orderedLeadingDelimiter"))
.setDesc(i18n.t("setting.orderedLeadingDelimiterDesc"))
.addToggle((toggle) =>
toggle
.setValue(settings.leadingDelimiter || false)
.onChange((value) => {
settings.leadingDelimiter = value;
value
? customLeadingDelimiterContainerEl.show()
: customLeadingDelimiterContainerEl.hide();
this.plugin.saveSettings();
})
);
const customLeadingDelimiterContainerEl = containerEl.createDiv(
className.settingItem
);
//* customLeadingDelimiter
new Setting(customLeadingDelimiterContainerEl)
.setName(i18n.t("setting.orderedCustomLeadingDelimiter"))
.setDesc(i18n.t("setting.orderedCustomLeadingDelimiterDesc"))
.addText((text) => {
text
.setValue(settings.customLeadingDelimiter || "")
.onChange((value) => {
settings.customLeadingDelimiter = value;
this.plugin.saveSettings();
});
});
if (!settings.leadingDelimiter) {
customLeadingDelimiterContainerEl.hide();
}
const customIdentsContainerEl = containerEl.createDiv(
className.settingItem
);
//* customIdents
new Setting(customIdentsContainerEl)
.setName(i18n.t("setting.orderedCustomIdents"))
.setDesc(i18n.t("setting.orderedCustomIdentsDesc"))
.addText((text) =>
text.setValue(settings.customIdents).onChange((value) => {
settings.customIdents = value;
this.plugin.saveSettings();
})
);
if (settings.styleType !== "customIdent") {
customIdentsContainerEl.hide();
}
const specifiedStringContainerEl = containerEl.createDiv(
className.settingItem
);
//* specifiedString
new Setting(specifiedStringContainerEl)
.setName(i18n.t("setting.orderedSpecifiedString"))
.setDesc(i18n.t("setting.orderedSpecifiedStringDesc"))
.addText((text) =>
text.setValue(settings.specifiedString).onChange((value) => {
settings.specifiedString = value;
this.plugin.saveSettings();
})
);
if (settings.styleType !== "string") {
specifiedStringContainerEl.hide();
}
}
}

View file

@ -98,8 +98,8 @@
"enabledInEachNote": "Enabled in each note",
"enabledInEachNoteDesc": "Toggle this setting to enable the decoration of headings in each note.",
"effect": "Effect",
"ordered": "Ordered",
"orderedDesc": "Toggle this setting to enable the decoration of headings as an ordered or unordered list.",
"decoratorMode": "Mode",
"decoratorModeDesc": "Toggle this setting to decorate headings as ordered list, independent list, or unordered list.",
"opacity": "Opacity",
"opacityDesc": "Set the opacity of the heading decorator. The value is the form of percentage.",
"position": "Position",
@ -110,32 +110,36 @@
"after": "After the heading",
"beforeInside": "Before the heading (inside)",
"afterInside": "After the heading (inside)",
"ordered": "Ordered",
"orderedStyleType": "Style type",
"orderedStyleTypeDesc": "Set the style type for ordered list.",
"orderedStyleTypeDesc": "Set the style type for decorator.",
"orderedDelimiter": "Delimiter",
"orderedDelimiterDesc": "Set the delimiter for ordered list.",
"orderedDelimiterDesc": "Set the delimiter for decorator.",
"orderedTrailingDelimiter": "Trailing delimiter",
"orderedTrailingDelimiterDesc": "Set whether to add a trailing delimiter for ordered list.",
"orderedTrailingDelimiterDesc": "Set whether to add a trailing delimiter for decorator.",
"orderedCustomTrailingDelimiter": "Custom trailing delimiter",
"orderedCustomTrailingDelimiterDesc": "Ignore the delimiter of the ordered list, customize another trailing delimiter.",
"orderedCustomTrailingDelimiterDesc": "Ignore the delimiter of the decorator, customize another trailing delimiter.",
"orderedLeadingDelimiter": "Leading delimiter",
"orderedLeadingDelimiterDesc": "Set whether to add a leading delimiter for ordered list.",
"orderedLeadingDelimiterDesc": "Set whether to add a leading delimiter for decorator.",
"orderedCustomLeadingDelimiter": "Custom leading delimiter",
"orderedCustomLeadingDelimiterDesc": "Ignore the delimiter of the ordered list, customize another leading delimiter.",
"orderedCustomLeadingDelimiterDesc": "Ignore the delimiter of the decorator, customize another leading delimiter.",
"orderedCustomIdents": "Custom list styles",
"orderedCustomIdentsDesc": "For the \"Custom list styles\" option. Set custom list styles for ordered list. Use spaces to separate entries.",
"orderedCustomIdentsDesc": "For the \"Custom list styles\" option. Set custom list styles for decorator. Use spaces to separate entries.",
"orderedSpecifiedString": "Specified string",
"orderedSpecifiedStringDesc": "For the \"Specified string\" option. Set a specified string for ordered list.",
"orderedSpecifiedStringDesc": "For the \"Specified string\" option. Set a specified string for decorator.",
"orderedAllowZeroLevel": "Allow zero level",
"orderedAllowZeroLevelDesc": "If the next heading is more than one level higher, the omitted level is zero instead of one.",
"orderedBasedOnExisting": "Based on the existing highest level",
"orderedBasedOnExistingDesc": "Use the highest level of headings in the note as the base for ordered list.",
"orderedBasedOnExistingDesc": "Use the highest level of headings in the note as the base for building decorators.",
"orderedAlwaysIgnore": "Always ignore the top-level headings",
"orderedAlwaysIgnoreDesc": "Exclude the top-level heading when building ordered lists. controlled by the \"Maximum number of ignored levels\" option.",
"orderedAlwaysIgnoreDesc": "Exclude the top-level heading when building decorators. controlled by the \"Maximum number of ignored levels\" option.",
"orderedIgnoreSingle": "Ignore the single heading at the top-level",
"orderedIgnoreSingleDesc": "If the top-level has only a single heading, exclude it when building an ordered list. controlled by the \"Maximum number of ignored levels\" option.",
"orderedIgnoreSingleDesc": "If the top-level has only a single heading, exclude it when building decorators. controlled by the \"Maximum number of ignored levels\" option.",
"orderedIgnoreMaximum": "Maximum number of ignored levels",
"orderedIgnoreMaximumDesc": "When enabling the option to ignore top-level headings, specify the maximum number of levels to ignore.",
"independent": "Independent",
"orderedRecLevel": "Level using ordered list",
"orderedRecLevelDesc": "When the heading level exceeds the specified level, build it as an ordered list.",
"unordered": "Unordered",
"unorderedLevelHeadings": "Level headings",
"unorderedLevelHeadingsDesc": "Set the names for each level. The value is 6 entries separated by spaces.",
@ -149,6 +153,13 @@
"folderBlocklistAddTip": "Add a new folder to the blocklist",
"fileRegexBlocklistIndex": "Note name regex blocklist {index}",
"fileRegexBlocklistPlaceholder": "e.g., /^daily.*/i",
"fileRegexBlocklistAddTip": "Add a new note name regex blocklist"
"fileRegexBlocklistAddTip": "Add a new note name regex blocklist",
"logic": "Logic",
"h1": "H1",
"h2": "H2",
"h3": "H3",
"h4": "H4",
"h5": "H5",
"h6": "H6"
}
}

View file

@ -98,11 +98,11 @@
"enabledInEachNote": "在每個筆記中啟用",
"enabledInEachNoteDesc": "切換此設定以啟用每個筆記中標題的裝飾。",
"effect": "效果",
"ordered": "有序列表",
"orderedDesc": "切換此設定可將標題裝飾為有序或無序列表。",
"opacity": "不透明度",
"decoratorMode": "裝飾器的模式",
"decoratorModeDesc": "切換此設定可將標題裝飾為有序列表、獨立層級或無序列表。",
"opacity": "裝飾器的不透明度",
"opacityDesc": "設定標題裝飾器的不透明度。值是百分比的形式。",
"position": "位置",
"position": "裝飾器的位置",
"positionDesc": "設定標題裝飾器的位置。",
"maxRecLevel": "所識別的最大級別",
"maxRecLevelDesc": "指定所識別標題的最大級別,不處理高於此級別的標題。",
@ -110,32 +110,36 @@
"after": "在標題之後",
"beforeInside": "在標題之前 (內部)",
"afterInside": "在標題之後 (內部)",
"ordered": "有序列表",
"orderedStyleType": "樣式型別",
"orderedStyleTypeDesc": "設定有序列表的樣式型別。",
"orderedStyleTypeDesc": "設定裝飾器的樣式型別。",
"orderedDelimiter": "分隔符",
"orderedDelimiterDesc": "設定有序列表的分隔符。",
"orderedDelimiterDesc": "設定裝飾器的分隔符。",
"orderedTrailingDelimiter": "尾隨分隔符",
"orderedTrailingDelimiterDesc": "設定是否為有序列表補全尾隨分隔符。",
"orderedTrailingDelimiterDesc": "設定是否為裝飾器補全尾隨分隔符。",
"orderedCustomTrailingDelimiter": "自定義尾隨分隔符",
"orderedCustomTrailingDelimiterDesc": "忽略有序列表的分隔符,自定義一個其他的尾隨分隔符。",
"orderedCustomTrailingDelimiterDesc": "忽略裝飾器的分隔符,自定義一個其他的尾隨分隔符。",
"orderedLeadingDelimiter": "前導分隔符",
"orderedLeadingDelimiterDesc": "設定是否為有序列表新增前導分隔符。",
"orderedLeadingDelimiterDesc": "設定是否為裝飾器新增前導分隔符。",
"orderedCustomLeadingDelimiter": "自定義前導分隔符",
"orderedCustomLeadingDelimiterDesc": "忽略有序列表的分隔符,自定義一個其他的前導分隔符。",
"orderedCustomLeadingDelimiterDesc": "忽略裝飾器的分隔符,自定義一個其他的前導分隔符。",
"orderedCustomIdents": "自定義列表樣式",
"orderedCustomIdentsDesc": "針對\"自定義列表樣式\"選項。為有序列表設定自定義列表樣式。使用空格分隔條目。",
"orderedCustomIdentsDesc": "針對\"自定義列表樣式\"選項。為裝飾器設定自定義列表樣式。使用空格分隔條目。",
"orderedSpecifiedString": "指定字串",
"orderedSpecifiedStringDesc": "針對\"指定字串\"選項。為有序列表設定指定的字串。",
"orderedSpecifiedStringDesc": "針對\"指定字串\"選項。為裝飾器設定指定的字串。",
"orderedAllowZeroLevel": "允許零級別",
"orderedAllowZeroLevelDesc": "如果下一個標題高出一個以上的級別,則省略的級別為零而不是一。",
"orderedBasedOnExisting": "基於現有的最高級別",
"orderedBasedOnExistingDesc": "使用筆記中最高級別的標題作為有序列表的基礎。",
"orderedBasedOnExistingDesc": "使用筆記中最高級別的標題作為構建裝飾器的基礎。",
"orderedAlwaysIgnore": "始終忽略頂層的標題",
"orderedAlwaysIgnoreDesc": "在構建有序列表時將頂層的標題排除掉,受\"忽略的最大層級數量\"選項控制。",
"orderedAlwaysIgnoreDesc": "在構建裝飾器時將頂層的標題排除掉,受\"忽略的最大層級數量\"選項控制。",
"orderedIgnoreSingle": "忽略頂層的獨立標題",
"orderedIgnoreSingleDesc": "如果頂層只有一個標題,則在構建有序列表時將其排除。受\"忽略的最大層級數量\"選項控制。",
"orderedIgnoreSingleDesc": "如果頂層只有一個標題,則在構建裝飾器時將其排除。受\"忽略的最大層級數量\"選項控制。",
"orderedIgnoreMaximum": "忽略的最大層級數量",
"orderedIgnoreMaximumDesc": "在啟用忽略頂層標題的選項時,指定可忽略的最大層級數量。",
"independent": "獨立層級",
"orderedRecLevel": "使用有序列表的級別",
"orderedRecLevelDesc": "標題級別超出指定級別後按有序列表進行構建。",
"unordered": "無序列表",
"unorderedLevelHeadings": "級別標題",
"unorderedLevelHeadingsDesc": "設定每個級別的名稱。值為 6 個條目,以空格分隔。",
@ -149,6 +153,13 @@
"folderBlocklistAddTip": "新增新的資料夾到阻止列表",
"fileRegexBlocklistIndex": "筆記名稱正則表示式阻止列表 {index}",
"fileRegexBlocklistPlaceholder": "例如:/^daily.*/i",
"fileRegexBlocklistAddTip": "新增新的筆記名稱正則表示式到阻止列表"
"fileRegexBlocklistAddTip": "新增新的筆記名稱正則表示式到阻止列表",
"logic": "邏輯控制",
"h1": "H1 級別",
"h2": "H2 級別",
"h3": "H3 級別",
"h4": "H4 級別",
"h5": "H5 級別",
"h6": "H6 級別"
}
}

View file

@ -98,11 +98,11 @@
"enabledInEachNote": "在每个笔记中启用",
"enabledInEachNoteDesc": "切换此设置以启用每个笔记中标题的装饰。",
"effect": "效果",
"ordered": "有序列表",
"orderedDesc": "切换此设置可将标题装饰为有序或无序列表。",
"opacity": "不透明度",
"decoratorMode": "装饰器的模式",
"decoratorModeDesc": "切换此设置可将标题装饰为有序列表、独立层级或无序列表。",
"opacity": "装饰器的不透明度",
"opacityDesc": "设置标题装饰器的不透明度。值是百分比的形式。",
"position": "位置",
"position": "装饰器的位置",
"positionDesc": "设置标题装饰器的位置。",
"maxRecLevel": "所识别的最大级别",
"maxRecLevelDesc": "指定所识别标题的最大级别,不处理高于此级别的标题。",
@ -110,32 +110,36 @@
"after": "在标题之后",
"beforeInside": "在标题之前 (内部)",
"afterInside": "在标题之后 (内部)",
"ordered": "有序列表",
"orderedStyleType": "样式类型",
"orderedStyleTypeDesc": "设置有序列表的样式类型。",
"orderedStyleTypeDesc": "设置装饰器的样式类型。",
"orderedDelimiter": "分隔符",
"orderedDelimiterDesc": "设置有序列表的分隔符。",
"orderedDelimiterDesc": "设置装饰器的分隔符。",
"orderedTrailingDelimiter": "尾随分隔符",
"orderedTrailingDelimiterDesc": "设置是否为有序列表添加尾随分隔符。",
"orderedTrailingDelimiterDesc": "设置是否为装饰器添加尾随分隔符。",
"orderedCustomTrailingDelimiter": "自定义尾随分隔符",
"orderedCustomTrailingDelimiterDesc": "忽略有序列表的分隔符,自定义一个其他的尾随分隔符。",
"orderedCustomTrailingDelimiterDesc": "忽略装饰器的分隔符,自定义一个其他的尾随分隔符。",
"orderedLeadingDelimiter": "前导分隔符",
"orderedLeadingDelimiterDesc": "设置是否为有序列表添加前导分隔符。",
"orderedLeadingDelimiterDesc": "设置是否为装饰器添加前导分隔符。",
"orderedCustomLeadingDelimiter": "自定义前导分隔符",
"orderedCustomLeadingDelimiterDesc": "忽略有序列表的分隔符,自定义一个其他的前导分隔符。",
"orderedCustomLeadingDelimiterDesc": "忽略装饰器的分隔符,自定义一个其他的前导分隔符。",
"orderedCustomIdents": "自定义列表样式",
"orderedCustomIdentsDesc": "针对\"自定义列表样式\"选项。为有序列表设置自定义列表样式。使用空格分隔条目。",
"orderedCustomIdentsDesc": "针对\"自定义列表样式\"选项。为装饰器设置自定义列表样式。使用空格分隔条目。",
"orderedSpecifiedString": "指定字符串",
"orderedSpecifiedStringDesc": "针对\"指定字符串\"选项。为有序列表设置指定的字符串。",
"orderedSpecifiedStringDesc": "针对\"指定字符串\"选项。为装饰器设置指定的字符串。",
"orderedAllowZeroLevel": "允许零级别",
"orderedAllowZeroLevelDesc": "如果下一个标题高出一个以上的级别,则省略的级别为零而不是一。",
"orderedBasedOnExisting": "基于现有的最高级别",
"orderedBasedOnExistingDesc": "使用笔记中最高级别的标题作为有序列表的基础。",
"orderedBasedOnExistingDesc": "使用笔记中最高级别的标题作为构建装饰器的基础。",
"orderedAlwaysIgnore": "始终忽略顶层的标题",
"orderedAlwaysIgnoreDesc": "在构建有序列表时将顶层的标题排除掉,受\"忽略的最大层级数量\"选项控制。",
"orderedAlwaysIgnoreDesc": "在构建装饰器时将顶层的标题排除掉,受\"忽略的最大层级数量\"选项控制。",
"orderedIgnoreSingle": "忽略顶层的独立标题",
"orderedIgnoreSingleDesc": "如果顶层只有一个标题,则在构建有序列表时将其排除。受\"忽略的最大层级数量\"选项控制。",
"orderedIgnoreSingleDesc": "如果顶层只有一个标题,则在构建装饰器时将其排除。受\"忽略的最大层级数量\"选项控制。",
"orderedIgnoreMaximum": "忽略的最大层级数量",
"orderedIgnoreMaximumDesc": "在启用忽略顶层标题的选项时,指定可忽略的最大层级数量。",
"independent": "独立层级",
"orderedRecLevel": "使用有序列表的级别",
"orderedRecLevelDesc": "标题级别超出指定级别后按有序列表进行构建。",
"unordered": "无序列表",
"unorderedLevelHeadings": "级别标题",
"unorderedLevelHeadingsDesc": "设置每个级别的名称。值为 6 个条目,以空格分隔。",
@ -149,6 +153,13 @@
"folderBlocklistAddTip": "添加新的文件夹到阻止列表",
"fileRegexBlocklistIndex": "笔记名称正则表达式阻止列表 {index}",
"fileRegexBlocklistPlaceholder": "例如:/^daily.*/i",
"fileRegexBlocklistAddTip": "添加新的笔记名称正则表达式到阻止列表"
"fileRegexBlocklistAddTip": "添加新的笔记名称正则表达式到阻止列表",
"logic": "逻辑控制",
"h1": "H1 级别",
"h2": "H2 级别",
"h3": "H3 级别",
"h4": "H4 级别",
"h5": "H5 级别",
"h6": "H6 级别"
}
}

View file

@ -443,3 +443,19 @@ body {
opacity: 100%;
}
/* file-explorer start */
/* setting start */
body .heading-decorator-setting-container,
.heading-decorator-setting-item + .setting-item-heading {
margin-top: 0.75em;
}
body .heading-decorator-setting-container .setting-item-heading:first-child {
padding-top: 0.75em;
}
body .heading-decorator-setting-item {
padding-top: 0.75em;
border-top: 1px solid var(--background-modifier-border);
}
/* setting end */

View file

@ -1,8 +1,15 @@
import "mocha";
import { expect } from "chai";
import { Counter, Querier } from "../../common/counter";
import type { IndependentDecoratorSettings } from "../../common/data";
import {
Querier,
UnorderedCounter,
OrderedCounter,
IndependentCounter,
} from "../../common/counter";
describe("common/counter", function () {
describe("Querier", function () {
it("Querier.handler", function () {
const querier1 = new Querier();
@ -609,35 +616,13 @@ describe("common/counter", function () {
querier4.handler(3);
expect(querier4.query(true, 1)).to.equal(1);
});
it("Counter.decorator with default", function () {
const counter1 = new Counter();
expect(counter1.decorator(-1)).to.be.empty;
expect(counter1.decorator(0)).to.empty;
expect(counter1.decorator(1)).to.equal("1");
expect(counter1.decorator(2)).to.equal("1.1");
expect(counter1.decorator(3)).to.equal("1.1.1");
expect(counter1.decorator(4)).to.equal("1.1.1.1");
expect(counter1.decorator(5)).to.equal("1.1.1.1.1");
expect(counter1.decorator(6)).to.equal("1.1.1.1.1.1");
expect(counter1.decorator(7)).to.empty;
const counter2 = new Counter();
expect(counter2.decorator(3)).to.equal("1.1.1");
expect(counter2.decorator(4)).to.equal("1.1.1.1");
expect(counter2.decorator(5)).to.equal("1.1.1.1.1");
expect(counter2.decorator(6)).to.equal("1.1.1.1.1.1");
expect(counter2.decorator(7)).to.empty;
expect(counter2.decorator(1)).to.equal("2");
expect(counter2.decorator(2)).to.equal("2.1");
expect(counter2.decorator(6)).to.equal("2.1.1.1.1.1");
});
it("Counter.decorator with unordered", function () {
const counter = new Counter({
ordered: false,
levelHeadings: ["H1", "H2", "H3", "H4", "H5", "H6"],
});
describe("UnorderedCounter", function () {
const list: HeadingTuple = ["H1", "H2", "H3", "H4", "H5", "H6"];
it("UnorderedCounter.decorator", function () {
const counter = new UnorderedCounter(list);
expect(counter.decorator(-1)).to.be.empty;
expect(counter.decorator(0)).to.empty;
expect(counter.decorator(1)).to.equal("H1");
@ -649,9 +634,23 @@ describe("common/counter", function () {
expect(counter.decorator(7)).to.empty;
});
it("Counter.decorator with decimal styleType", function () {
const counter = new Counter({
ordered: true,
it("UnorderedCounter.decorator with maxRecLevel", function () {
const counter = new UnorderedCounter(list, 4);
expect(counter.decorator(-1)).to.be.empty;
expect(counter.decorator(0)).to.empty;
expect(counter.decorator(1)).to.equal("H1");
expect(counter.decorator(2)).to.equal("H2");
expect(counter.decorator(3)).to.equal("H3");
expect(counter.decorator(4)).to.equal("H4");
expect(counter.decorator(5)).to.be.empty;
expect(counter.decorator(6)).to.be.empty;
expect(counter.decorator(7)).to.empty;
});
});
describe("OrderedCounter", function () {
it("OrderedCounter.decorator with decimal styleType", function () {
const counter = new OrderedCounter({
styleType: "decimal",
});
expect(counter.decorator(-1)).to.be.empty;
@ -672,9 +671,8 @@ describe("common/counter", function () {
expect(counter.decorator(7)).to.be.empty;
});
it("Counter.decorator with lowerAlpha styleType", function () {
const counter = new Counter({
ordered: true,
it("OrderedCounter.decorator with lowerAlpha styleType", function () {
const counter = new OrderedCounter({
styleType: "lowerAlpha",
});
expect(counter.decorator(1)).to.equal("a");
@ -710,9 +708,8 @@ describe("common/counter", function () {
expect(counter.decorator(7)).to.be.empty;
});
it("Counter.decorator with upperAlpha styleType", function () {
const counter = new Counter({
ordered: true,
it("OrderedCounter.decorator with upperAlpha styleType", function () {
const counter = new OrderedCounter({
styleType: "upperAlpha",
});
expect(counter.decorator(1)).to.equal("A");
@ -748,9 +745,8 @@ describe("common/counter", function () {
expect(counter.decorator(7)).to.be.empty;
});
it("Counter.decorator with cjkDecimal styleType", function () {
const counter = new Counter({
ordered: true,
it("OrderedCounter.decorator with cjkDecimal styleType", function () {
const counter = new OrderedCounter({
styleType: "cjkDecimal",
});
expect(counter.decorator(1)).to.equal("一");
@ -769,9 +765,8 @@ describe("common/counter", function () {
expect(counter.decorator(1)).to.equal("二");
});
it("Counter.decorator with customIdent styleType", function () {
const counter1 = new Counter({
ordered: true,
it("OrderedCounter.decorator with customIdent styleType", function () {
const counter1 = new OrderedCounter({
styleType: "customIdent",
customIdents: [],
});
@ -783,8 +778,7 @@ describe("common/counter", function () {
expect(counter1.decorator(6)).to.equal("1.1.1.1.1.1");
expect(counter1.decorator(2)).to.equal("1.2");
const counter2 = new Counter({
ordered: true,
const counter2 = new OrderedCounter({
styleType: "customIdent",
customIdents: ["Ⓐ", "Ⓑ", "Ⓒ"],
});
@ -807,17 +801,15 @@ describe("common/counter", function () {
expect(counter2.decorator(3)).to.equal("Ⓐ.6.6");
});
it("Counter.decorator with string styleType", function () {
const counter1 = new Counter({
ordered: true,
it("OrderedCounter.decorator with string styleType", function () {
const counter1 = new OrderedCounter({
styleType: "string",
});
expect(counter1.decorator(1)).to.equal("#");
expect(counter1.decorator(2)).to.equal("#.#");
expect(counter1.decorator(3)).to.equal("#.#.#");
const counter2 = new Counter({
ordered: true,
const counter2 = new OrderedCounter({
styleType: "string",
specifiedString: "*",
});
@ -825,8 +817,7 @@ describe("common/counter", function () {
expect(counter2.decorator(2)).to.equal("*.*");
expect(counter2.decorator(3)).to.equal("*.*.*");
const counter3 = new Counter({
ordered: true,
const counter3 = new OrderedCounter({
styleType: "string",
specifiedString: "",
});
@ -835,32 +826,28 @@ describe("common/counter", function () {
expect(counter3.decorator(3)).to.equal("#.#.#");
});
it("Counter.decorator with custom delimiter", function () {
const counter1 = new Counter({
ordered: true,
it("OrderedCounter.decorator with custom delimiter", function () {
const counter1 = new OrderedCounter({
styleType: "decimal",
});
expect(counter1.decorator(1)).to.equal("1");
expect(counter1.decorator(2)).to.equal("1.1");
const counter2 = new Counter({
ordered: true,
const counter2 = new OrderedCounter({
styleType: "decimal",
delimiter: ",",
});
expect(counter2.decorator(1)).to.equal("1");
expect(counter2.decorator(2)).to.equal("1,1");
const counter3 = new Counter({
ordered: true,
const counter3 = new OrderedCounter({
styleType: "decimal",
delimiter: "",
});
expect(counter3.decorator(1)).to.equal("1");
expect(counter3.decorator(2)).to.equal("11");
const counter4 = new Counter({
ordered: true,
const counter4 = new OrderedCounter({
styleType: "string",
specifiedString: "#",
delimiter: "",
@ -869,9 +856,8 @@ describe("common/counter", function () {
expect(counter4.decorator(2)).to.equal("##");
});
it("Counter.decorator with trailing delimiter", function () {
const counter = new Counter({
ordered: true,
it("OrderedCounter.decorator with trailing delimiter", function () {
const counter = new OrderedCounter({
styleType: "decimal",
delimiter: ".",
trailingDelimiter: true,
@ -881,9 +867,8 @@ describe("common/counter", function () {
expect(counter.decorator(2)).to.equal("1.1.");
});
it("Counter.decorator with custom trailing delimiter", function () {
const counter1 = new Counter({
ordered: true,
it("OrderedCounter.decorator with custom trailing delimiter", function () {
const counter1 = new OrderedCounter({
styleType: "decimal",
delimiter: ".",
customTrailingDelimiter: "",
@ -893,8 +878,7 @@ describe("common/counter", function () {
expect(counter1.decorator(1)).to.equal("1.");
expect(counter1.decorator(2)).to.equal("1.1.");
const counter2 = new Counter({
ordered: true,
const counter2 = new OrderedCounter({
styleType: "decimal",
delimiter: ".",
customTrailingDelimiter: "|",
@ -905,9 +889,8 @@ describe("common/counter", function () {
expect(counter2.decorator(2)).to.equal("1.1|");
});
it("Counter.decorator with custom leading delimiter", function () {
const counter1 = new Counter({
ordered: true,
it("OrderedCounter.decorator with custom leading delimiter", function () {
const counter1 = new OrderedCounter({
styleType: "decimal",
delimiter: ".",
customLeadingDelimiter: "",
@ -917,8 +900,7 @@ describe("common/counter", function () {
expect(counter1.decorator(1)).to.equal(".1");
expect(counter1.decorator(2)).to.equal(".1.1");
const counter2 = new Counter({
ordered: true,
const counter2 = new OrderedCounter({
styleType: "decimal",
delimiter: ".",
customLeadingDelimiter: "|",
@ -929,9 +911,8 @@ describe("common/counter", function () {
expect(counter2.decorator(2)).to.equal("|1.1");
});
it("Counter.decorator with custom leading delimiter and trailing delimiter", function () {
const counter1 = new Counter({
ordered: true,
it("OrderedCounter.decorator with custom leading delimiter and trailing delimiter", function () {
const counter1 = new OrderedCounter({
styleType: "decimal",
delimiter: ".",
customTrailingDelimiter: "",
@ -943,8 +924,7 @@ describe("common/counter", function () {
expect(counter1.decorator(1)).to.equal(".1.");
expect(counter1.decorator(2)).to.equal(".1.1.");
const counter2 = new Counter({
ordered: true,
const counter2 = new OrderedCounter({
styleType: "decimal",
delimiter: ".",
customTrailingDelimiter: ")",
@ -957,9 +937,8 @@ describe("common/counter", function () {
expect(counter2.decorator(2)).to.equal("(1.1)");
});
it("Counter.decorator with ignoreTopLevel", function () {
const counter1 = new Counter({
ordered: true,
it("OrderedCounter.decorator with ignoreTopLevel", function () {
const counter1 = new OrderedCounter({
styleType: "decimal",
ignoreTopLevel: 1,
});
@ -979,8 +958,7 @@ describe("common/counter", function () {
expect(counter1.decorator(2)).to.equal("3");
expect(counter1.decorator(6)).to.equal("3.1.1.1.1");
const counter2 = new Counter({
ordered: true,
const counter2 = new OrderedCounter({
styleType: "decimal",
ignoreTopLevel: 2,
});
@ -994,8 +972,7 @@ describe("common/counter", function () {
expect(counter2.decorator(3)).to.equal("2");
expect(counter2.decorator(6)).to.equal("2.1.1.1");
const counter3 = new Counter({
ordered: true,
const counter3 = new OrderedCounter({
styleType: "decimal",
ignoreTopLevel: 2,
});
@ -1006,8 +983,7 @@ describe("common/counter", function () {
expect(counter3.decorator(3)).to.equal("2");
expect(counter3.decorator(6)).to.equal("2.1.1.1");
const counter4 = new Counter({
ordered: true,
const counter4 = new OrderedCounter({
styleType: "decimal",
trailingDelimiter: true,
ignoreTopLevel: 2,
@ -1026,9 +1002,8 @@ describe("common/counter", function () {
expect(counter4.decorator(6)).to.equal("3.1.1.1.");
});
it("Counter.decorator with allowZeroLevel", function () {
const counter1 = new Counter({
ordered: true,
it("OrderedCounter.decorator with allowZeroLevel", function () {
const counter1 = new OrderedCounter({
styleType: "decimal",
allowZeroLevel: true,
});
@ -1047,8 +1022,7 @@ describe("common/counter", function () {
expect(counter1.decorator(5)).to.equal("0.1.0.0.1");
expect(counter1.decorator(6)).to.equal("0.1.0.0.1.1");
const counter2 = new Counter({
ordered: true,
const counter2 = new OrderedCounter({
styleType: "lowerAlpha",
allowZeroLevel: true,
});
@ -1056,8 +1030,7 @@ describe("common/counter", function () {
expect(counter2.decorator(3)).to.equal("0.0.a");
expect(counter2.decorator(4)).to.equal("0.0.a.a");
const counter3 = new Counter({
ordered: true,
const counter3 = new OrderedCounter({
styleType: "cjkDecimal",
allowZeroLevel: true,
});
@ -1066,9 +1039,8 @@ describe("common/counter", function () {
expect(counter3.decorator(4)).to.equal("..一.一");
});
it("Counter.decorator with allowZeroLevel and ignoreTopLevel", function () {
const counter1 = new Counter({
ordered: true,
it("OrderedCounter.decorator with allowZeroLevel and ignoreTopLevel", function () {
const counter1 = new OrderedCounter({
styleType: "decimal",
allowZeroLevel: true,
ignoreTopLevel: 1,
@ -1089,8 +1061,7 @@ describe("common/counter", function () {
expect(counter1.decorator(2)).to.equal("3");
expect(counter1.decorator(6)).to.equal("3.0.0.0.1");
const counter2 = new Counter({
ordered: true,
const counter2 = new OrderedCounter({
styleType: "decimal",
allowZeroLevel: true,
ignoreTopLevel: 2,
@ -1105,8 +1076,7 @@ describe("common/counter", function () {
expect(counter2.decorator(3)).to.equal("2");
expect(counter2.decorator(6)).to.equal("2.0.0.1");
const counter3 = new Counter({
ordered: true,
const counter3 = new OrderedCounter({
styleType: "decimal",
allowZeroLevel: true,
ignoreTopLevel: 2,
@ -1118,8 +1088,7 @@ describe("common/counter", function () {
expect(counter3.decorator(3)).to.equal("2");
expect(counter3.decorator(6)).to.equal("2.0.0.1");
const counter4 = new Counter({
ordered: true,
const counter4 = new OrderedCounter({
styleType: "decimal",
trailingDelimiter: true,
allowZeroLevel: true,
@ -1139,24 +1108,8 @@ describe("common/counter", function () {
expect(counter4.decorator(6)).to.equal("3.0.0.1.");
});
it("Counter.decorator with maxRecLevel", function () {
const counter = new Counter({
ordered: false,
levelHeadings: ["H1", "H2", "H3", "H4", "H5", "H6"],
maxRecLevel: 4,
});
expect(counter.decorator(-1)).to.be.empty;
expect(counter.decorator(0)).to.empty;
expect(counter.decorator(1)).to.equal("H1");
expect(counter.decorator(2)).to.equal("H2");
expect(counter.decorator(3)).to.equal("H3");
expect(counter.decorator(4)).to.equal("H4");
expect(counter.decorator(5)).to.be.empty;
expect(counter.decorator(6)).to.be.empty;
expect(counter.decorator(7)).to.empty;
const counter1 = new Counter({
ordered: true,
it("OrderedCounter.decorator with maxRecLevel", function () {
const counter1 = new OrderedCounter({
styleType: "decimal",
maxRecLevel: 4,
});
@ -1176,8 +1129,7 @@ describe("common/counter", function () {
expect(counter1.decorator(2)).to.equal("1.3");
expect(counter1.decorator(6)).to.be.empty;
const counter2 = new Counter({
ordered: true,
const counter2 = new OrderedCounter({
styleType: "decimal",
allowZeroLevel: true,
ignoreTopLevel: 2,
@ -1193,8 +1145,7 @@ describe("common/counter", function () {
expect(counter2.decorator(3)).to.equal("2");
expect(counter2.decorator(6)).to.be.empty;
const counter3 = new Counter({
ordered: true,
const counter3 = new OrderedCounter({
styleType: "decimal",
allowZeroLevel: true,
ignoreTopLevel: 2,
@ -1207,8 +1158,7 @@ describe("common/counter", function () {
expect(counter3.decorator(3)).to.equal("2");
expect(counter3.decorator(6)).to.be.empty;
const counter4 = new Counter({
ordered: true,
const counter4 = new OrderedCounter({
styleType: "decimal",
trailingDelimiter: true,
allowZeroLevel: true,
@ -1229,3 +1179,271 @@ describe("common/counter", function () {
expect(counter4.decorator(6)).to.be.empty;
});
});
describe("IndependentCounter", function () {
const h1: Partial<IndependentDecoratorSettings> = {
styleType: "upperAlpha",
};
const h2: Partial<IndependentDecoratorSettings> = {
styleType: "lowerAlpha",
};
const h3: Partial<IndependentDecoratorSettings> = {
styleType: "decimal",
trailingDelimiter: true,
};
const h4: Partial<IndependentDecoratorSettings> = {
styleType: "decimalLeadingZero",
delimiter: ",",
trailingDelimiter: true,
};
const h5: Partial<IndependentDecoratorSettings> = {
styleType: "cjkDecimal",
delimiter: "、",
trailingDelimiter: true,
customTrailingDelimiter: "|",
};
const h6: Partial<IndependentDecoratorSettings> = {
styleType: "lowerGreek",
delimiter: "/",
trailingDelimiter: true,
customTrailingDelimiter: ")",
leadingDelimiter: true,
customLeadingDelimiter: "(",
};
it("IndependentCounter.decorator", function () {
const counter1 = new IndependentCounter({});
expect(counter1.decorator(-1)).to.be.empty;
expect(counter1.decorator(0)).to.be.empty;
expect(counter1.decorator(1)).to.equal("1");
expect(counter1.decorator(2)).to.equal("1");
expect(counter1.decorator(2)).to.equal("2");
expect(counter1.decorator(2)).to.equal("3");
expect(counter1.decorator(2)).to.equal("4");
expect(counter1.decorator(2)).to.equal("5");
expect(counter1.decorator(3)).to.equal("1");
expect(counter1.decorator(4)).to.equal("1");
expect(counter1.decorator(5)).to.equal("1");
expect(counter1.decorator(6)).to.equal("1");
expect(counter1.decorator(1)).to.equal("2");
expect(counter1.decorator(2)).to.equal("1");
expect(counter1.decorator(3)).to.equal("1");
expect(counter1.decorator(2)).to.equal("2");
const counter2 = new IndependentCounter({
h1,
h2,
h3,
h4,
h5,
h6,
});
expect(counter2.decorator(-1)).to.be.empty;
expect(counter2.decorator(0)).to.be.empty;
expect(counter2.decorator(1)).to.equal("A");
expect(counter2.decorator(2)).to.equal("a");
expect(counter2.decorator(2)).to.equal("b");
expect(counter2.decorator(2)).to.equal("c");
expect(counter2.decorator(2)).to.equal("d");
expect(counter2.decorator(2)).to.equal("e");
expect(counter2.decorator(3)).to.equal("1.");
expect(counter2.decorator(4)).to.equal("01,");
expect(counter2.decorator(5)).to.equal("一|");
expect(counter2.decorator(6)).to.equal("(α)");
expect(counter2.decorator(1)).to.equal("B");
expect(counter2.decorator(2)).to.equal("a");
expect(counter2.decorator(3)).to.equal("1.");
expect(counter2.decorator(2)).to.equal("b");
});
it("IndependentCounter.decorator with ignoreTopLevel", function () {
const counter1 = new IndependentCounter({
ignoreTopLevel: 1,
h1: h1,
h2: h2,
h3: h3,
h4: h4,
h5: h5,
h6: h6,
});
expect(counter1.decorator(1)).to.be.empty;
expect(counter1.decorator(2)).to.equal("a");
expect(counter1.decorator(3)).to.equal("1.");
expect(counter1.decorator(4)).to.equal("01,");
expect(counter1.decorator(5)).to.equal("一|");
expect(counter1.decorator(6)).to.equal("(α)");
expect(counter1.decorator(7)).to.be.empty;
expect(counter1.decorator(2)).to.equal("b");
expect(counter1.decorator(3)).to.equal("1.");
expect(counter1.decorator(4)).to.equal("01,");
expect(counter1.decorator(5)).to.equal("一|");
expect(counter1.decorator(6)).to.equal("(α)");
expect(counter1.decorator(7)).to.be.empty;
expect(counter1.decorator(2)).to.equal("c");
expect(counter1.decorator(6)).to.equal("(α)");
const counter2 = new IndependentCounter({
ignoreTopLevel: 2,
h1,
h2,
h3,
h4,
h5,
h6,
});
expect(counter2.decorator(1)).to.be.empty;
expect(counter2.decorator(2)).to.be.empty;
expect(counter2.decorator(3)).to.equal("1.");
expect(counter2.decorator(4)).to.equal("01,");
expect(counter2.decorator(5)).to.equal("一|");
expect(counter2.decorator(6)).to.equal("(α)");
expect(counter2.decorator(7)).to.be.empty;
expect(counter2.decorator(3)).to.equal("2.");
expect(counter2.decorator(6)).to.equal("(α)");
});
it("IndependentCounter.decorator with allowZeroLevel", function () {
const counter = new IndependentCounter({
allowZeroLevel: true,
h1,
h2,
h3,
h4,
h5,
h6,
});
expect(counter.decorator(3)).to.equal("1.");
expect(counter.decorator(4)).to.equal("01,");
});
it("IndependentCounter.decorator with maxRecLevel", function () {
const counter1 = new IndependentCounter({
maxRecLevel: 4,
h1,
h2,
h3,
h4,
h5,
h6,
});
expect(counter1.decorator(1)).to.equal("A");
expect(counter1.decorator(2)).to.equal("a");
expect(counter1.decorator(3)).to.equal("1.");
expect(counter1.decorator(4)).to.equal("01,");
expect(counter1.decorator(5)).to.be.empty;
expect(counter1.decorator(6)).to.be.empty;
expect(counter1.decorator(7)).to.be.empty;
expect(counter1.decorator(2)).to.equal("b");
expect(counter1.decorator(3)).to.equal("1.");
expect(counter1.decorator(4)).to.equal("01,");
expect(counter1.decorator(5)).to.be.empty;
expect(counter1.decorator(6)).to.be.empty;
expect(counter1.decorator(2)).to.equal("c");
expect(counter1.decorator(6)).to.be.empty;
const counter2 = new IndependentCounter({
maxRecLevel: 4,
ignoreTopLevel: 2,
h1,
h2,
h3,
h4,
h5,
h6,
});
expect(counter2.decorator(1)).to.be.empty;
expect(counter2.decorator(2)).to.be.empty;
expect(counter2.decorator(3)).to.equal("1.");
expect(counter2.decorator(4)).to.equal("01,");
expect(counter2.decorator(5)).to.be.empty;
expect(counter2.decorator(6)).to.be.empty;
expect(counter2.decorator(7)).to.be.empty;
expect(counter2.decorator(3)).to.equal("2.");
expect(counter2.decorator(4)).to.equal("01,");
expect(counter2.decorator(6)).to.be.empty;
});
it("IndependentCounter.decorator with orderedRecLevel", function () {
const counter1 = new IndependentCounter({
orderedRecLevel: 3,
h1,
h2,
h3,
h4,
h5,
h6,
});
expect(counter1.decorator(1)).to.equal("A");
expect(counter1.decorator(2)).to.equal("a");
expect(counter1.decorator(3)).to.equal("1.");
expect(counter1.decorator(4)).to.equal("1.1.");
expect(counter1.decorator(5)).to.equal("1.1.1.");
expect(counter1.decorator(6)).to.equal("1.1.1.1.");
expect(counter1.decorator(7)).to.be.empty;
expect(counter1.decorator(2)).to.equal("b");
expect(counter1.decorator(3)).to.equal("1.");
expect(counter1.decorator(4)).to.equal("1.1.");
expect(counter1.decorator(4)).to.equal("1.2.");
expect(counter1.decorator(4)).to.equal("1.3.");
expect(counter1.decorator(5)).to.equal("1.3.1.");
expect(counter1.decorator(6)).to.equal("1.3.1.1.");
expect(counter1.decorator(6)).to.equal("1.3.1.2.");
expect(counter1.decorator(3)).to.equal("2.");
const counter2 = new IndependentCounter({
orderedRecLevel: 3,
allowZeroLevel: true,
h1,
h2,
h3,
h4,
h5,
h6,
});
expect(counter2.decorator(1)).to.equal("A");
expect(counter2.decorator(2)).to.equal("a");
expect(counter2.decorator(3)).to.equal("1.");
expect(counter2.decorator(4)).to.equal("1.1.");
expect(counter2.decorator(5)).to.equal("1.1.1.");
expect(counter2.decorator(6)).to.equal("1.1.1.1.");
expect(counter2.decorator(7)).to.be.empty;
expect(counter2.decorator(2)).to.equal("b");
expect(counter2.decorator(3)).to.equal("1.");
expect(counter2.decorator(4)).to.equal("1.1.");
expect(counter2.decorator(4)).to.equal("1.2.");
expect(counter2.decorator(4)).to.equal("1.3.");
expect(counter2.decorator(6)).to.equal("1.3.0.1.");
expect(counter2.decorator(6)).to.equal("1.3.0.2.");
expect(counter2.decorator(5)).to.equal("1.3.1.");
const counter3 = new IndependentCounter({
orderedRecLevel: 3,
ignoreTopLevel: 2,
h1,
h2,
h3,
h4,
h5,
h6,
});
expect(counter3.decorator(1)).to.be.empty;
expect(counter3.decorator(2)).to.be.empty;
expect(counter3.decorator(3)).to.equal("1.");
expect(counter3.decorator(4)).to.equal("1.1.");
expect(counter3.decorator(5)).to.equal("1.1.1.");
expect(counter3.decorator(6)).to.equal("1.1.1.1.");
expect(counter3.decorator(7)).to.be.empty;
expect(counter3.decorator(3)).to.equal("2.");
expect(counter3.decorator(6)).to.equal("2.1.1.1.");
});
});
});

6
types.d.ts vendored
View file

@ -10,6 +10,7 @@ type _TupleOf<T, N extends number, R extends unknown[]> = R["length"] extends N
type LevelTuple = TupleOf<number, 6>;
type HeadingTuple = TupleOf<string, 6>;
type DecoratorMode = "orderd" | "independent" | "unordered";
type OpacityOptions = 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | 90 | 100;
type PostionOptions = "before" | "before-inside" | "after" | "after-inside";
type RenderPolicy = "partial" | "full";
@ -39,3 +40,8 @@ interface CSSEnabledStatus {
mode: boolean | null;
all: boolean | null;
}
interface Counter {
decorator(level: number): string;
handler(level: number): unknown;
}