private-schrijfsels-en-noti.../.obsidian/plugins/novel-word-count/main.js

2064 lines
80 KiB
JavaScript

/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// main.ts
var main_exports = {};
__export(main_exports, {
default: () => NovelWordCountPlugin
});
module.exports = __toCommonJS(main_exports);
// logic/debug.ts
var DebugHelper = class {
constructor() {
this.debugMode = false;
this.idCounter = 0;
}
setDebugMode(debug) {
this.debugMode = debug;
}
debug(...args) {
if (!this.debugMode) {
return;
}
console.log("novel-word-count:", ...args);
}
error(message) {
if (!this.debugMode) {
return;
}
console.error(message);
}
debugStart(name) {
if (!this.debugMode) {
return () => {
};
}
var qualifiedName = `novel-word-count|${name} (${++this.idCounter})`;
console.time(qualifiedName);
return () => console.timeEnd(qualifiedName);
}
};
// logic/event.ts
var import_obsidian = require("obsidian");
// logic/cancellation.ts
var CANCEL = Symbol("Cancel");
var CancellationToken = class {
constructor() {
this._isCancelled = false;
}
get isCancelled() {
return this._isCancelled;
}
[CANCEL]() {
this._isCancelled = true;
}
};
var CancellationTokenSource = class {
constructor() {
this.token = new CancellationToken();
}
cancel() {
this.token[CANCEL]();
}
};
// logic/event.ts
var EventHelper = class {
constructor(plugin, app, debugHelper, fileHelper) {
this.plugin = plugin;
this.app = app;
this.debugHelper = debugHelper;
this.fileHelper = fileHelper;
this.cancellationSources = [];
}
async handleEvents() {
const debouncedFileModified = (0, import_obsidian.debounce)(async (file) => {
const countToken = this.registerNewCountToken();
await this.fileHelper.updateFileCounts(
file,
this.plugin.savedData.cachedCounts,
countToken.token
);
this.cancelToken(countToken);
await this.plugin.updateDisplayedCounts(file);
this.plugin.saveSettingsDebounced();
}, 500);
this.plugin.registerEvent(
this.app.metadataCache.on("changed", async (file) => {
this.debugHelper.debug(
"[changed] metadataCache hook fired, scheduling file for analysis",
file.path
);
debouncedFileModified(file);
})
);
this.app.workspace.onLayoutReady(() => {
this.plugin.registerEvent(
this.app.vault.on("create", async (file) => {
this.debugHelper.debug(
"[create] vault hook fired, analyzing file",
file.path
);
const countToken = this.registerNewCountToken();
await this.fileHelper.updateFileCounts(
file,
this.plugin.savedData.cachedCounts,
countToken.token
);
this.cancelToken(countToken);
await this.plugin.updateDisplayedCounts(file);
await this.plugin.saveSettings();
})
);
this.plugin.registerEvent(
this.app.vault.on("modify", async (file) => {
this.debugHelper.debug(
"[modify] vault hook fired, scheduling file for analysis",
file.path
);
debouncedFileModified(file);
})
);
});
this.plugin.registerEvent(
this.app.vault.on("delete", async (file) => {
this.debugHelper.debug(
"[delete] vault hook fired, forgetting file",
file.path
);
this.fileHelper.removeFileCounts(
file.path,
this.plugin.savedData.cachedCounts
);
await this.plugin.updateDisplayedCounts(file);
await this.plugin.saveSettings();
})
);
this.plugin.registerEvent(
this.app.vault.on("rename", async (file, oldPath) => {
if (file instanceof import_obsidian.TFolder) {
return;
}
this.debugHelper.debug(
"[rename] vault hook fired, recounting file",
file.path
);
this.fileHelper.removeFileCounts(
oldPath,
this.plugin.savedData.cachedCounts
);
const countToken = this.registerNewCountToken();
await this.fileHelper.updateFileCounts(
file,
this.plugin.savedData.cachedCounts,
countToken.token
);
this.cancelToken(countToken);
await this.plugin.updateDisplayedCounts(file);
await this.plugin.saveSettings();
})
);
const reshowCountsIfNeeded = async (hookName) => {
this.debugHelper.debug(`[${hookName}] hook fired`);
const fileExplorerLeaf = await this.plugin.getFileExplorerLeaf();
if (this.isContainerTouched(fileExplorerLeaf)) {
this.debugHelper.debug(
"container already touched, skipping display update"
);
return;
}
this.debugHelper.debug("container is clean, updating display");
await this.plugin.updateDisplayedCounts();
};
this.plugin.registerEvent(
this.app.workspace.on(
"layout-change",
(0, import_obsidian.debounce)(reshowCountsIfNeeded.bind(this, "layout-change"), 1e3)
)
);
}
isContainerTouched(leaf) {
const container = leaf.view.containerEl;
return container.className.includes("novel-word-count--");
}
async reinitializeAllCounts() {
this.cancelAllCountTokens();
const countToken = this.registerNewCountToken();
this.debugHelper.debug("refreshAllCounts");
this.plugin.savedData.cachedCounts = await this.fileHelper.initializeAllFileCounts(
countToken.token
);
this.cancelToken(countToken);
await this.plugin.saveSettings();
}
/*
CANCELLATION HANDLING
*/
registerNewCountToken() {
const cancellationSource = new CancellationTokenSource();
this.cancellationSources.push(cancellationSource);
return cancellationSource;
}
cancelToken(source) {
source.cancel();
if (this.cancellationSources.includes(source)) {
this.cancellationSources.splice(
this.cancellationSources.indexOf(source),
1
);
}
}
cancelAllCountTokens() {
for (const source of this.cancellationSources) {
source.cancel();
}
this.cancellationSources = [];
}
};
// logic/file.ts
var import_obsidian2 = require("obsidian");
// logic/settings.ts
var $CountType = /* @__PURE__ */ (($CountType2) => {
$CountType2["None"] = "none";
$CountType2["Word"] = "word";
$CountType2["Page"] = "page";
$CountType2["PageDecimal"] = "pagedecimal";
$CountType2["Linebreak"] = "linebreak";
$CountType2["ReadTime"] = "readtime";
$CountType2["PercentGoal"] = "percentgoal";
$CountType2["Note"] = "note";
$CountType2["Character"] = "character";
$CountType2["Link"] = "link";
$CountType2["Embed"] = "embed";
$CountType2["Alias"] = "alias";
$CountType2["Created"] = "created";
$CountType2["Modified"] = "modified";
$CountType2["FileSize"] = "filesize";
$CountType2["FrontmatterKey"] = "frontmatterKey";
$CountType2["TrackSession"] = "tracksession";
return $CountType2;
})($CountType || {});
var COUNT_TYPES = Object.values($CountType);
var SESSION_COUNT_TYPES = [
"word" /* Word */,
"page" /* Page */,
"pagedecimal" /* PageDecimal */,
"linebreak" /* Linebreak */,
"note" /* Note */,
"character" /* Character */
];
var COUNT_TYPE_DISPLAY_STRINGS = {
["none" /* None */]: "None",
["word" /* Word */]: "Word Count",
["page" /* Page */]: "Page Count",
["pagedecimal" /* PageDecimal */]: "Page Count (decimal)",
["linebreak" /* Linebreak */]: "Line Break Count",
["readtime" /* ReadTime */]: "Reading Time",
["percentgoal" /* PercentGoal */]: "% of Word Goal",
["note" /* Note */]: "Note Count",
["character" /* Character */]: "Character Count",
["link" /* Link */]: "Link Count",
["embed" /* Embed */]: "Embed Count",
["alias" /* Alias */]: "First Alias",
["created" /* Created */]: "Created Date",
["modified" /* Modified */]: "Last Updated Date",
["filesize" /* FileSize */]: "File Size",
["frontmatterKey" /* FrontmatterKey */]: "Frontmatter Key",
["tracksession" /* TrackSession */]: "Track Session"
};
var COUNT_TYPE_DESCRIPTIONS = {
["none" /* None */]: "Hidden.",
["word" /* Word */]: "Total words.",
["page" /* Page */]: "Total pages, rounded up.",
["pagedecimal" /* PageDecimal */]: "Total pages, precise to 2 digits after the decimal.",
["linebreak" /* Linebreak */]: "Newlines (\xB6), including empty lines.",
["readtime" /* ReadTime */]: "Estimated time to read the note.",
["percentgoal" /* PercentGoal */]: "Set a word goal by adding the 'word-goal' property to a note.",
["note" /* Note */]: "Total notes.",
["character" /* Character */]: "Total characters (letters, symbols, numbers, and spaces).",
["link" /* Link */]: "Total links to other notes.",
["embed" /* Embed */]: "Total embedded images, files, and notes.",
["alias" /* Alias */]: "The first alias property of each note.",
["created" /* Created */]: "Creation date. (On folders: earliest creation date of any note.)",
["modified" /* Modified */]: "Date of last edit. (On folders: latest edit date of any note.)",
["filesize" /* FileSize */]: "Total size on hard drive.",
["frontmatterKey" /* FrontmatterKey */]: "Key in the frontmatter block.",
["tracksession" /* TrackSession */]: "Track progress since last Obsidian startup, plugin init, settings change, or recount"
};
var UNFORMATTABLE_COUNT_TYPES = [
"none" /* None */,
"alias" /* Alias */,
"filesize" /* FileSize */,
"readtime" /* ReadTime */
];
var COUNT_TYPE_DEFAULT_SHORT_SUFFIXES = {
["word" /* Word */]: "w",
["page" /* Page */]: "p",
["pagedecimal" /* PageDecimal */]: "p",
["linebreak" /* Linebreak */]: "\xB6",
["percentgoal" /* PercentGoal */]: "%",
["note" /* Note */]: "n",
["character" /* Character */]: "ch",
["link" /* Link */]: "x",
["embed" /* Embed */]: "em",
["created" /* Created */]: "/c",
["modified" /* Modified */]: "/u",
["frontmatterKey" /* FrontmatterKey */]: "",
["tracksession" /* TrackSession */]: "/s"
};
function getDescription(countType) {
return `[${COUNT_TYPE_DISPLAY_STRINGS[countType]}] ${COUNT_TYPE_DESCRIPTIONS[countType]}`;
}
var ALIGNMENT_TYPES = [
"inline" /* Inline */,
"right" /* Right */,
"below" /* Below */
];
var DEFAULT_SETTINGS = {
// FORMATTING
useAdvancedFormatting: false,
// NOTES
countType: "word" /* Word */,
countConfig: {
customSuffix: "w",
$sessionCountType: "word" /* Word */
},
countType2: "none" /* None */,
countConfig2: {
$sessionCountType: "word" /* Word */
},
countType3: "none" /* None */,
countConfig3: {
$sessionCountType: "word" /* Word */
},
pipeSeparator: "|",
abbreviateDescriptions: false,
alignment: "inline" /* Inline */,
// FOLDERS
showSameCountsOnFolders: true,
folderCountType: "word" /* Word */,
folderCountConfig: {
customSuffix: "w",
$sessionCountType: "word" /* Word */
},
folderCountType2: "none" /* None */,
folderCountConfig2: {
$sessionCountType: "word" /* Word */
},
folderCountType3: "none" /* None */,
folderCountConfig3: {
$sessionCountType: "word" /* Word */
},
folderPipeSeparator: "|",
folderAbbreviateDescriptions: false,
folderAlignment: "inline" /* Inline */,
// ROOT
showSameCountsOnRoot: true,
rootCountType: "word" /* Word */,
rootCountConfig: {
customSuffix: "w",
$sessionCountType: "word" /* Word */
},
rootCountType2: "none" /* None */,
rootCountConfig2: {
$sessionCountType: "word" /* Word */
},
rootCountType3: "none" /* None */,
rootCountConfig3: {
$sessionCountType: "word" /* Word */
},
rootPipeSeparator: "|",
rootAbbreviateDescriptions: false,
// ADVANCED
showAdvanced: false,
labelOpacity: 0.75,
wordsPerMinute: 265,
charsPerMinute: 500,
wordsPerPage: 300,
charsPerPage: 1500,
charsPerPageIncludesWhitespace: false,
characterCountType: "AllCharacters" /* StringLength */,
pageCountType: "ByWords" /* ByWords */,
includeDirectories: "",
excludeComments: false,
excludeCodeBlocks: false,
excludeNonVisibleLinkPortions: false,
excludeFootnotes: false,
momentDateFormat: "",
debugMode: false
};
// logic/parser.ts
var cjkRegex = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/gu;
var allSymbolsRegex = /[\p{S}\p{P}]/gu;
function countMarkdown(content, config) {
content = removeNonCountedContent(content, config);
let wordSequences = content.replace(cjkRegex, " ").replace(allSymbolsRegex, "").trim().split(/\s+/);
if (wordSequences.length === 1 && wordSequences[0] === "") {
wordSequences = [];
}
let lineSequences = content.split("\n");
if (lineSequences.length === 1 && lineSequences[0] === "") {
lineSequences = [];
}
const result = {
charCount: content.length,
nonWhitespaceCharCount: countNonWhitespaceCharacters(content),
spaceDelimitedWordCount: wordSequences.length,
cjkWordCount: (content.match(cjkRegex) || []).length,
newlineCount: lineSequences.length
};
return result;
}
var whitespaceRegex = /\s/g;
function countNonWhitespaceCharacters(content) {
return content.replace(whitespaceRegex, "").length;
}
function removeNonCountedContent(content, config) {
if (config.excludeCodeBlocks) {
content = content.replace(/(```.+?```)/gims, "");
}
if (config.excludeComments) {
content = content.replace(/(%%.+?%%|<!--.+?-->)/gims, "");
}
if (config.excludeNonVisibleLinkPortions) {
content = content.replace(/\[([^\]]*?)\]\([^\)]*?\)/gim, "$1");
content = content.replace(/\[\[(.*?)\]\]/gim, (_, $1) => {
return !$1 ? "" : $1.includes("|") ? $1.slice($1.indexOf("|") + 1) : $1;
});
}
if (config.excludeFootnotes) {
content = content.replace(/\[\^.+?\]: .*/gim, "");
content = content.replace(/\[\^.+?\]/gim, "");
}
return content;
}
// logic/canvas.ts
var CanvasHelper = class {
constructor(debug) {
this.debug = debug;
}
getCanvasText(file, content) {
try {
const canvas = JSON.parse(content);
const texts = canvas.nodes.map((node) => node.text).filter((text) => !!text);
return texts.join("\n");
} catch (ex) {
this.debug.error(`Unable to parse canvas file [${file.name}]: ${ex}`);
return "";
}
}
};
// logic/file.ts
var FileHelper = class {
constructor(app, plugin) {
this.app = app;
this.plugin = plugin;
this.debugHelper = new DebugHelper();
this.canvasHelper = new CanvasHelper(this.debugHelper);
this.pathIncludeMatchers = [];
this.pathExcludeMatchers = [];
this.FileTypeAllowlist = /* @__PURE__ */ new Set([
"",
// Markdown extensions
"markdown",
"md",
"mdml",
"mdown",
"mdtext",
"mdtxt",
"mdwn",
"mkd",
"mkdn",
// Obsidian canvas
"canvas",
// Text files
"txt",
"text",
"rtf",
// MD with embedded code
"qmd",
"rmd",
// MD for screenwriters
"fountain",
// LaTeX files
"tex"
]);
}
get settings() {
return this.plugin.settings;
}
get vault() {
return this.app.vault;
}
async initializeAllFileCounts(cancellationToken) {
const debugEnd = this.debugHelper.debugStart("getAllFileCounts");
const files = this.vault.getFiles();
if (typeof this.plugin.settings.includeDirectories === "string" && this.plugin.settings.includeDirectories.trim() !== "*" && this.plugin.settings.includeDirectories.trim() !== "") {
const allMatchers = this.plugin.settings.includeDirectories.trim().split(",").map((matcher) => matcher.trim());
const includeMatchers = allMatchers.filter(
(matcher) => !matcher.startsWith("!")
);
const excludeMatchers = allMatchers.filter((matcher) => matcher.startsWith("!")).map((matcher) => matcher.slice(1));
const matchedFiles = files.filter(
(file) => (includeMatchers.length === 0 ? true : includeMatchers.some((matcher) => file.path.includes(matcher))) && !excludeMatchers.some((matcher) => file.path.includes(matcher))
);
if (matchedFiles.length > 0) {
this.pathIncludeMatchers = includeMatchers;
this.pathExcludeMatchers = excludeMatchers;
} else {
this.pathIncludeMatchers = [];
this.pathExcludeMatchers = [];
this.debugHelper.debug(
"No files matched by includeDirectories setting. Defaulting to all files."
);
}
}
const counts = {};
for (const file of files) {
if (cancellationToken.isCancelled) {
break;
}
this.setCounts(counts, file, true);
}
debugEnd();
return counts;
}
getCachedDataForPath(counts, path) {
if (counts.hasOwnProperty(path)) {
return counts[path];
}
const childPaths = this.getChildPaths(counts, path);
const directoryDefault = {
isCountable: false,
targetNodeType: this.isRoot(path) ? "root" /* Root */ : "directory" /* Directory */,
noteCount: 0,
wordCount: 0,
wordCountTowardGoal: 0,
wordGoal: 0,
pageCount: 0,
characterCount: 0,
nonWhitespaceCharacterCount: 0,
newlineCount: 0,
readingTimeInMinutes: 0,
linkCount: 0,
embedCount: 0,
aliases: null,
createdDate: 0,
modifiedDate: 0,
sizeInBytes: 0,
sessionStart: {
noteCount: 0,
pageCount: 0,
wordCount: 0,
characterCount: 0,
nonWhitespaceCharacterCount: 0,
newlineCount: 0
}
};
return childPaths.reduce((total, childPath) => {
const childCount = this.getCachedDataForPath(counts, childPath);
return {
isCountable: total.isCountable || childCount.isCountable,
targetNodeType: total.targetNodeType,
noteCount: total.noteCount + childCount.noteCount,
linkCount: total.linkCount + childCount.linkCount,
embedCount: total.embedCount + childCount.embedCount,
aliases: [],
wordCount: total.wordCount + childCount.wordCount,
wordCountTowardGoal: total.wordCountTowardGoal + childCount.wordCountTowardGoal,
wordGoal: total.wordGoal + childCount.wordGoal,
pageCount: total.pageCount + childCount.pageCount,
characterCount: total.characterCount + childCount.characterCount,
nonWhitespaceCharacterCount: total.nonWhitespaceCharacterCount + childCount.nonWhitespaceCharacterCount,
newlineCount: total.newlineCount + childCount.newlineCount,
readingTimeInMinutes: total.readingTimeInMinutes + childCount.readingTimeInMinutes,
createdDate: total.createdDate === 0 ? childCount.createdDate : Math.min(total.createdDate, childCount.createdDate),
modifiedDate: Math.max(total.modifiedDate, childCount.modifiedDate),
sizeInBytes: total.sizeInBytes + childCount.sizeInBytes,
sessionStart: {
noteCount: total.sessionStart.noteCount + childCount.sessionStart.noteCount,
pageCount: total.sessionStart.pageCount + childCount.sessionStart.pageCount,
wordCount: total.sessionStart.wordCount + childCount.sessionStart.wordCount,
characterCount: total.sessionStart.characterCount + childCount.sessionStart.characterCount,
nonWhitespaceCharacterCount: total.sessionStart.nonWhitespaceCharacterCount + childCount.sessionStart.nonWhitespaceCharacterCount,
newlineCount: total.sessionStart.newlineCount + childCount.sessionStart.newlineCount
}
};
}, directoryDefault);
}
setDebugMode(debug) {
this.debugHelper.setDebugMode(debug);
}
removeFileCounts(path, counts) {
this.removeCounts(counts, path);
for (const childPath of this.getChildPaths(counts, path)) {
this.removeCounts(counts, childPath);
}
}
async updateFileCounts(abstractFile, counts, cancellationToken) {
if (abstractFile instanceof import_obsidian2.TFolder) {
for (const child of abstractFile.children) {
await this.updateFileCounts(child, counts, cancellationToken);
}
return;
}
if (abstractFile instanceof import_obsidian2.TFile) {
await this.setCounts(counts, abstractFile, false);
}
}
countEmbeds(metadata) {
var _a, _b;
return (_b = (_a = metadata == null ? void 0 : metadata.embeds) == null ? void 0 : _a.length) != null ? _b : 0;
}
countLinks(metadata) {
var _a, _b;
return (_b = (_a = metadata == null ? void 0 : metadata.links) == null ? void 0 : _a.length) != null ? _b : 0;
}
getChildPaths(counts, path) {
const childPaths = Object.keys(counts).filter(
(countPath) => path === "/" || countPath.startsWith(path + "/")
);
return childPaths;
}
isRoot(path) {
return !path || path === "/";
}
removeCounts(counts, path) {
delete counts[path];
}
async setCounts(counts, file, startNewSession) {
var _a, _b;
const metadata = this.app.metadataCache.getFileCache(
file
);
const shouldCountFile = this.shouldCountFile(file, metadata);
const existingSession = (_a = counts[file.path]) == null ? void 0 : _a.sessionStart;
counts[file.path] = {
isCountable: shouldCountFile,
targetNodeType: "file" /* File */,
noteCount: 0,
wordCount: 0,
wordCountTowardGoal: 0,
wordGoal: 0,
pageCount: 0,
characterCount: 0,
nonWhitespaceCharacterCount: 0,
newlineCount: 0,
readingTimeInMinutes: 0,
linkCount: 0,
embedCount: 0,
aliases: [],
createdDate: file.stat.ctime,
modifiedDate: file.stat.mtime,
sizeInBytes: file.stat.size,
sessionStart: startNewSession || !existingSession ? {
noteCount: 0,
pageCount: 0,
wordCount: 0,
characterCount: 0,
nonWhitespaceCharacterCount: 0,
newlineCount: 0
} : existingSession
};
if (!shouldCountFile) {
return;
}
let content = await this.vault.cachedRead(file);
if (file.extension.toLowerCase() === "canvas") {
content = this.canvasHelper.getCanvasText(file, content);
} else {
content = this.trimFrontmatter(content, metadata);
}
const countResult = countMarkdown(content, {
excludeCodeBlocks: this.settings.excludeCodeBlocks,
excludeComments: this.settings.excludeComments,
excludeNonVisibleLinkPortions: this.settings.excludeNonVisibleLinkPortions,
excludeFootnotes: this.settings.excludeFootnotes
});
const combinedWordCount = countResult.cjkWordCount + countResult.spaceDelimitedWordCount;
const wordGoal = this.getWordGoal(metadata);
const cjkReadingTime = countResult.cjkWordCount / (this.settings.charsPerMinute || 500);
const spaceDelimitedReadingTime = countResult.spaceDelimitedWordCount / (this.settings.wordsPerMinute || 265);
const readingTimeInMinutes = cjkReadingTime + spaceDelimitedReadingTime;
let pageCount = 0;
if (this.settings.pageCountType === "ByWords" /* ByWords */) {
const wordsPerPage = Number(this.settings.wordsPerPage);
const wordsPerPageValid = !isNaN(wordsPerPage) && wordsPerPage > 0;
pageCount = combinedWordCount / (wordsPerPageValid ? wordsPerPage : 300);
} else if (this.settings.pageCountType === "ByChars" /* ByChars */ && !this.settings.charsPerPageIncludesWhitespace) {
const charsPerPage = Number(this.settings.charsPerPage);
const charsPerPageValid = !isNaN(charsPerPage) && charsPerPage > 0;
pageCount = countResult.nonWhitespaceCharCount / (charsPerPageValid ? charsPerPage : 1500);
} else if (this.settings.pageCountType === "ByChars" /* ByChars */ && this.settings.charsPerPageIncludesWhitespace) {
const charsPerPage = Number(this.settings.charsPerPage);
const charsPerPageValid = !isNaN(charsPerPage) && charsPerPage > 0;
pageCount = countResult.charCount / (charsPerPageValid ? charsPerPage : 1500);
}
Object.assign(counts[file.path], {
noteCount: 1,
wordCount: combinedWordCount,
wordCountTowardGoal: wordGoal !== null ? combinedWordCount : 0,
wordGoal,
pageCount,
characterCount: countResult.charCount,
nonWhitespaceCharacterCount: countResult.nonWhitespaceCharCount,
newlineCount: countResult.newlineCount,
readingTimeInMinutes,
linkCount: this.countLinks(metadata),
embedCount: this.countEmbeds(metadata),
aliases: (0, import_obsidian2.parseFrontMatterAliases)(metadata == null ? void 0 : metadata.frontmatter),
frontmatter: metadata == null ? void 0 : metadata.frontmatter,
sessionStart: {
...(_b = counts[file.path]) == null ? void 0 : _b.sessionStart,
...startNewSession ? {
noteCount: 1,
pageCount,
wordCount: combinedWordCount,
characterCount: countResult.charCount,
nonWhitespaceCharacterCount: countResult.nonWhitespaceCharCount,
newlineCount: countResult.newlineCount
} : {}
}
});
}
getWordGoal(metadata) {
const goal = metadata && metadata.frontmatter && metadata.frontmatter["word-goal"];
if (!goal || isNaN(Number(goal))) {
return null;
}
return Number(goal);
}
trimFrontmatter(content, metadata) {
let meaningfulContent = content;
const hasFrontmatter = !!metadata && !!metadata.frontmatter;
if (hasFrontmatter) {
const frontmatterPos = metadata.frontmatterPosition || metadata.frontmatter.position;
meaningfulContent = frontmatterPos && frontmatterPos.start && frontmatterPos.end ? meaningfulContent.slice(0, frontmatterPos.start.offset) + meaningfulContent.slice(frontmatterPos.end.offset) : meaningfulContent;
}
return meaningfulContent;
}
shouldCountFile(file, metadata) {
if (this.pathIncludeMatchers.length > 0 && !this.pathIncludeMatchers.some((matcher) => file.path.includes(matcher))) {
return false;
}
if (this.pathExcludeMatchers.length > 0 && this.pathExcludeMatchers.some((matcher) => file.path.includes(matcher))) {
return false;
}
if (!this.FileTypeAllowlist.has(file.extension.toLowerCase())) {
return false;
}
if (!metadata) {
return true;
}
if (metadata.frontmatter && metadata.frontmatter.hasOwnProperty("wordcount") && (metadata.frontmatter.wordcount === null || metadata.frontmatter.wordcount === false || metadata.frontmatter.wordcount === "false")) {
return false;
}
const tags = (0, import_obsidian2.getAllTags)(metadata).map((tag) => tag.toLowerCase());
if (tags.length && (tags.includes("#excalidraw") || tags.filter((tag) => tag.startsWith("#exclude")).map((tag) => tag.replace(/[-_]/g, "")).includes("#excludefromwordcount"))) {
return false;
}
return true;
}
};
// logic/locale_format.ts
var locales = [...navigator.languages, "en-US"];
var NumberFormatDefault = new Intl.NumberFormat(locales);
var NumberFormatDecimal = new Intl.NumberFormat(locales, {
minimumFractionDigits: 1,
maximumFractionDigits: 2
});
var NumberFormatFileSize = new Intl.NumberFormat(locales, {
minimumFractionDigits: 0,
maximumFractionDigits: 2
});
// logic/filesize.ts
var formatThresholds = [{
suffix: "B",
suffixLong: " B",
divisor: 1
}, {
suffix: "kB",
suffixLong: " kB",
divisor: 1e3
}, {
suffix: "MB",
suffixLong: " MB",
divisor: 1e6
}, {
suffix: "GB",
suffixLong: " GB",
divisor: 1e9
}, {
suffix: "TB",
suffixLong: " TB",
divisor: 1e12
}];
var FileSizeHelper = class {
formatFileSize(bytes, shouldAbbreviate) {
const largestThreshold = formatThresholds.last();
for (const formatThreshold of formatThresholds) {
if (bytes < formatThreshold.divisor * 1e3 || formatThreshold === largestThreshold) {
const units = bytes / formatThreshold.divisor;
const suffix = shouldAbbreviate ? formatThreshold.suffix : formatThreshold.suffixLong;
return `${NumberFormatFileSize.format(units)}${suffix}`;
}
}
return `?B`;
}
};
// logic/readtime.ts
var ReadTimeHelper = class {
formatReadTime(minutes, shouldAbbreviate) {
const final = shouldAbbreviate ? "" : " read";
if (minutes * 60 < 1) {
return `0m${final}`;
}
if (minutes < 1) {
const seconds = Math.round(minutes * 60);
return `${seconds}s${final}`;
}
if (minutes < 60) {
return `${Math.round(minutes)}m${final}`;
}
const hours = NumberFormatDefault.format(Math.floor(minutes / 60));
const remainder = Math.floor(minutes) % 60;
return remainder === 0 ? `${hours}h${final}` : `${hours}h${remainder}m${final}`;
}
};
// logic/node_label.ts
var import_obsidian3 = require("obsidian");
var NodeLabelHelper = class {
constructor(plugin) {
this.plugin = plugin;
this.fileSizeHelper = new FileSizeHelper();
this.readTimeHelper = new ReadTimeHelper();
this.unconditionalCountTypes = [
"created" /* Created */,
"filesize" /* FileSize */,
"modified" /* Modified */
];
}
get settings() {
return this.plugin.settings;
}
getNodeLabel(counts) {
let countTypes;
let abbreviateDescriptions;
let separator;
const noteCountTypes = [
this.getCountTypeWithSuffix(
this.settings.countType,
this.settings.countConfig
),
this.getCountTypeWithSuffix(
this.settings.countType2,
this.settings.countConfig2
),
this.getCountTypeWithSuffix(
this.settings.countType3,
this.settings.countConfig3
)
];
const noteAbbreviateDescriptions = this.settings.abbreviateDescriptions;
const noteSeparator = this.settings.useAdvancedFormatting ? this.settings.pipeSeparator : "|";
switch (counts.targetNodeType) {
case "root" /* Root */:
if (this.settings.showSameCountsOnRoot) {
countTypes = noteCountTypes;
abbreviateDescriptions = noteAbbreviateDescriptions;
separator = noteSeparator;
break;
}
countTypes = [
this.getCountTypeWithSuffix(
this.settings.rootCountType,
this.settings.rootCountConfig
),
this.getCountTypeWithSuffix(
this.settings.rootCountType2,
this.settings.rootCountConfig2
),
this.getCountTypeWithSuffix(
this.settings.rootCountType3,
this.settings.rootCountConfig3
)
];
abbreviateDescriptions = this.settings.rootAbbreviateDescriptions;
separator = this.settings.useAdvancedFormatting ? this.settings.rootPipeSeparator : noteSeparator;
break;
case "directory" /* Directory */:
if (this.settings.showSameCountsOnFolders) {
countTypes = noteCountTypes;
abbreviateDescriptions = noteAbbreviateDescriptions;
separator = noteSeparator;
break;
}
countTypes = [
this.getCountTypeWithSuffix(
this.settings.folderCountType,
this.settings.folderCountConfig
),
this.getCountTypeWithSuffix(
this.settings.folderCountType2,
this.settings.folderCountConfig2
),
this.getCountTypeWithSuffix(
this.settings.folderCountType3,
this.settings.folderCountConfig3
)
];
abbreviateDescriptions = this.settings.folderAbbreviateDescriptions;
separator = this.settings.useAdvancedFormatting ? this.settings.folderPipeSeparator : noteSeparator;
break;
default:
countTypes = noteCountTypes;
abbreviateDescriptions = noteAbbreviateDescriptions;
separator = noteSeparator;
break;
}
return countTypes.filter((ct) => ct.$countType !== "none" /* None */).map(
(ct) => this.getDataTypeLabel(
counts,
ct,
abbreviateDescriptions
)
).filter((display) => display !== null).join(` ${separator} `);
}
getCountTypeWithSuffix($countType, countConfig) {
return {
$countType,
customSuffix: this.settings.useAdvancedFormatting ? countConfig.customSuffix : null,
frontmatterKey: countConfig.frontmatterKey,
$sessionCountType: countConfig.$sessionCountType
};
}
getBasicCountString(config) {
var _a;
const defaultSuffix = config.abbreviateDescriptions ? config.abbreviatedNoun : ` ${config.noun}${config.count == "1" ? "" : "s"}`;
const suffix = (_a = config.customSuffix) != null ? _a : defaultSuffix;
return `${config.count}${suffix}`;
}
getDataTypeLabel(counts, config, abbreviateDescriptions) {
var _a, _b;
if (!counts || typeof counts.wordCount !== "number") {
return null;
}
if (!counts.isCountable && !this.unconditionalCountTypes.includes(config.$countType)) {
return null;
}
switch (config.$countType) {
case "none" /* None */:
return null;
case "word" /* Word */:
return this.getBasicCountString({
count: NumberFormatDefault.format(Math.ceil(counts.wordCount)),
noun: "word",
abbreviatedNoun: COUNT_TYPE_DEFAULT_SHORT_SUFFIXES["word" /* Word */],
abbreviateDescriptions,
customSuffix: config.customSuffix
});
case "page" /* Page */:
return this.getBasicCountString({
count: NumberFormatDefault.format(Math.ceil(counts.pageCount)),
noun: "page",
abbreviatedNoun: COUNT_TYPE_DEFAULT_SHORT_SUFFIXES["page" /* Page */],
abbreviateDescriptions,
customSuffix: config.customSuffix
});
case "pagedecimal" /* PageDecimal */:
return this.getBasicCountString({
count: NumberFormatDecimal.format(counts.pageCount),
noun: "page",
abbreviatedNoun: COUNT_TYPE_DEFAULT_SHORT_SUFFIXES["pagedecimal" /* PageDecimal */],
abbreviateDescriptions,
customSuffix: config.customSuffix
});
case "linebreak" /* Linebreak */:
return this.getBasicCountString({
count: NumberFormatDefault.format(counts.newlineCount),
noun: "line",
abbreviatedNoun: COUNT_TYPE_DEFAULT_SHORT_SUFFIXES["linebreak" /* Linebreak */],
abbreviateDescriptions,
customSuffix: config.customSuffix
});
case "percentgoal" /* PercentGoal */: {
if (counts.wordGoal <= 0) {
return null;
}
const fraction = counts.wordCountTowardGoal / counts.wordGoal;
const percent = NumberFormatDefault.format(Math.round(fraction * 100));
const defaultSuffix = abbreviateDescriptions ? "%" : `% of ${NumberFormatDefault.format(counts.wordGoal)}`;
const suffix = (_a = config.customSuffix) != null ? _a : defaultSuffix;
return `${percent}${suffix}`;
}
case "note" /* Note */:
return this.getBasicCountString({
count: NumberFormatDefault.format(counts.noteCount),
noun: "note",
abbreviatedNoun: COUNT_TYPE_DEFAULT_SHORT_SUFFIXES["note" /* Note */],
abbreviateDescriptions,
customSuffix: config.customSuffix
});
case "character" /* Character */: {
const characterCount = this.settings.characterCountType === "ExcludeWhitespace" /* ExcludeWhitespace */ ? counts.nonWhitespaceCharacterCount : counts.characterCount;
return this.getBasicCountString({
count: NumberFormatDefault.format(characterCount),
noun: "character",
abbreviatedNoun: COUNT_TYPE_DEFAULT_SHORT_SUFFIXES["character" /* Character */],
abbreviateDescriptions,
customSuffix: config.customSuffix
});
}
case "readtime" /* ReadTime */:
return this.readTimeHelper.formatReadTime(
counts.readingTimeInMinutes,
abbreviateDescriptions
);
case "link" /* Link */:
if (counts.linkCount === 0) {
return null;
}
return this.getBasicCountString({
count: NumberFormatDefault.format(counts.linkCount),
noun: "link",
abbreviatedNoun: COUNT_TYPE_DEFAULT_SHORT_SUFFIXES["link" /* Link */],
abbreviateDescriptions,
customSuffix: config.customSuffix
});
case "embed" /* Embed */:
if (counts.embedCount === 0) {
return null;
}
return this.getBasicCountString({
count: NumberFormatDefault.format(counts.embedCount),
noun: "embed",
abbreviatedNoun: COUNT_TYPE_DEFAULT_SHORT_SUFFIXES["embed" /* Embed */],
abbreviateDescriptions,
customSuffix: config.customSuffix
});
case "alias" /* Alias */:
if (!counts.aliases || !Array.isArray(counts.aliases) || !counts.aliases.length) {
return null;
}
return abbreviateDescriptions ? `${counts.aliases[0]}` : `alias: ${counts.aliases[0]}${counts.aliases.length > 1 ? ` +${counts.aliases.length - 1}` : ""}`;
case "created" /* Created */: {
if (counts.createdDate === 0) {
return null;
}
const cDate = (0, import_obsidian3.moment)(counts.createdDate).format(this.settings.momentDateFormat || "YYYY/MM/DD");
if (config.customSuffix !== null) {
return `${cDate}${config.customSuffix}`;
}
return abbreviateDescriptions ? `${cDate}/c` : `Created ${cDate}`;
}
case "modified" /* Modified */: {
if (counts.modifiedDate === 0) {
return null;
}
const uDate = (0, import_obsidian3.moment)(counts.modifiedDate).format(this.settings.momentDateFormat || "YYYY/MM/DD");
if (config.customSuffix !== null) {
return `${uDate}${config.customSuffix}`;
}
return abbreviateDescriptions ? `${uDate}/u` : `Updated ${uDate}`;
}
case "filesize" /* FileSize */:
return this.fileSizeHelper.formatFileSize(
counts.sizeInBytes,
abbreviateDescriptions
);
case "frontmatterKey" /* FrontmatterKey */: {
if (!config.frontmatterKey) {
return null;
}
const value = (_b = counts == null ? void 0 : counts.frontmatter) == null ? void 0 : _b[config.frontmatterKey];
if (value === void 0 || value === null) {
return null;
}
if (config.customSuffix !== null) {
return `${value}${config.customSuffix}`;
}
return value;
}
case "tracksession" /* TrackSession */: {
if (!config.$sessionCountType) {
return null;
}
const quantity = this.getSessionQuantity(counts, config.$sessionCountType);
if (config.customSuffix !== null) {
return `${quantity}${config.customSuffix}`;
}
return abbreviateDescriptions ? `${quantity}/s` : `Session: ${quantity}`;
}
}
return null;
}
getSessionQuantity(counts, $countType) {
switch ($countType) {
case "word" /* Word */:
return NumberFormatDefault.format(Math.ceil(counts.wordCount - counts.sessionStart.wordCount));
case "page" /* Page */:
return NumberFormatDefault.format(Math.ceil(counts.pageCount - counts.sessionStart.pageCount));
case "pagedecimal" /* PageDecimal */:
return NumberFormatDecimal.format(counts.pageCount - counts.sessionStart.pageCount);
case "linebreak" /* Linebreak */:
return NumberFormatDefault.format(counts.newlineCount - counts.sessionStart.newlineCount);
case "note" /* Note */:
return NumberFormatDefault.format(counts.noteCount - counts.sessionStart.noteCount);
case "character" /* Character */: {
const characterCount = this.settings.characterCountType === "ExcludeWhitespace" /* ExcludeWhitespace */ ? counts.nonWhitespaceCharacterCount : counts.characterCount;
const startingCharacterCount = this.settings.characterCountType === "ExcludeWhitespace" /* ExcludeWhitespace */ ? counts.sessionStart.nonWhitespaceCharacterCount : counts.sessionStart.characterCount;
return NumberFormatDefault.format(characterCount - startingCharacterCount);
}
}
}
};
// logic/saved_data.ts
var SavedDataHelper = class {
constructor(plugin) {
this.plugin = plugin;
}
async getSavedData() {
const loaded = await this.plugin.loadData();
const denulled = Object.assign({}, loaded);
denulled.settings = Object.assign({}, DEFAULT_SETTINGS, denulled.settings);
const migrated = migrateSavedData(denulled);
return migrated;
}
};
function migrateSavedData(saved) {
const migrations = [
overwriteInvalidCountTypes,
migrateToCountConfigurationObject
];
for (const migrate of migrations) {
saved = migrate(saved);
}
return saved;
}
var overwriteInvalidCountTypes = (saved) => {
var _a;
if (!((_a = saved == null ? void 0 : saved.settings) == null ? void 0 : _a.countType)) {
return;
}
const fieldsToCheck = [
"countType",
"countType2",
"countType3",
"folderCountType",
"folderCountType2",
"folderCountType3",
"rootCountType",
"rootCountType2",
"rootCountType3"
];
for (const field of fieldsToCheck) {
if (!COUNT_TYPES.includes(saved.settings[field])) {
saved.settings[field] = field === "countType" ? "word" /* Word */ : "none" /* None */;
}
}
return saved;
};
var migrateToCountConfigurationObject = (saved) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
const settings = saved.settings;
const oldSettings = saved.settings;
(_a = settings.countConfig) != null ? _a : settings.countConfig = {};
if (typeof oldSettings.countTypeSuffix === "string") {
settings.countConfig.customSuffix = oldSettings.countTypeSuffix;
delete oldSettings.countTypeSuffix;
}
if (typeof oldSettings.frontmatterKey === "string") {
settings.countConfig.frontmatterKey = oldSettings.frontmatterKey;
delete oldSettings.frontmatterKey;
}
(_b = settings.countConfig2) != null ? _b : settings.countConfig2 = {};
if (typeof oldSettings.countType2Suffix === "string") {
settings.countConfig2.customSuffix = oldSettings.countType2Suffix;
delete oldSettings.countType2Suffix;
}
if (typeof oldSettings.frontmatterKey2 === "string") {
settings.countConfig2.frontmatterKey = oldSettings.frontmatterKey2;
delete oldSettings.frontmatterKey2;
}
(_c = settings.countConfig3) != null ? _c : settings.countConfig3 = {};
if (typeof oldSettings.countType3Suffix === "string") {
settings.countConfig3.customSuffix = oldSettings.countType3Suffix;
delete oldSettings.countType3Suffix;
}
if (typeof oldSettings.frontmatterKey3 === "string") {
settings.countConfig3.frontmatterKey = oldSettings.frontmatterKey3;
delete oldSettings.frontmatterKey3;
}
(_d = settings.folderCountConfig) != null ? _d : settings.folderCountConfig = {};
if (typeof oldSettings.folderCountTypeSuffix === "string") {
settings.folderCountConfig.customSuffix = oldSettings.folderCountTypeSuffix;
delete oldSettings.folderCountTypeSuffix;
}
(_e = settings.folderCountConfig2) != null ? _e : settings.folderCountConfig2 = {};
if (typeof oldSettings.folderCountType2Suffix === "string") {
settings.folderCountConfig2.customSuffix = oldSettings.folderCountType2Suffix;
delete oldSettings.folderCountType2Suffix;
}
(_f = settings.folderCountConfig3) != null ? _f : settings.folderCountConfig3 = {};
if (typeof oldSettings.folderCountType3Suffix === "string") {
settings.folderCountConfig3.customSuffix = oldSettings.folderCountType3Suffix;
delete oldSettings.folderCountType3Suffix;
}
(_g = settings.rootCountConfig) != null ? _g : settings.rootCountConfig = {};
if (typeof oldSettings.rootCountTypeSuffix === "string") {
settings.rootCountConfig.customSuffix = oldSettings.rootCountTypeSuffix;
delete oldSettings.rootCountTypeSuffix;
}
(_h = settings.rootCountConfig2) != null ? _h : settings.rootCountConfig2 = {};
if (typeof oldSettings.rootCountType2Suffix === "string") {
settings.rootCountConfig2.customSuffix = oldSettings.rootCountType2Suffix;
delete oldSettings.rootCountType2Suffix;
}
(_i = settings.rootCountConfig3) != null ? _i : settings.rootCountConfig3 = {};
if (typeof oldSettings.rootCountType3Suffix === "string") {
settings.rootCountConfig3.customSuffix = oldSettings.rootCountType3Suffix;
delete oldSettings.rootCountType3Suffix;
}
return saved;
};
// logic/settings.tab.ts
var import_obsidian4 = require("obsidian");
var NovelWordCountSettingTab = class extends import_obsidian4.PluginSettingTab {
constructor(app, plugin) {
super(app, plugin);
this.plugin = plugin;
}
display() {
const { containerEl } = this;
containerEl.empty();
this.renderNoteSettings(containerEl);
this.renderFolderSettings(containerEl);
this.renderRootSettings(containerEl);
this.renderAdvancedSettings(containerEl);
this.renderReanalyzeButton(containerEl);
this.renderDonationButton(containerEl);
}
//
// NOTES
//
renderNoteSettings(containerEl) {
const mainHeader = containerEl.createEl("div", {
cls: [
"setting-item",
"setting-item-heading",
"novel-word-count-settings-header"
]
});
mainHeader.createEl("div", { text: "Notes" });
mainHeader.createEl("div", {
text: "You can display up to three data types side by side.",
cls: "setting-item-description"
});
new import_obsidian4.Setting(containerEl).setDesc("Use advanced formatting").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.useAdvancedFormatting).onChange(async (value) => {
this.plugin.settings.useAdvancedFormatting = value;
await this.plugin.saveSettings();
await this.plugin.updateDisplayedCounts();
this.display();
})
);
this.renderCountTypeSetting(containerEl, {
name: "1st data type to show",
oldCountType: this.plugin.settings.countType,
setNewCountType: (value) => {
this.plugin.settings.countType = value;
this.plugin.settings.countConfig.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.countType];
}
});
this.renderSessionCountTypeSetting(containerEl, {
parentCountType: this.plugin.settings.countType,
oldCountType: this.plugin.settings.countConfig.$sessionCountType,
setNewCountType: (value) => {
this.plugin.settings.countConfig.$sessionCountType = value;
}
});
this.renderFrontmatterKeySetting(containerEl, {
countType: this.plugin.settings.countType,
oldKey: this.plugin.settings.countConfig.frontmatterKey,
setNewKey: (value) => this.plugin.settings.countConfig.frontmatterKey = value
});
this.renderCustomFormatSetting(containerEl, {
countType: this.plugin.settings.countType,
oldSuffix: this.plugin.settings.countConfig.customSuffix,
setNewSuffix: (value) => this.plugin.settings.countConfig.customSuffix = value
});
this.renderCountTypeSetting(containerEl, {
name: "2nd data type to show",
oldCountType: this.plugin.settings.countType2,
setNewCountType: (value) => {
this.plugin.settings.countType2 = value;
this.plugin.settings.countConfig2.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.countType2];
}
});
this.renderSessionCountTypeSetting(containerEl, {
parentCountType: this.plugin.settings.countType2,
oldCountType: this.plugin.settings.countConfig2.$sessionCountType,
setNewCountType: (value) => {
this.plugin.settings.countConfig2.$sessionCountType = value;
}
});
this.renderFrontmatterKeySetting(containerEl, {
countType: this.plugin.settings.countType2,
oldKey: this.plugin.settings.countConfig2.frontmatterKey,
setNewKey: (value) => this.plugin.settings.countConfig2.frontmatterKey = value
});
this.renderCustomFormatSetting(containerEl, {
countType: this.plugin.settings.countType2,
oldSuffix: this.plugin.settings.countConfig2.customSuffix,
setNewSuffix: (value) => this.plugin.settings.countConfig2.customSuffix = value
});
this.renderCountTypeSetting(containerEl, {
name: "3rd data type to show",
oldCountType: this.plugin.settings.countType3,
setNewCountType: (value) => {
this.plugin.settings.countType3 = value;
this.plugin.settings.countConfig3.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.countType3];
}
});
this.renderSessionCountTypeSetting(containerEl, {
parentCountType: this.plugin.settings.countType3,
oldCountType: this.plugin.settings.countConfig3.$sessionCountType,
setNewCountType: (value) => {
this.plugin.settings.countConfig3.$sessionCountType = value;
}
});
this.renderFrontmatterKeySetting(containerEl, {
countType: this.plugin.settings.countType3,
oldKey: this.plugin.settings.countConfig3.frontmatterKey,
setNewKey: (value) => this.plugin.settings.countConfig3.frontmatterKey = value
});
this.renderCustomFormatSetting(containerEl, {
countType: this.plugin.settings.countType3,
oldSuffix: this.plugin.settings.countConfig3.customSuffix,
setNewSuffix: (value) => this.plugin.settings.countConfig3.customSuffix = value
});
if (this.plugin.settings.useAdvancedFormatting) {
new import_obsidian4.Setting(containerEl).setName("Data type separator").addText(
(text) => text.setValue(this.plugin.settings.pipeSeparator).onChange(async (value) => {
this.plugin.settings.pipeSeparator = value;
await this.plugin.saveSettings();
await this.plugin.updateDisplayedCounts();
})
);
}
if (!this.plugin.settings.useAdvancedFormatting) {
new import_obsidian4.Setting(containerEl).setName("Abbreviate descriptions").setDesc("E.g. show '120w' instead of '120 words'").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.abbreviateDescriptions).onChange(async (value) => {
this.plugin.settings.abbreviateDescriptions = value;
await this.plugin.saveSettings();
await this.plugin.updateDisplayedCounts();
})
);
}
new import_obsidian4.Setting(containerEl).setName("Alignment").setDesc(
"Show data inline with file/folder names, right-aligned, or underneath"
).addDropdown((drop) => {
drop.addOption("inline" /* Inline */, "Inline").addOption("right" /* Right */, "Right-aligned").addOption("below" /* Below */, "Below").setValue(this.plugin.settings.alignment).onChange(async (value) => {
this.plugin.settings.alignment = value;
await this.plugin.saveSettings();
await this.plugin.updateDisplayedCounts();
});
});
}
renderFolderSettings(containerEl) {
this.renderSeparator(containerEl);
new import_obsidian4.Setting(containerEl).setHeading().setName("Folders: Same data as Notes").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.showSameCountsOnFolders).onChange(async (value) => {
this.plugin.settings.showSameCountsOnFolders = value;
await this.plugin.saveSettings();
this.display();
await this.plugin.updateDisplayedCounts();
})
);
if (!this.plugin.settings.showSameCountsOnFolders) {
this.renderCountTypeSetting(containerEl, {
name: "1st data type to show",
oldCountType: this.plugin.settings.folderCountType,
setNewCountType: (value) => {
this.plugin.settings.folderCountType = value;
this.plugin.settings.folderCountConfig.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.folderCountType];
}
});
this.renderSessionCountTypeSetting(containerEl, {
parentCountType: this.plugin.settings.folderCountType,
oldCountType: this.plugin.settings.folderCountConfig.$sessionCountType,
setNewCountType: (value) => {
this.plugin.settings.folderCountConfig.$sessionCountType = value;
}
});
this.renderCustomFormatSetting(containerEl, {
countType: this.plugin.settings.folderCountType,
oldSuffix: this.plugin.settings.folderCountConfig.customSuffix,
setNewSuffix: (value) => this.plugin.settings.folderCountConfig.customSuffix = value
});
this.renderCountTypeSetting(containerEl, {
name: "2nd data type to show",
oldCountType: this.plugin.settings.folderCountType2,
setNewCountType: (value) => {
this.plugin.settings.folderCountType2 = value;
this.plugin.settings.folderCountConfig2.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.folderCountType2];
}
});
this.renderSessionCountTypeSetting(containerEl, {
parentCountType: this.plugin.settings.folderCountType2,
oldCountType: this.plugin.settings.folderCountConfig2.$sessionCountType,
setNewCountType: (value) => {
this.plugin.settings.folderCountConfig2.$sessionCountType = value;
}
});
this.renderCustomFormatSetting(containerEl, {
countType: this.plugin.settings.folderCountType2,
oldSuffix: this.plugin.settings.folderCountConfig2.customSuffix,
setNewSuffix: (value) => this.plugin.settings.folderCountConfig2.customSuffix = value
});
this.renderCountTypeSetting(containerEl, {
name: "3rd data type to show",
oldCountType: this.plugin.settings.folderCountType3,
setNewCountType: (value) => {
this.plugin.settings.folderCountType3 = value;
this.plugin.settings.folderCountConfig3.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.folderCountType3];
}
});
this.renderSessionCountTypeSetting(containerEl, {
parentCountType: this.plugin.settings.folderCountType3,
oldCountType: this.plugin.settings.folderCountConfig3.$sessionCountType,
setNewCountType: (value) => {
this.plugin.settings.folderCountConfig3.$sessionCountType = value;
}
});
this.renderCustomFormatSetting(containerEl, {
countType: this.plugin.settings.folderCountType3,
oldSuffix: this.plugin.settings.folderCountConfig3.customSuffix,
setNewSuffix: (value) => this.plugin.settings.folderCountConfig3.customSuffix = value
});
if (this.plugin.settings.useAdvancedFormatting) {
new import_obsidian4.Setting(containerEl).setName("Data type separator").addText(
(text) => text.setValue(this.plugin.settings.folderPipeSeparator).onChange(async (value) => {
this.plugin.settings.folderPipeSeparator = value;
await this.plugin.saveSettings();
await this.plugin.updateDisplayedCounts();
})
);
}
if (!this.plugin.settings.useAdvancedFormatting) {
new import_obsidian4.Setting(containerEl).setName("Abbreviate descriptions").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.folderAbbreviateDescriptions).onChange(async (value) => {
this.plugin.settings.folderAbbreviateDescriptions = value;
await this.plugin.saveSettings();
await this.plugin.updateDisplayedCounts();
})
);
}
new import_obsidian4.Setting(containerEl).setName("Alignment").addDropdown((drop) => {
drop.addOption("inline" /* Inline */, "Inline").addOption("right" /* Right */, "Right-aligned").addOption("below" /* Below */, "Below").setValue(this.plugin.settings.folderAlignment).onChange(async (value) => {
this.plugin.settings.folderAlignment = value;
await this.plugin.saveSettings();
await this.plugin.updateDisplayedCounts();
});
});
}
}
renderRootSettings(containerEl) {
this.renderSeparator(containerEl);
new import_obsidian4.Setting(containerEl).setHeading().setName("Root: Same data as Notes").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.showSameCountsOnRoot).onChange(async (value) => {
this.plugin.settings.showSameCountsOnRoot = value;
await this.plugin.saveSettings();
this.display();
await this.plugin.updateDisplayedCounts();
})
);
if (!this.plugin.settings.showSameCountsOnRoot) {
this.renderCountTypeSetting(containerEl, {
name: "1st data type to show",
oldCountType: this.plugin.settings.rootCountType,
setNewCountType: (value) => {
this.plugin.settings.rootCountType = value;
this.plugin.settings.rootCountConfig.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.rootCountType];
}
});
this.renderSessionCountTypeSetting(containerEl, {
parentCountType: this.plugin.settings.rootCountType,
oldCountType: this.plugin.settings.rootCountConfig.$sessionCountType,
setNewCountType: (value) => {
this.plugin.settings.rootCountConfig.$sessionCountType = value;
}
});
this.renderCustomFormatSetting(containerEl, {
countType: this.plugin.settings.rootCountType,
oldSuffix: this.plugin.settings.rootCountConfig.customSuffix,
setNewSuffix: (value) => this.plugin.settings.rootCountConfig.customSuffix = value
});
this.renderCountTypeSetting(containerEl, {
name: "2nd data type to show",
oldCountType: this.plugin.settings.rootCountType2,
setNewCountType: (value) => {
this.plugin.settings.rootCountType2 = value;
this.plugin.settings.rootCountConfig2.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.rootCountType2];
}
});
this.renderSessionCountTypeSetting(containerEl, {
parentCountType: this.plugin.settings.rootCountType2,
oldCountType: this.plugin.settings.rootCountConfig2.$sessionCountType,
setNewCountType: (value) => {
this.plugin.settings.rootCountConfig2.$sessionCountType = value;
}
});
this.renderCustomFormatSetting(containerEl, {
countType: this.plugin.settings.rootCountType2,
oldSuffix: this.plugin.settings.rootCountConfig2.customSuffix,
setNewSuffix: (value) => this.plugin.settings.rootCountConfig2.customSuffix = value
});
this.renderCountTypeSetting(containerEl, {
name: "3rd data type to show",
oldCountType: this.plugin.settings.rootCountType3,
setNewCountType: (value) => {
this.plugin.settings.rootCountType3 = value;
this.plugin.settings.rootCountConfig3.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.rootCountType3];
}
});
this.renderSessionCountTypeSetting(containerEl, {
parentCountType: this.plugin.settings.rootCountType3,
oldCountType: this.plugin.settings.rootCountConfig3.$sessionCountType,
setNewCountType: (value) => {
this.plugin.settings.rootCountConfig3.$sessionCountType = value;
}
});
this.renderCustomFormatSetting(containerEl, {
countType: this.plugin.settings.rootCountType3,
oldSuffix: this.plugin.settings.rootCountConfig3.customSuffix,
setNewSuffix: (value) => this.plugin.settings.rootCountConfig3.customSuffix = value
});
if (this.plugin.settings.useAdvancedFormatting) {
new import_obsidian4.Setting(containerEl).setName("Data type separator").addText(
(text) => text.setValue(this.plugin.settings.rootPipeSeparator).onChange(async (value) => {
this.plugin.settings.rootPipeSeparator = value;
await this.plugin.saveSettings();
await this.plugin.updateDisplayedCounts();
})
);
}
if (!this.plugin.settings.useAdvancedFormatting) {
new import_obsidian4.Setting(containerEl).setName("Abbreviate descriptions").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.rootAbbreviateDescriptions).onChange(async (value) => {
this.plugin.settings.rootAbbreviateDescriptions = value;
await this.plugin.saveSettings();
await this.plugin.updateDisplayedCounts();
})
);
}
}
}
renderAdvancedSettings(containerEl) {
this.renderSeparator(containerEl);
new import_obsidian4.Setting(containerEl).setHeading().setName("Show advanced options").setDesc("Language compatibility and fine-tuning").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.showAdvanced).onChange(async (value) => {
this.plugin.settings.showAdvanced = value;
await this.plugin.saveSettings();
this.display();
})
);
if (this.plugin.settings.showAdvanced) {
const opacityChanged = async (value) => {
this.plugin.settings.labelOpacity = Math.clamp(value, 0, 1);
await this.plugin.saveSettings();
await this.plugin.updateDisplayedCounts();
};
new import_obsidian4.Setting(containerEl).setName("Label opacity").setDesc("Increase this value to make all count labels in the File Explorer more visible.").addSlider((slider) => {
slider.setLimits(0, 1, 0.05).setDynamicTooltip().setValue(this.plugin.settings.labelOpacity).onChange((0, import_obsidian4.debounce)(opacityChanged.bind(this), 500));
});
const includePathsChanged = async (txt, value) => {
this.plugin.settings.includeDirectories = value;
await this.plugin.saveSettings();
await this.plugin.initialize();
};
new import_obsidian4.Setting(containerEl).setName("Include file/folder names").setDesc(
"Only count paths matching the indicated term(s). Case-sensitive, comma-separated. Defaults to all files. Any term starting with ! will be excluded instead of included."
).addText((txt) => {
txt.setPlaceholder("").setValue(this.plugin.settings.includeDirectories).onChange((0, import_obsidian4.debounce)(includePathsChanged.bind(this, txt), 1e3));
});
new import_obsidian4.Setting(containerEl).setName("Exclude comments").setDesc(
"Exclude %%Obsidian%% and <!--HTML--> comments from counts. May affect performance on large vaults."
).addToggle(
(toggle) => toggle.setValue(this.plugin.settings.excludeComments).onChange(async (value) => {
this.plugin.settings.excludeComments = value;
await this.plugin.saveSettings();
await this.plugin.initialize();
})
);
new import_obsidian4.Setting(containerEl).setName("Exclude code blocks").setDesc(
"Exclude ```code blocks``` (e.g. DataView snippets) from all counts. May affect performance on large vaults."
).addToggle(
(toggle) => toggle.setValue(this.plugin.settings.excludeCodeBlocks).onChange(async (value) => {
this.plugin.settings.excludeCodeBlocks = value;
await this.plugin.saveSettings();
await this.plugin.initialize();
})
);
new import_obsidian4.Setting(containerEl).setName("Exclude non-visible portions of links").setDesc(
"For external links, exclude the URI from all counts. For internal links with aliases, only count the alias. May affect performance on large vaults."
).addToggle(
(toggle) => toggle.setValue(this.plugin.settings.excludeNonVisibleLinkPortions).onChange(async (value) => {
this.plugin.settings.excludeNonVisibleLinkPortions = value;
await this.plugin.saveSettings();
await this.plugin.initialize();
})
);
new import_obsidian4.Setting(containerEl).setName("Exclude footnotes").setDesc(
"Exclude footnotes[^1] from counts. May affect performance on large vaults."
).addToggle(
(toggle) => toggle.setValue(this.plugin.settings.excludeFootnotes).onChange(async (value) => {
this.plugin.settings.excludeFootnotes = value;
await this.plugin.saveSettings();
await this.plugin.initialize();
})
);
new import_obsidian4.Setting(containerEl).setName("Character count method").setDesc("For language compatibility").addDropdown((drop) => {
drop.addOption("AllCharacters" /* StringLength */, "All characters").addOption(
"ExcludeWhitespace" /* ExcludeWhitespace */,
"Exclude whitespace"
).setValue(this.plugin.settings.characterCountType).onChange(async (value) => {
this.plugin.settings.characterCountType = value;
await this.plugin.saveSettings();
await this.plugin.initialize();
});
});
new import_obsidian4.Setting(containerEl).setName("Page count method").setDesc("For language compatibility").addDropdown((drop) => {
drop.addOption("ByWords" /* ByWords */, "Words per page").addOption("ByChars" /* ByChars */, "Characters per page").setValue(this.plugin.settings.pageCountType).onChange(async (value) => {
this.plugin.settings.pageCountType = value;
await this.plugin.saveSettings();
this.display();
await this.plugin.updateDisplayedCounts();
});
});
const wordsPerMinuteChanged = async (txt, value) => {
const asNumber = Number(value);
const isValid = !isNaN(asNumber) && asNumber > 0;
txt.inputEl.style.borderColor = isValid ? null : "red";
this.plugin.settings.wordsPerMinute = isValid ? Number(value) : 265;
await this.plugin.saveSettings();
await this.plugin.initialize();
};
new import_obsidian4.Setting(containerEl).setName("Words per minute").setDesc(
"Used to calculate Reading Time. 265 is the average speed of an English-speaking adult."
).addText((txt) => {
txt.setPlaceholder("265").setValue(this.plugin.settings.wordsPerMinute.toString()).onChange((0, import_obsidian4.debounce)(wordsPerMinuteChanged.bind(this, txt), 1e3));
});
const charsPerMinuteChanged = async (txt, value) => {
const asNumber = Number(value);
const isValid = !isNaN(asNumber) && asNumber > 0;
txt.inputEl.style.borderColor = isValid ? null : "red";
this.plugin.settings.charsPerMinute = isValid ? Number(value) : 500;
await this.plugin.saveSettings();
await this.plugin.initialize();
};
new import_obsidian4.Setting(containerEl).setName("CJK characters per minute").setDesc(
"Used to calculate Reading Time. 500 is the average speed for CJK texts."
).addText((txt) => {
txt.setPlaceholder("500").setValue(this.plugin.settings.charsPerMinute.toString()).onChange((0, import_obsidian4.debounce)(charsPerMinuteChanged.bind(this, txt), 1e3));
});
if (this.plugin.settings.pageCountType === "ByWords" /* ByWords */) {
const wordsPerPageChanged = async (txt, value) => {
const asNumber = Number(value);
const isValid = !isNaN(asNumber) && asNumber > 0;
txt.inputEl.style.borderColor = isValid ? null : "red";
this.plugin.settings.wordsPerPage = isValid ? Number(value) : 300;
await this.plugin.saveSettings();
await this.plugin.initialize();
};
new import_obsidian4.Setting(containerEl).setName("Words per page").setDesc(
"Used for page count. 300 is standard in English language publishing."
).addText((txt) => {
txt.setPlaceholder("300").setValue(this.plugin.settings.wordsPerPage.toString()).onChange((0, import_obsidian4.debounce)(wordsPerPageChanged.bind(this, txt), 1e3));
});
}
if (this.plugin.settings.pageCountType === "ByChars" /* ByChars */) {
new import_obsidian4.Setting(containerEl).setName("Include whitespace characters in page count").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.charsPerPageIncludesWhitespace).onChange(async (value) => {
this.plugin.settings.charsPerPageIncludesWhitespace = value;
await this.plugin.saveSettings();
this.display();
await this.plugin.initialize();
})
);
const charsPerPageChanged = async (txt, value) => {
const asNumber = Number(value);
const isValid = !isNaN(asNumber) && asNumber > 0;
txt.inputEl.style.borderColor = isValid ? null : "red";
const defaultCharsPerPage = 1500;
this.plugin.settings.charsPerPage = isValid ? Number(value) : defaultCharsPerPage;
await this.plugin.saveSettings();
await this.plugin.initialize();
};
new import_obsidian4.Setting(containerEl).setName("Characters per page").setDesc(
`Used for page count. ${this.plugin.settings.charsPerPageIncludesWhitespace ? "2400 is common in Danish." : "1500 is common in German (Normseite)."}`
).addText((txt) => {
txt.setPlaceholder("1500").setValue(this.plugin.settings.charsPerPage.toString()).onChange((0, import_obsidian4.debounce)(charsPerPageChanged.bind(this, txt), 1e3));
});
}
const dateFormatChanged = async (txt, value) => {
const isValid = typeof value === "string" && !!value.trim();
this.plugin.settings.momentDateFormat = isValid ? value : "";
await this.plugin.saveSettings();
await this.plugin.initialize();
};
new import_obsidian4.Setting(containerEl).setName("Date format").setDesc("MomentJS date format to use for date strings").addText((txt) => {
txt.setPlaceholder("YYYY/MM/DD").setValue(this.plugin.settings.momentDateFormat).onChange((0, import_obsidian4.debounce)(dateFormatChanged.bind(this, txt), 1e3));
});
new import_obsidian4.Setting(containerEl).setName("Debug mode").setDesc("Log debugging information to the developer console").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.debugMode).onChange(async (value) => {
this.plugin.settings.debugMode = value;
this.plugin.debugHelper.setDebugMode(value);
this.plugin.fileHelper.setDebugMode(value);
await this.plugin.saveSettings();
})
);
}
}
renderReanalyzeButton(containerEl) {
this.renderSeparator(containerEl);
new import_obsidian4.Setting(containerEl).setHeading().setName("Recount all documents").setDesc(
"If changes have occurred outside of Obsidian, you may need to trigger a manual recount"
).addButton(
(button) => button.setButtonText("Recount").setCta().onClick(async () => {
button.disabled = true;
await this.plugin.initialize();
button.setButtonText("Done");
button.removeCta();
setTimeout(() => {
button.setButtonText("Recount");
button.setCta();
button.disabled = false;
}, 1e3);
})
);
}
renderDonationButton(containerEl) {
this.renderSeparator(containerEl);
const label = containerEl.createEl("div", {
cls: [
"setting-item",
"setting-item-heading",
"novel-word-count-settings-header",
"novel-word-count-donation-line"
]
});
label.createEl("div", {
text: "Enjoying this plugin? Want more features?"
});
const button = label.createEl("div");
button.innerHTML = `<a href='https://ko-fi.com/J3J6OWA5C' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://storage.ko-fi.com/cdn/kofi2.png?v=3' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>`;
}
renderCountTypeSetting(containerEl, config) {
new import_obsidian4.Setting(containerEl).setName(config.name).setDesc(getDescription(config.oldCountType)).addDropdown((drop) => {
for (const countType of COUNT_TYPES) {
drop.addOption(countType, COUNT_TYPE_DISPLAY_STRINGS[countType]);
}
drop.setValue(config.oldCountType).onChange(async (value) => {
config.setNewCountType(value);
await this.plugin.saveSettings();
this.display();
await this.plugin.updateDisplayedCounts();
});
});
}
renderSessionCountTypeSetting(containerEl, config) {
if (config.parentCountType !== "tracksession" /* TrackSession */) {
return;
}
new import_obsidian4.Setting(containerEl).setDesc("[Track Session] Session count type").addDropdown((drop) => {
for (const countType of SESSION_COUNT_TYPES) {
drop.addOption(countType, COUNT_TYPE_DISPLAY_STRINGS[countType]);
}
drop.setValue(config.oldCountType).onChange(async (value) => {
config.setNewCountType(value);
await this.plugin.saveSettings();
this.display();
await this.plugin.updateDisplayedCounts();
});
});
}
renderCustomFormatSetting(containerEl, config) {
if (!this.plugin.settings.useAdvancedFormatting || config.countType === "none" /* None */) {
return;
}
if (UNFORMATTABLE_COUNT_TYPES.includes(config.countType)) {
new import_obsidian4.Setting(containerEl).setDesc(
`[${COUNT_TYPE_DISPLAY_STRINGS[config.countType]}] can't be formatted.`
);
} else {
new import_obsidian4.Setting(containerEl).setDesc(
`[${COUNT_TYPE_DISPLAY_STRINGS[config.countType]}] Custom suffix`
).addText(
(text) => text.setValue(config.oldSuffix).onChange(async (value) => {
config.setNewSuffix(value);
await this.plugin.saveSettings();
await this.plugin.updateDisplayedCounts();
})
);
}
}
renderFrontmatterKeySetting(containerEl, config) {
if (config.countType !== "frontmatterKey" /* FrontmatterKey */) {
return;
}
new import_obsidian4.Setting(containerEl).setDesc(
`[${COUNT_TYPE_DISPLAY_STRINGS["frontmatterKey" /* FrontmatterKey */]}] Key name`
).addText(
(text) => text.setValue(config.oldKey).onChange(async (value) => {
config.setNewKey(value);
await this.plugin.saveSettings();
await this.plugin.updateDisplayedCounts();
})
);
}
renderSeparator(containerEl) {
containerEl.createEl("hr", {
cls: "novel-word-count-hr"
});
}
};
// main.ts
var import_obsidian5 = require("obsidian");
var NovelWordCountPlugin = class extends import_obsidian5.Plugin {
constructor(app, manifest) {
super(app, manifest);
this.debugHelper = new DebugHelper();
this.FIVE_MINUTES = 5 * 60 * 1e3;
this.saveSettingsDebounced = (0, import_obsidian5.debounce)(this.saveSettings, this.FIVE_MINUTES, false);
this.fileHelper = new FileHelper(this.app, this);
this.eventHelper = new EventHelper(
this,
app,
this.debugHelper,
this.fileHelper
);
this.nodeLabelHelper = new NodeLabelHelper(this);
this.savedDataHelper = new SavedDataHelper(this);
}
get settings() {
return this.savedData.settings;
}
// LIFECYCLE
async onload() {
this.savedData = await this.savedDataHelper.getSavedData();
this.fileHelper.setDebugMode(this.savedData.settings.debugMode);
this.debugHelper.setDebugMode(this.savedData.settings.debugMode);
this.debugHelper.debug(`Detected locales: [${navigator.languages}]`);
this.debugHelper.debug("onload lifecycle hook");
this.addSettingTab(new NovelWordCountSettingTab(this.app, this));
this.addCommand({
id: "recount-vault",
name: "Recount all notes / Reset session",
callback: async () => {
this.debugHelper.debug("[Recount] command triggered");
await this.initialize();
}
});
this.addCommand({
id: "cycle-count-type",
name: "Show next data type (1st position)",
callback: async () => {
this.debugHelper.debug("[Cycle next data type] command triggered");
this.settings.countType = COUNT_TYPES[(COUNT_TYPES.indexOf(this.settings.countType) + 1) % COUNT_TYPES.length];
await this.saveSettings();
this.updateDisplayedCounts();
}
});
this.addCommand({
id: "toggle-abbreviate",
name: "Toggle abbreviation on Notes",
callback: async () => {
this.debugHelper.debug(
"[Toggle abbrevation - Notes] command triggered"
);
this.settings.abbreviateDescriptions = !this.settings.abbreviateDescriptions;
await this.saveSettings();
this.updateDisplayedCounts();
}
});
for (const countType of COUNT_TYPES) {
this.addCommand({
id: `set-count-type-${countType}`,
name: `Show ${COUNT_TYPE_DISPLAY_STRINGS[countType]} (1st position)`,
callback: async () => {
this.debugHelper.debug(
`[Set count type to ${countType}] command triggered`
);
this.settings.countType = countType;
await this.saveSettings();
this.updateDisplayedCounts();
}
});
}
this.eventHelper.handleEvents();
this.initialize();
}
async onunload() {
await this.saveSettings();
}
// SETTINGS
async saveSettings() {
this.debugHelper.debug("Saving to data.json.");
await this.saveData(this.savedData);
}
// PUBLIC
/**
Called with (true) when the plugin initializes or the user clicks Reanalyze.
Called with (false) every second while waiting for the file explorer to load.
*/
async initialize(reinitializeAllCounts = true) {
this.debugHelper.debug("initialize");
this.app.workspace.onLayoutReady(async () => {
if (reinitializeAllCounts) {
await this.eventHelper.reinitializeAllCounts();
}
try {
await this.getFileExplorerLeaf();
await this.updateDisplayedCounts();
} catch (err) {
this.debugHelper.debug("Error while updating displayed counts");
this.debugHelper.error(err);
setTimeout(() => {
this.initialize(false);
}, 1e3);
}
});
}
async updateDisplayedCounts(file = null) {
var _a, _b;
const debugEnd = this.debugHelper.debugStart(
`updateDisplayedCounts [${file == null ? "ALL" : file.path}]`
);
if (!Object.keys(this.savedData.cachedCounts).length) {
this.debugHelper.debug("No cached data found; skipping update.");
return;
}
let fileExplorerLeaf;
try {
fileExplorerLeaf = await this.getFileExplorerLeaf();
} catch (err) {
this.debugHelper.debug("File explorer leaf not found; skipping update.");
return;
}
this.setContainerClass(fileExplorerLeaf);
const fileExplorerView = fileExplorerLeaf.view;
const fileItems = fileExplorerView.fileItems;
if ((_a = fileExplorerView == null ? void 0 : fileExplorerView.headerDom) == null ? void 0 : _a.navButtonsEl) {
const counts = this.fileHelper.getCachedDataForPath(
this.savedData.cachedCounts,
"/"
);
fileExplorerView.headerDom.navButtonsEl.setAttribute(
"data-novel-word-count-plugin",
this.nodeLabelHelper.getNodeLabel(counts)
);
document.documentElement.style.setProperty("--novel-word-count-opacity", `${this.settings.labelOpacity}`);
}
if (file) {
const relevantItems = Object.keys(fileItems).filter(
(path) => file.path.includes(path)
);
this.debugHelper.debug(
"Setting display counts for",
relevantItems.length,
"fileItems matching path",
file.path
);
} else {
this.debugHelper.debug(
`Setting display counts for ${Object.keys(fileItems).length} fileItems`
);
}
for (const path in fileItems) {
if (file && (!file.path.includes(path) || file.path === "/")) {
continue;
}
const counts = this.fileHelper.getCachedDataForPath(
this.savedData.cachedCounts,
path
);
const item = fileItems[path];
((_b = item.titleEl) != null ? _b : item.selfEl).setAttribute(
"data-novel-word-count-plugin",
this.nodeLabelHelper.getNodeLabel(counts)
);
}
debugEnd();
}
// FUNCTIONALITY
async getFileExplorerLeaf() {
return new Promise((resolve, reject) => {
let foundLeaf = null;
this.app.workspace.iterateAllLeaves((leaf) => {
if (foundLeaf) {
return;
}
const view = leaf.view;
if (!view || !view.fileItems) {
return;
}
foundLeaf = leaf;
resolve(foundLeaf);
});
if (!foundLeaf) {
reject(Error("Could not find file explorer leaf."));
}
});
}
setContainerClass(leaf) {
const container = leaf.view.containerEl;
container.toggleClass(`novel-word-count--active`, true);
const notePrefix = `novel-word-count--note-`;
const folderPrefix = `novel-word-count--folder-`;
const alignmentClasses = ALIGNMENT_TYPES.map((at) => notePrefix + at).concat(ALIGNMENT_TYPES.map((at) => folderPrefix + at));
for (const ac of alignmentClasses) {
container.toggleClass(ac, false);
}
container.toggleClass(notePrefix + this.settings.alignment, true);
const folderAlignment = this.settings.showSameCountsOnFolders ? this.settings.alignment : this.settings.folderAlignment;
container.toggleClass(folderPrefix + folderAlignment, true);
}
};
/* nosourcemap */