diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ca00d96 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +end_of_line = lf +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..52e105b --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# vscode +.vscode + +# Intellij +*.iml +.idea + +# npm +node_modules + +# Don't include the compiled main.js file in the repo. +# They should be uploaded to GitHub releases instead. +main.js + +# Exclude sourcemaps +*.map + +# obsidian +data.json + +# Exclude macOS Finder (System Explorer) View States +.DS_Store + +src/cjk-characters.js \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b973752 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +tag-version-prefix="" \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..9f26559 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,26 @@ +# vscode +.vscode + +# Intellij +*.iml +.idea + +# npm +node_modules + +# Don't include the compiled main.js file in the repo. +# They should be uploaded to GitHub releases instead. +main.js + +# Exclude sourcemaps +*.map + +# obsidian +data.json + +# Exclude macOS Finder (System Explorer) View States +.DS_Store + +pnpm-lock.yaml + +src/cjk-characters.js \ No newline at end of file diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..765ba15 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,8 @@ +export default { + printWidth: 100, + semi: true, + singleQuote: false, + jsxSingleQuote: false, + trailingComma: "es5", + tabWidth: 2, +}; diff --git a/esbuild.config.js b/esbuild.config.js new file mode 100644 index 0000000..b42e3aa --- /dev/null +++ b/esbuild.config.js @@ -0,0 +1,33 @@ +import esbuild from "esbuild"; +import process from "process"; + +const banner = `/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ +`; + +const prod = process.argv[2] === "production"; + +const context = await esbuild.context({ + banner: { + js: banner, + }, + entryPoints: ["src/index.js"], + bundle: true, + external: ["obsidian"], + format: "cjs", + target: "es2018", + logLevel: "info", + sourcemap: prod ? false : "inline", + treeShaking: true, + outfile: "main.js", + minify: prod, +}); + +if (prod) { + await context.rebuild(); + process.exit(0); +} else { + await context.watch(); +} diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..7765672 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "module": "ESNext", + "target": "ES6", + "moduleResolution": "node", + "lib": ["DOM", "ES5", "ES6", "ES7"] + }, + "include": ["**/*"] +} diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..59ea7ab --- /dev/null +++ b/manifest.json @@ -0,0 +1,10 @@ +{ + "id": "cjk-count", + "name": "CJK Count", + "version": "1.0.0", + "minAppVersion": "1.8.10", + "description": "A word count plugin that only counts Chinese, Japanese and Korean (CJK) characters.", + "author": "vrabe", + "authorUrl": "https://vrabe.tw", + "isDesktopOnly": true +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..219fff6 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "obsidian-cjk-count", + "version": "1.0.0", + "description": "A word count Obsidian plugin that only counts Chinese, Japanese and Korean (CJK) characters.", + "main": "main.js", + "type": "module", + "scripts": { + "dev": "node esbuild.config.js", + "build": "node esbuild.config.js production", + "version": "node version-bump.js && git add manifest.json versions.json", + "generate": "node src/scripts/generate-cjk-characters.js", + "format": "prettier -w --cache ." + }, + "keywords": [], + "author": "", + "license": "MIT", + "devDependencies": { + "cjk-regex": "^3.3.0", + "esbuild": "0.17.3", + "obsidian": "^1.8.7", + "prettier": "^3.5.3" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..1288477 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,352 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + cjk-regex: + specifier: ^3.3.0 + version: 3.3.0 + esbuild: + specifier: 0.17.3 + version: 0.17.3 + obsidian: + specifier: ^1.8.7 + version: 1.8.7(@codemirror/state@6.5.2)(@codemirror/view@6.36.7) + prettier: + specifier: ^3.5.3 + version: 3.5.3 + +packages: + + '@codemirror/state@6.5.2': + resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==} + + '@codemirror/view@6.36.7': + resolution: {integrity: sha512-kCWGW/chWGPgZqfZ36Um9Iz0X2IVpmCjg1P/qY6B6a2ecXtWRRAigmpJ6YgUQ5lTWXMyyVdfmpzhLZmsZQMbtg==} + + '@esbuild/android-arm64@0.17.3': + resolution: {integrity: sha512-XvJsYo3dO3Pi4kpalkyMvfQsjxPWHYjoX4MDiB/FUM4YMfWcXa5l4VCwFWVYI1+92yxqjuqrhNg0CZg3gSouyQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.17.3': + resolution: {integrity: sha512-1Mlz934GvbgdDmt26rTLmf03cAgLg5HyOgJN+ZGCeP3Q9ynYTNMn2/LQxIl7Uy+o4K6Rfi2OuLsr12JQQR8gNg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.17.3': + resolution: {integrity: sha512-nuV2CmLS07Gqh5/GrZLuqkU9Bm6H6vcCspM+zjp9TdQlxJtIe+qqEXQChmfc7nWdyr/yz3h45Utk1tUn8Cz5+A==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.17.3': + resolution: {integrity: sha512-01Hxaaat6m0Xp9AXGM8mjFtqqwDjzlMP0eQq9zll9U85ttVALGCGDuEvra5Feu/NbP5AEP1MaopPwzsTcUq1cw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.17.3': + resolution: {integrity: sha512-Eo2gq0Q/er2muf8Z83X21UFoB7EU6/m3GNKvrhACJkjVThd0uA+8RfKpfNhuMCl1bKRfBzKOk6xaYKQZ4lZqvA==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.17.3': + resolution: {integrity: sha512-CN62ESxaquP61n1ZjQP/jZte8CE09M6kNn3baos2SeUfdVBkWN5n6vGp2iKyb/bm/x4JQzEvJgRHLGd5F5b81w==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.17.3': + resolution: {integrity: sha512-feq+K8TxIznZE+zhdVurF3WNJ/Sa35dQNYbaqM/wsCbWdzXr5lyq+AaTUSER2cUR+SXPnd/EY75EPRjf4s1SLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.17.3': + resolution: {integrity: sha512-JHeZXD4auLYBnrKn6JYJ0o5nWJI9PhChA/Nt0G4MvLaMrvXuWnY93R3a7PiXeJQphpL1nYsaMcoV2QtuvRnF/g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.17.3': + resolution: {integrity: sha512-CLP3EgyNuPcg2cshbwkqYy5bbAgK+VhyfMU7oIYyn+x4Y67xb5C5ylxsNUjRmr8BX+MW3YhVNm6Lq6FKtRTWHQ==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.17.3': + resolution: {integrity: sha512-FyXlD2ZjZqTFh0sOQxFDiWG1uQUEOLbEh9gKN/7pFxck5Vw0qjWSDqbn6C10GAa1rXJpwsntHcmLqydY9ST9ZA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.17.3': + resolution: {integrity: sha512-OrDGMvDBI2g7s04J8dh8/I7eSO+/E7nMDT2Z5IruBfUO/RiigF1OF6xoH33Dn4W/OwAWSUf1s2nXamb28ZklTA==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.17.3': + resolution: {integrity: sha512-DcnUpXnVCJvmv0TzuLwKBC2nsQHle8EIiAJiJ+PipEVC16wHXaPEKP0EqN8WnBe0TPvMITOUlP2aiL5YMld+CQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.17.3': + resolution: {integrity: sha512-BDYf/l1WVhWE+FHAW3FzZPtVlk9QsrwsxGzABmN4g8bTjmhazsId3h127pliDRRu5674k1Y2RWejbpN46N9ZhQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.17.3': + resolution: {integrity: sha512-WViAxWYMRIi+prTJTyV1wnqd2mS2cPqJlN85oscVhXdb/ZTFJdrpaqm/uDsZPGKHtbg5TuRX/ymKdOSk41YZow==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.17.3': + resolution: {integrity: sha512-Iw8lkNHUC4oGP1O/KhumcVy77u2s6+KUjieUqzEU3XuWJqZ+AY7uVMrrCbAiwWTkpQHkr00BuXH5RpC6Sb/7Ug==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.17.3': + resolution: {integrity: sha512-0AGkWQMzeoeAtXQRNB3s4J1/T2XbigM2/Mn2yU1tQSmQRmHIZdkGbVq2A3aDdNslPyhb9/lH0S5GMTZ4xsjBqg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.17.3': + resolution: {integrity: sha512-4+rR/WHOxIVh53UIQIICryjdoKdHsFZFD4zLSonJ9RRw7bhKzVyXbnRPsWSfwybYqw9sB7ots/SYyufL1mBpEg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.17.3': + resolution: {integrity: sha512-cVpWnkx9IYg99EjGxa5Gc0XmqumtAwK3aoz7O4Dii2vko+qXbkHoujWA68cqXjhh6TsLaQelfDO4MVnyr+ODeA==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.17.3': + resolution: {integrity: sha512-RxmhKLbTCDAY2xOfrww6ieIZkZF+KBqG7S2Ako2SljKXRFi+0863PspK74QQ7JpmWwncChY25JTJSbVBYGQk2Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.17.3': + resolution: {integrity: sha512-0r36VeEJ4efwmofxVJRXDjVRP2jTmv877zc+i+Pc7MNsIr38NfsjkQj23AfF7l0WbB+RQ7VUb+LDiqC/KY/M/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.17.3': + resolution: {integrity: sha512-wgO6rc7uGStH22nur4aLFcq7Wh86bE9cOFmfTr/yxN3BXvDEdCSXyKkO+U5JIt53eTOgC47v9k/C1bITWL/Teg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.17.3': + resolution: {integrity: sha512-FdVl64OIuiKjgXBjwZaJLKp0eaEckifbhn10dXWhysMJkWblg3OEEGKSIyhiD5RSgAya8WzP3DNkngtIg3Nt7g==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@marijn/find-cluster-break@1.0.2': + resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} + + '@types/codemirror@5.60.8': + resolution: {integrity: sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==} + + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + + '@types/tern@0.23.9': + resolution: {integrity: sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==} + + cjk-regex@3.3.0: + resolution: {integrity: sha512-o9QeA4DIiljRGO3mXzkQXBttzE6XRGZG99V9F8uqrdqKo5RHTFe8w+pk1aOMB/wxQ7qQ8J7WoTagabTabPgl8A==} + engines: {node: '>=16'} + + esbuild@0.17.3: + resolution: {integrity: sha512-9n3AsBRe6sIyOc6kmoXg2ypCLgf3eZSraWFRpnkto+svt8cZNuKTkb1bhQcitBcvIqjNiK7K0J3KPmwGSfkA8g==} + engines: {node: '>=12'} + hasBin: true + + moment@2.29.4: + resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} + + obsidian@1.8.7: + resolution: {integrity: sha512-h4bWwNFAGRXlMlMAzdEiIM2ppTGlrh7uGOJS6w4gClrsjc+ei/3YAtU2VdFUlCiPuTHpY4aBpFJJW75S1Tl/JA==} + peerDependencies: + '@codemirror/state': ^6.0.0 + '@codemirror/view': ^6.0.0 + + prettier@3.5.3: + resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + engines: {node: '>=14'} + hasBin: true + + regexp-util@2.0.3: + resolution: {integrity: sha512-GP6h9OgJmhAZpb3dbNbXTfRWVnGcoMhWRZv/HxgM4/qCVqs1P9ukQdYxaUhjWBSAs9oJ/uPXUUvGT1VMe0Bs0Q==} + engines: {node: '>=16'} + + style-mod@4.1.2: + resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} + + unicode-regex@4.1.2: + resolution: {integrity: sha512-30Y3tQ8OUxceQjsEJHzNh20lLYZX6ZwQyUOHBUdN1UPKQWH3AvH20aUADWa1gEz2lQPTSQ/l2ZqdM4FjFNMJsQ==} + engines: {node: '>=16'} + + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + +snapshots: + + '@codemirror/state@6.5.2': + dependencies: + '@marijn/find-cluster-break': 1.0.2 + + '@codemirror/view@6.36.7': + dependencies: + '@codemirror/state': 6.5.2 + style-mod: 4.1.2 + w3c-keyname: 2.2.8 + + '@esbuild/android-arm64@0.17.3': + optional: true + + '@esbuild/android-arm@0.17.3': + optional: true + + '@esbuild/android-x64@0.17.3': + optional: true + + '@esbuild/darwin-arm64@0.17.3': + optional: true + + '@esbuild/darwin-x64@0.17.3': + optional: true + + '@esbuild/freebsd-arm64@0.17.3': + optional: true + + '@esbuild/freebsd-x64@0.17.3': + optional: true + + '@esbuild/linux-arm64@0.17.3': + optional: true + + '@esbuild/linux-arm@0.17.3': + optional: true + + '@esbuild/linux-ia32@0.17.3': + optional: true + + '@esbuild/linux-loong64@0.17.3': + optional: true + + '@esbuild/linux-mips64el@0.17.3': + optional: true + + '@esbuild/linux-ppc64@0.17.3': + optional: true + + '@esbuild/linux-riscv64@0.17.3': + optional: true + + '@esbuild/linux-s390x@0.17.3': + optional: true + + '@esbuild/linux-x64@0.17.3': + optional: true + + '@esbuild/netbsd-x64@0.17.3': + optional: true + + '@esbuild/openbsd-x64@0.17.3': + optional: true + + '@esbuild/sunos-x64@0.17.3': + optional: true + + '@esbuild/win32-arm64@0.17.3': + optional: true + + '@esbuild/win32-ia32@0.17.3': + optional: true + + '@esbuild/win32-x64@0.17.3': + optional: true + + '@marijn/find-cluster-break@1.0.2': {} + + '@types/codemirror@5.60.8': + dependencies: + '@types/tern': 0.23.9 + + '@types/estree@1.0.7': {} + + '@types/tern@0.23.9': + dependencies: + '@types/estree': 1.0.7 + + cjk-regex@3.3.0: + dependencies: + regexp-util: 2.0.3 + unicode-regex: 4.1.2 + + esbuild@0.17.3: + optionalDependencies: + '@esbuild/android-arm': 0.17.3 + '@esbuild/android-arm64': 0.17.3 + '@esbuild/android-x64': 0.17.3 + '@esbuild/darwin-arm64': 0.17.3 + '@esbuild/darwin-x64': 0.17.3 + '@esbuild/freebsd-arm64': 0.17.3 + '@esbuild/freebsd-x64': 0.17.3 + '@esbuild/linux-arm': 0.17.3 + '@esbuild/linux-arm64': 0.17.3 + '@esbuild/linux-ia32': 0.17.3 + '@esbuild/linux-loong64': 0.17.3 + '@esbuild/linux-mips64el': 0.17.3 + '@esbuild/linux-ppc64': 0.17.3 + '@esbuild/linux-riscv64': 0.17.3 + '@esbuild/linux-s390x': 0.17.3 + '@esbuild/linux-x64': 0.17.3 + '@esbuild/netbsd-x64': 0.17.3 + '@esbuild/openbsd-x64': 0.17.3 + '@esbuild/sunos-x64': 0.17.3 + '@esbuild/win32-arm64': 0.17.3 + '@esbuild/win32-ia32': 0.17.3 + '@esbuild/win32-x64': 0.17.3 + + moment@2.29.4: {} + + obsidian@1.8.7(@codemirror/state@6.5.2)(@codemirror/view@6.36.7): + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.7 + '@types/codemirror': 5.60.8 + moment: 2.29.4 + + prettier@3.5.3: {} + + regexp-util@2.0.3: {} + + style-mod@4.1.2: {} + + unicode-regex@4.1.2: + dependencies: + regexp-util: 2.0.3 + + w3c-keyname@2.2.8: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..efc037a --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +onlyBuiltDependencies: + - esbuild diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..1dba4ec --- /dev/null +++ b/src/index.js @@ -0,0 +1,32 @@ +import { MarkdownView, Plugin } from "obsidian"; +import CJKCharacters from "./cjk-characters"; + +export default class CJKCountPlugin extends Plugin { + #statusBarItemEl; + + async onload() { + this.#statusBarItemEl = this.addStatusBarItem(); + this.#statusBarItemEl.setText("0 字"); + + this.registerEvent( + this.app.workspace.on("file-open", () => setTimeout(() => this.#updateCount(), 100)) + ); + + this.registerEvent(this.app.workspace.on("editor-change", () => this.#updateCount())); + + this.#updateCount(); + } + + #updateCount() { + const activeView = this.app.workspace.getActiveViewOfType(MarkdownView); + let characterCount = 0; + + if (activeView) { + const content = activeView.editor.getValue(); + const characters = content.match(new RegExp(CJKCharacters, "g")); + characterCount = characters ? characters.length : 0; + } + + this.#statusBarItemEl.setText(`${characterCount} 字`); + } +} diff --git a/src/scripts/generate-cjk-characters.js b/src/scripts/generate-cjk-characters.js new file mode 100644 index 0000000..fdff9bb --- /dev/null +++ b/src/scripts/generate-cjk-characters.js @@ -0,0 +1,4 @@ +import { writeFile } from "node:fs/promises"; +import { letters as CJKCharacters } from "cjk-regex"; + +writeFile("src/cjk-characters.js", `export default "${CJKCharacters().toString()}";`); diff --git a/version-bump.js b/version-bump.js new file mode 100644 index 0000000..d409fa0 --- /dev/null +++ b/version-bump.js @@ -0,0 +1,14 @@ +import { readFileSync, writeFileSync } from "fs"; + +const targetVersion = process.env.npm_package_version; + +// read minAppVersion from manifest.json and bump version to target version +let manifest = JSON.parse(readFileSync("manifest.json", "utf8")); +const { minAppVersion } = manifest; +manifest.version = targetVersion; +writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t")); + +// update versions.json with target version and minAppVersion from manifest.json +let versions = JSON.parse(readFileSync("versions.json", "utf8")); +versions[targetVersion] = minAppVersion; +writeFileSync("versions.json", JSON.stringify(versions, null, "\t")); diff --git a/versions.json b/versions.json new file mode 100644 index 0000000..af9a39e --- /dev/null +++ b/versions.json @@ -0,0 +1,3 @@ +{ + "1.0.0": "0.15.0" +}