main.js
  1  /*
  2  THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
  3  if you want to view the source, please visit the github repository of this plugin
  4  */
  5  
  6  var __create = Object.create;
  7  var __defProp = Object.defineProperty;
  8  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  9  var __getOwnPropNames = Object.getOwnPropertyNames;
 10  var __getProtoOf = Object.getPrototypeOf;
 11  var __hasOwnProp = Object.prototype.hasOwnProperty;
 12  var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
 13  var __commonJS = (cb, mod) => function __require() {
 14    return mod || (0, cb[Object.keys(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
 15  };
 16  var __export = (target, all) => {
 17    __markAsModule(target);
 18    for (var name in all)
 19      __defProp(target, name, { get: all[name], enumerable: true });
 20  };
 21  var __reExport = (target, module2, desc) => {
 22    if (module2 && typeof module2 === "object" || typeof module2 === "function") {
 23      for (let key of __getOwnPropNames(module2))
 24        if (!__hasOwnProp.call(target, key) && key !== "default")
 25          __defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable });
 26    }
 27    return target;
 28  };
 29  var __toModule = (module2) => {
 30    return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2);
 31  };
 32  var __async = (__this, __arguments, generator) => {
 33    return new Promise((resolve, reject) => {
 34      var fulfilled = (value) => {
 35        try {
 36          step(generator.next(value));
 37        } catch (e) {
 38          reject(e);
 39        }
 40      };
 41      var rejected = (value) => {
 42        try {
 43          step(generator.throw(value));
 44        } catch (e) {
 45          reject(e);
 46        }
 47      };
 48      var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
 49      step((generator = generator.apply(__this, __arguments)).next());
 50    });
 51  };
 52  
 53  // node_modules/parse-ms/index.js
 54  var require_parse_ms = __commonJS({
 55    "node_modules/parse-ms/index.js"(exports, module2) {
 56      "use strict";
 57      module2.exports = (milliseconds) => {
 58        if (typeof milliseconds !== "number") {
 59          throw new TypeError("Expected a number");
 60        }
 61        const roundTowardsZero = milliseconds > 0 ? Math.floor : Math.ceil;
 62        return {
 63          days: roundTowardsZero(milliseconds / 864e5),
 64          hours: roundTowardsZero(milliseconds / 36e5) % 24,
 65          minutes: roundTowardsZero(milliseconds / 6e4) % 60,
 66          seconds: roundTowardsZero(milliseconds / 1e3) % 60,
 67          milliseconds: roundTowardsZero(milliseconds) % 1e3,
 68          microseconds: roundTowardsZero(milliseconds * 1e3) % 1e3,
 69          nanoseconds: roundTowardsZero(milliseconds * 1e6) % 1e3
 70        };
 71      };
 72    }
 73  });
 74  
 75  // node_modules/pretty-ms/index.js
 76  var require_pretty_ms = __commonJS({
 77    "node_modules/pretty-ms/index.js"(exports, module2) {
 78      "use strict";
 79      var parseMilliseconds = require_parse_ms();
 80      var pluralize = (word, count) => count === 1 ? word : `${word}s`;
 81      var SECOND_ROUNDING_EPSILON = 1e-7;
 82      module2.exports = (milliseconds, options = {}) => {
 83        if (!Number.isFinite(milliseconds)) {
 84          throw new TypeError("Expected a finite number");
 85        }
 86        if (options.colonNotation) {
 87          options.compact = false;
 88          options.formatSubMilliseconds = false;
 89          options.separateMilliseconds = false;
 90          options.verbose = false;
 91        }
 92        if (options.compact) {
 93          options.secondsDecimalDigits = 0;
 94          options.millisecondsDecimalDigits = 0;
 95        }
 96        const result = [];
 97        const floorDecimals = (value, decimalDigits) => {
 98          const flooredInterimValue = Math.floor(value * 10 ** decimalDigits + SECOND_ROUNDING_EPSILON);
 99          const flooredValue = Math.round(flooredInterimValue) / 10 ** decimalDigits;
100          return flooredValue.toFixed(decimalDigits);
101        };
102        const add = (value, long, short, valueString) => {
103          if ((result.length === 0 || !options.colonNotation) && value === 0 && !(options.colonNotation && short === "m")) {
104            return;
105          }
106          valueString = (valueString || value || "0").toString();
107          let prefix;
108          let suffix;
109          if (options.colonNotation) {
110            prefix = result.length > 0 ? ":" : "";
111            suffix = "";
112            const wholeDigits = valueString.includes(".") ? valueString.split(".")[0].length : valueString.length;
113            const minLength = result.length > 0 ? 2 : 1;
114            valueString = "0".repeat(Math.max(0, minLength - wholeDigits)) + valueString;
115          } else {
116            prefix = "";
117            suffix = options.verbose ? " " + pluralize(long, value) : short;
118          }
119          result.push(prefix + valueString + suffix);
120        };
121        const parsed = parseMilliseconds(milliseconds);
122        add(Math.trunc(parsed.days / 365), "year", "y");
123        add(parsed.days % 365, "day", "d");
124        add(parsed.hours, "hour", "h");
125        add(parsed.minutes, "minute", "m");
126        if (options.separateMilliseconds || options.formatSubMilliseconds || !options.colonNotation && milliseconds < 1e3) {
127          add(parsed.seconds, "second", "s");
128          if (options.formatSubMilliseconds) {
129            add(parsed.milliseconds, "millisecond", "ms");
130            add(parsed.microseconds, "microsecond", "\xB5s");
131            add(parsed.nanoseconds, "nanosecond", "ns");
132          } else {
133            const millisecondsAndBelow = parsed.milliseconds + parsed.microseconds / 1e3 + parsed.nanoseconds / 1e6;
134            const millisecondsDecimalDigits = typeof options.millisecondsDecimalDigits === "number" ? options.millisecondsDecimalDigits : 0;
135            const roundedMiliseconds = millisecondsAndBelow >= 1 ? Math.round(millisecondsAndBelow) : Math.ceil(millisecondsAndBelow);
136            const millisecondsString = millisecondsDecimalDigits ? millisecondsAndBelow.toFixed(millisecondsDecimalDigits) : roundedMiliseconds;
137            add(Number.parseFloat(millisecondsString, 10), "millisecond", "ms", millisecondsString);
138          }
139        } else {
140          const seconds = milliseconds / 1e3 % 60;
141          const secondsDecimalDigits = typeof options.secondsDecimalDigits === "number" ? options.secondsDecimalDigits : 1;
142          const secondsFixed = floorDecimals(seconds, secondsDecimalDigits);
143          const secondsString = options.keepDecimalsOnWholeSeconds ? secondsFixed : secondsFixed.replace(/\.0+$/, "");
144          add(Number.parseFloat(secondsString, 10), "second", "s", secondsString);
145        }
146        if (result.length === 0) {
147          return "0" + (options.verbose ? " milliseconds" : "ms");
148        }
149        if (options.compact) {
150          return result[0];
151        }
152        if (typeof options.unitCount === "number") {
153          const separator = options.colonNotation ? "" : " ";
154          return result.slice(0, Math.max(options.unitCount, 1)).join(separator);
155        }
156        return options.colonNotation ? result.join("") : result.join(" ");
157      };
158    }
159  });
160  
161  // node_modules/reading-time/lib/reading-time.js
162  var require_reading_time = __commonJS({
163    "node_modules/reading-time/lib/reading-time.js"(exports, module2) {
164      "use strict";
165      function codeIsInRanges(number, arrayOfRanges) {
166        return arrayOfRanges.some(([lowerBound, upperBound]) => lowerBound <= number && number <= upperBound);
167      }
168      function isCJK(c) {
169        if (typeof c !== "string") {
170          return false;
171        }
172        const charCode = c.charCodeAt(0);
173        return codeIsInRanges(charCode, [
174          [12352, 12447],
175          [19968, 40959],
176          [44032, 55203],
177          [131072, 191456]
178        ]);
179      }
180      function isAnsiWordBound(c) {
181        return " \n\r	".includes(c);
182      }
183      function isPunctuation(c) {
184        if (typeof c !== "string") {
185          return false;
186        }
187        const charCode = c.charCodeAt(0);
188        return codeIsInRanges(charCode, [
189          [33, 47],
190          [58, 64],
191          [91, 96],
192          [123, 126],
193          [12288, 12351],
194          [65280, 65519]
195        ]);
196      }
197      function readingTime2(text, options = {}) {
198        let words = 0, start = 0, end = text.length - 1;
199        const wordsPerMinute = options.wordsPerMinute || 200;
200        const isWordBound = options.wordBound || isAnsiWordBound;
201        while (isWordBound(text[start]))
202          start++;
203        while (isWordBound(text[end]))
204          end--;
205        const normalizedText = `${text}
206  `;
207        for (let i = start; i <= end; i++) {
208          if (isCJK(normalizedText[i]) || !isWordBound(normalizedText[i]) && (isWordBound(normalizedText[i + 1]) || isCJK(normalizedText[i + 1]))) {
209            words++;
210          }
211          if (isCJK(normalizedText[i])) {
212            while (i <= end && (isPunctuation(normalizedText[i + 1]) || isWordBound(normalizedText[i + 1]))) {
213              i++;
214            }
215          }
216        }
217        const minutes = words / wordsPerMinute;
218        const time = Math.round(minutes * 60 * 1e3);
219        const displayed = Math.ceil(minutes.toFixed(2));
220        return {
221          text: displayed + " min read",
222          minutes,
223          time,
224          words
225        };
226      }
227      module2.exports = readingTime2;
228    }
229  });
230  
231  // src/main.ts
232  __export(exports, {
233    default: () => ReadingTime
234  });
235  var import_obsidian2 = __toModule(require("obsidian"));
236  
237  // src/settings.ts
238  var import_obsidian = __toModule(require("obsidian"));
239  var RT_DEFAULT_SETTINGS = {
240    readingSpeed: 200,
241    format: "default",
242    appendText: "read"
243  };
244  var ReadingTimeSettingsTab = class extends import_obsidian.PluginSettingTab {
245    constructor(app, plugin) {
246      super(app, plugin);
247      this.plugin = plugin;
248    }
249    display() {
250      const { containerEl } = this;
251      containerEl.empty();
252      new import_obsidian.Setting(containerEl).setName("Reading speed").setDesc("Words per minute used for reading speed (default: 200).").addText((text) => {
253        text.setPlaceholder("Example: 200").setValue(this.plugin.settings.readingSpeed.toString()).onChange((value) => __async(this, null, function* () {
254          this.plugin.settings.readingSpeed = parseInt(value.trim());
255          yield this.plugin.saveSettings().then(this.plugin.calculateReadingTime);
256        }));
257      });
258      new import_obsidian.Setting(this.containerEl).setName("Format").setDesc("Choose the output format").addDropdown((dropdown) => dropdown.addOption("default", "Default (10 min)").addOption("compact", "Compact (10m)").addOption("simple", "Simple (10m 4s)").addOption("verbose", "Verbose (10 minutes 4 seconds)").addOption("digital", "Colon Notation (10:04)").setValue(this.plugin.settings.format).onChange((value) => __async(this, null, function* () {
259        this.plugin.settings.format = value;
260        yield this.plugin.saveSettings().then(this.plugin.calculateReadingTime);
261      })));
262      new import_obsidian.Setting(this.containerEl).setName("Append Text").setDesc("Append 'read' to formatted string.").addText((text) => text.setValue(this.plugin.settings.appendText).onChange((value) => __async(this, null, function* () {
263        this.plugin.settings.appendText = value.trim();
264        yield this.plugin.saveSettings().then(this.plugin.calculateReadingTime);
265      })));
266    }
267  };
268  
269  // src/helpers.ts
270  var import_pretty_ms = __toModule(require_pretty_ms());
271  var ReadTime = require_reading_time();
272  function readingTimeText(text, plugin) {
273    const result = ReadTime(text, {
274      wordsPerMinute: plugin.settings.readingSpeed
275    });
276    let options = {
277      secondsDecimalDigits: 0
278    };
279    switch (plugin.settings.format) {
280      case "simple":
281        break;
282      case "compact":
283        if (result.time > 36e5) {
284          options.unitCount = 2;
285        } else {
286          options.compact = true;
287        }
288        break;
289      case "verbose":
290        options.verbose = true;
291        break;
292      case "digital":
293        options.colonNotation = true;
294        break;
295      case "default":
296        return plugin.settings.appendText ? result.text : result.text.replace(" read", "");
297    }
298    let output = (0, import_pretty_ms.default)(result.time, options);
299    return plugin.settings.appendText ? `${output} ${plugin.settings.appendText}` : output;
300  }
301  
302  // src/main.ts
303  var ReadingTime = class extends import_obsidian2.Plugin {
304    constructor() {
305      super(...arguments);
306      this.calculateReadingTime = () => {
307        const mdView = this.app.workspace.getActiveViewOfType(import_obsidian2.MarkdownView);
308        if (mdView && mdView.getViewData()) {
309          const result = readingTimeText(mdView.getViewData(), this);
310          this.statusBar.setText(`${result}`);
311        } else {
312          this.statusBar.setText("0 min read");
313        }
314      };
315    }
316    onload() {
317      return __async(this, null, function* () {
318        yield this.loadSettings();
319        this.statusBar = this.addStatusBarItem();
320        this.statusBar.setText("");
321        this.addSettingTab(new ReadingTimeSettingsTab(this.app, this));
322        this.addCommand({
323          id: "reading-time-editor-command",
324          name: "Selected Text",
325          editorCallback: (editor, view) => {
326            new ReadingTimeModal(this.app, editor, this).open();
327          }
328        });
329        this.registerEvent(this.app.workspace.on("file-open", this.calculateReadingTime));
330        this.registerEvent(this.app.workspace.on("editor-change", (0, import_obsidian2.debounce)(this.calculateReadingTime, 1e3)));
331      });
332    }
333    loadSettings() {
334      return __async(this, null, function* () {
335        this.settings = Object.assign({}, RT_DEFAULT_SETTINGS, yield this.loadData());
336      });
337    }
338    saveSettings() {
339      return __async(this, null, function* () {
340        yield this.saveData(this.settings);
341      });
342    }
343  };
344  var ReadingTimeModal = class extends import_obsidian2.Modal {
345    constructor(app, editor, plugin) {
346      super(app);
347      this.editor = editor;
348      this.plugin = plugin;
349    }
350    onOpen() {
351      const { contentEl, titleEl } = this;
352      titleEl.setText("Reading Time of Selected Text");
353      const stats = readingTime(this.editor.getSelection(), this.plugin);
354      contentEl.setText(`${stats} (at ${this.plugin.settings.readingSpeed} wpm)`);
355    }
356    onClose() {
357      const { contentEl } = this;
358      contentEl.empty();
359    }
360  };
361  /*!
362   * reading-time
363   * Copyright (c) Nicolas Gryman <ngryman@gmail.com>
364   * MIT Licensed
365   */