obsidian-plugin-config 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/.vscode/settings.json +4 -0
  2. package/README.md +45 -0
  3. package/bin/obsidian-inject.js +98 -0
  4. package/obsidian-plugin-config-1.0.2.tgz +0 -0
  5. package/package.json +88 -0
  6. package/scripts/acp.ts +71 -0
  7. package/scripts/build-npm.ts +137 -0
  8. package/scripts/esbuild.config.ts +311 -0
  9. package/scripts/help.ts +46 -0
  10. package/scripts/inject-path.ts +487 -0
  11. package/scripts/inject-prompt.ts +399 -0
  12. package/scripts/open-editor.mjs +18 -0
  13. package/scripts/release.ts +97 -0
  14. package/scripts/update-exports.js +91 -0
  15. package/scripts/update-version-config.ts +98 -0
  16. package/scripts/update-version.ts +102 -0
  17. package/scripts/utils.ts +117 -0
  18. package/src/index.ts +6 -0
  19. package/src/main_test.ts +106 -0
  20. package/src/modals/GenericConfirmModal.ts +67 -0
  21. package/src/modals/index.ts +3 -0
  22. package/src/test-centralized-utils.ts +23 -0
  23. package/src/tools/index.ts +9 -0
  24. package/src/utils/NoticeHelper.ts +102 -0
  25. package/src/utils/SettingsHelper.ts +180 -0
  26. package/src/utils/index.ts +3 -0
  27. package/templates/.vscode/settings.json +4 -0
  28. package/templates/eslint.config.ts +48 -0
  29. package/templates/help-plugin.ts +39 -0
  30. package/templates/package-versions.json +28 -0
  31. package/templates/tsconfig.json +37 -0
  32. package/test-plugin/manifest.json +10 -0
  33. package/test-plugin/package.json +38 -0
  34. package/test-plugin/scripts/acp.ts +71 -0
  35. package/test-plugin/scripts/esbuild.config.ts +165 -0
  36. package/test-plugin/scripts/help.ts +29 -0
  37. package/test-plugin/scripts/release.ts +97 -0
  38. package/test-plugin/scripts/update-version.ts +102 -0
  39. package/test-plugin/scripts/utils.ts +117 -0
  40. package/test-plugin/src/main.ts +11 -0
  41. package/test-plugin/yarn.lock +386 -0
  42. package/test-plugin-v2/main.js +5 -0
  43. package/test-plugin-v2/manifest.json +10 -0
  44. package/test-plugin-v2/package.json +40 -0
  45. package/test-plugin-v2/scripts/acp.ts +71 -0
  46. package/test-plugin-v2/scripts/esbuild.config.ts +165 -0
  47. package/test-plugin-v2/scripts/help.ts +29 -0
  48. package/test-plugin-v2/scripts/release.ts +97 -0
  49. package/test-plugin-v2/scripts/update-version.ts +102 -0
  50. package/test-plugin-v2/scripts/utils.ts +117 -0
  51. package/test-plugin-v2/src/main.ts +11 -0
  52. package/test-plugin-v2/tsconfig.json +31 -0
  53. package/test-plugin-v2/yarn.lock +1986 -0
  54. package/tsconfig.json +38 -0
  55. package/versions.json +5 -0
@@ -0,0 +1,102 @@
1
+ import { readFile, writeFile } from "fs/promises";
2
+ import dedent from "dedent";
3
+ import { inc, valid } from "semver";
4
+ import { askQuestion, createReadlineInterface, gitExec } from "./utils.js";
5
+
6
+ const rl = createReadlineInterface();
7
+
8
+ async function getTargetVersion(currentVersion: string): Promise<string> {
9
+ const updateType = await askQuestion(dedent`
10
+ Current version: ${currentVersion}
11
+ Kind of update:
12
+ patch(1.0.1) -> type 1 or p
13
+ minor(1.1.0) -> type 2 or min
14
+ major(2.0.0) -> type 3 or maj
15
+ or version number (e.g. 2.0.0)
16
+ Enter choice: `, rl);
17
+
18
+ switch (updateType.trim()) {
19
+ case "p":
20
+ case "1":
21
+ return inc(currentVersion, "patch") || "";
22
+ case "min":
23
+ case "2":
24
+ return inc(currentVersion, "minor") || "";
25
+ case "maj":
26
+ case "3":
27
+ return inc(currentVersion, "major") || "";
28
+ default:
29
+ return valid(updateType.trim()) || "";
30
+ }
31
+ }
32
+
33
+ async function updateJsonFile(filename: string, updateFn: (json: any) => void): Promise<void> {
34
+ try {
35
+ const content = JSON.parse(await readFile(filename, "utf8"));
36
+ updateFn(content);
37
+ await writeFile(filename, JSON.stringify(content, null, "\t"));
38
+ } catch (error) {
39
+ console.error(`Error updating ${filename}:`, error instanceof Error ? error.message : String(error));
40
+ throw error;
41
+ }
42
+ }
43
+
44
+ async function updateManifestVersions(targetVersion: string): Promise<void> {
45
+ try {
46
+ const manifest = JSON.parse(await readFile("manifest.json", "utf8"));
47
+ const { minAppVersion } = manifest;
48
+
49
+ await Promise.all([
50
+ updateJsonFile("manifest.json", json => json.version = targetVersion),
51
+ updateJsonFile("versions.json", json => json[targetVersion] = minAppVersion),
52
+ updateJsonFile("package.json", json => json.version = targetVersion),
53
+ // updateJsonFile("package-lock.json", json => json.version = targetVersion)
54
+ ]);
55
+ } catch (error) {
56
+ console.error("Error updating manifest versions:", error instanceof Error ? error.message : String(error));
57
+ throw error;
58
+ }
59
+ }
60
+
61
+ async function updateVersion(): Promise<void> {
62
+ try {
63
+ const currentVersion = process.env.npm_package_version || "1.0.0";
64
+ const targetVersion = await getTargetVersion(currentVersion);
65
+
66
+ if (!targetVersion) {
67
+ console.log("Invalid version");
68
+ return;
69
+ }
70
+
71
+ try {
72
+ // Update all files first
73
+ await updateManifestVersions(targetVersion);
74
+ console.log(`Files updated to version ${targetVersion}`);
75
+
76
+ // Add files to git
77
+ gitExec("git add manifest.json package.json versions.json");
78
+ gitExec(`git commit -m "Updated to version ${targetVersion}"`);
79
+ console.log("Changes committed");
80
+ } catch (error) {
81
+ console.error("Error during update or commit:", error instanceof Error ? error.message : String(error));
82
+ console.log("Operation failed.");
83
+ return;
84
+ }
85
+
86
+ try {
87
+ gitExec("git push");
88
+ console.log(`Version successfully updated to ${targetVersion} and pushed.`);
89
+ } catch (pushError) {
90
+ console.error("Failed to push version update:", pushError instanceof Error ? pushError.message : String(pushError));
91
+ }
92
+ } catch (error) {
93
+ console.error("Error:", error instanceof Error ? error.message : String(error));
94
+ } finally {
95
+ rl.close();
96
+ }
97
+ }
98
+
99
+ updateVersion().catch(console.error).finally(() => {
100
+ console.log("Exiting...");
101
+ process.exit();
102
+ });
@@ -0,0 +1,117 @@
1
+ import {
2
+ access,
3
+ mkdir,
4
+ copyFile,
5
+ rm
6
+ } from "fs/promises";
7
+ import path from "path";
8
+ import * as readline from "readline";
9
+ import { execSync } from "child_process";
10
+
11
+ export function createReadlineInterface(): readline.Interface {
12
+ return readline.createInterface({
13
+ input: process.stdin as NodeJS.ReadableStream,
14
+ output: process.stdout as NodeJS.WritableStream,
15
+ });
16
+ }
17
+
18
+ export const askQuestion = async (question: string, rl: readline.Interface): Promise<string> => {
19
+ try {
20
+ return await new Promise(resolve => rl.question(question, input => resolve(input.trim())));
21
+ } catch (error) {
22
+ console.error("Error asking question:", error);
23
+ throw error;
24
+ }
25
+ };
26
+
27
+ /**
28
+ * Ask a yes/no confirmation question with standardized logic
29
+ * Accepts: y, yes, Y, YES, or empty (default to yes)
30
+ * Rejects: n, no, N, NO
31
+ * Invalid input defaults to no for safety
32
+ */
33
+ export const askConfirmation = async (question: string, rl: readline.Interface): Promise<boolean> => {
34
+ const answer = await askQuestion(`${question} [Y/n]: `, rl);
35
+ const response = answer.toLowerCase();
36
+
37
+ // Accept: y, yes, Y, YES, or empty (default to yes)
38
+ // Reject: n, no, N, NO
39
+ const isYes = response === '' || response === 'y' || response === 'yes';
40
+ const isNo = response === 'n' || response === 'no';
41
+
42
+ if (isNo) {
43
+ return false;
44
+ } else if (isYes) {
45
+ return true;
46
+ } else {
47
+ console.log("Please answer Y (yes) or n (no). Defaulting to no for safety.");
48
+ return false;
49
+ }
50
+ };
51
+
52
+ export const cleanInput = (inputStr: string): string => {
53
+ if (!inputStr) return "";
54
+ return inputStr.trim().replace(/["`]/g, "'").replace(/\r\n/g, "\n");
55
+ };
56
+
57
+ export const isValidPath = async (pathToCheck: string): Promise<boolean> => {
58
+ if (!pathToCheck) return false;
59
+
60
+ try {
61
+ // Using async fs.access is preferred over synchronous existsSync
62
+ // as it doesn't block the main thread/event loop
63
+ await access(pathToCheck.trim());
64
+ return true;
65
+ } catch {
66
+ return false;
67
+ }
68
+ };
69
+
70
+ export async function copyFilesToTargetDir(buildPath: string): Promise<void> {
71
+ const manifestDest = path.join(buildPath, "manifest.json");
72
+ const cssDest = path.join(buildPath, "styles.css");
73
+ const folderToRemove = path.join(buildPath, "_.._");
74
+
75
+ try {
76
+ await mkdir(buildPath);
77
+ } catch (error: any) {
78
+ if (error.code !== "EEXIST") {
79
+ console.error(`Error creating directory: ${error.message}`);
80
+ }
81
+ }
82
+
83
+ // Copy manifest
84
+ try {
85
+ await copyFile("./manifest.json", manifestDest);
86
+ } catch (error: any) {
87
+ console.error(`Error copying manifest: ${error.message}`);
88
+ }
89
+
90
+ // Copy CSS
91
+ try {
92
+ // First check if CSS exists in src/styles.css
93
+ if (await isValidPath("./src/styles.css")) {
94
+ await copyFile("./src/styles.css", cssDest);
95
+ }
96
+ // Otherwise, check if it exists in the root
97
+ else if (await isValidPath("./styles.css")) {
98
+ await copyFile("./styles.css", cssDest);
99
+ if (await isValidPath(folderToRemove)) {
100
+ await rm(folderToRemove, { recursive: true });
101
+ }
102
+ } else {
103
+ return;
104
+ }
105
+ } catch (error: any) {
106
+ console.error(`Error copying CSS: ${error.message}`);
107
+ }
108
+ }
109
+
110
+ export function gitExec(command: string): void {
111
+ try {
112
+ execSync(command, { stdio: "inherit" });
113
+ } catch (error: any) {
114
+ console.error(`Error executing '${command}':`, error.message);
115
+ throw error;
116
+ }
117
+ }
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ // Auto-generated exports - DO NOT EDIT MANUALLY
2
+ // Run 'npm run update-exports' to regenerate this file
3
+
4
+ export * from './modals/index.js';
5
+ export * from './tools/index.js';
6
+ export * from './utils/index.js';
@@ -0,0 +1,106 @@
1
+ import {
2
+ App,
3
+ Plugin,
4
+ PluginSettingTab,
5
+ Setting,
6
+ Notice
7
+ } from "obsidian";
8
+ import { showConfirmModal } from "./modals/GenericConfirmModal.ts";
9
+ import { showTestMessage, getRandomEmoji } from "obsidian-plugin-config/tools";
10
+
11
+ interface MyPluginSettings {
12
+ mySetting: string;
13
+ }
14
+
15
+ const DEFAULT_SETTINGS: MyPluginSettings = {
16
+ mySetting: "default"
17
+ };
18
+
19
+ export default class MyPlugin extends Plugin {
20
+ settings: MyPluginSettings;
21
+
22
+ async onload(): Promise<void> {
23
+ console.log("loading plugin");
24
+ await this.loadSettings();
25
+
26
+ this.addCommand({
27
+ id: 'show-confirmation-modal',
28
+ name: 'Show Confirmation Modal (Local)',
29
+ callback: () => this.showConfirmationModal()
30
+ });
31
+
32
+ this.addCommand({
33
+ id: 'show-centralized-modal',
34
+ name: 'Show Confirmation Modal (Centralized)',
35
+ callback: () => this.showCentralizedModal()
36
+ });
37
+
38
+ this.addCommand({
39
+ id: 'test-tools',
40
+ name: 'Test Centralized Tools',
41
+ callback: () => {
42
+ const message = showTestMessage();
43
+ const emoji = getRandomEmoji();
44
+ new Notice(`${emoji} ${message}`);
45
+ }
46
+ });
47
+
48
+ this.addSettingTab(new SampleSettingTab(this.app, this));
49
+ }
50
+
51
+ private showConfirmationModal(): void {
52
+ showConfirmModal(this.app, {
53
+ title: "Confirmation requise",
54
+ message: "รŠtes-vous sรปr de vouloir effectuer cette action ? Cette action ne peut pas รชtre annulรฉe.",
55
+ confirmText: "Confirmer",
56
+ cancelText: "Annuler",
57
+ onConfirm: () => {
58
+ new Notice("Action confirmรฉe !");
59
+ console.log("Action confirmรฉe par l'utilisateur");
60
+ },
61
+ onCancel: () => {
62
+ new Notice("Action annulรฉe.");
63
+ console.log("Action annulรฉe par l'utilisateur");
64
+ }
65
+ });
66
+ }
67
+
68
+ private showCentralizedModal(): void {
69
+ showConfirmModal(this.app, {
70
+ title: "Centralized Modal Test",
71
+ message: "This modal comes from the centralized configuration! Pretty cool, right?",
72
+ confirmText: "Awesome!",
73
+ cancelText: "Not bad",
74
+ onConfirm: () => {
75
+ new Notice("Centralized modal confirmed! ๐ŸŽ‰");
76
+ },
77
+ onCancel: () => {
78
+ new Notice("Centralized modal cancelled ๐Ÿ˜ข");
79
+ }
80
+ });
81
+ }
82
+
83
+ async loadSettings(): Promise<void> {
84
+ this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
85
+ }
86
+
87
+ async saveSettings(): Promise<void> {
88
+ await this.saveData(this.settings);
89
+ }
90
+ }
91
+
92
+ class SampleSettingTab extends PluginSettingTab {
93
+ plugin: MyPlugin;
94
+
95
+ constructor(app: App, plugin: MyPlugin) {
96
+ super(app, plugin);
97
+ this.plugin = plugin;
98
+ }
99
+
100
+ display(): void {
101
+ const { containerEl } = this;
102
+ containerEl.empty();
103
+ new Setting(containerEl);
104
+ }
105
+ }
106
+
@@ -0,0 +1,67 @@
1
+ import { App, Modal } from "obsidian";
2
+ import { capitalize } from "lodash";
3
+
4
+ export interface ConfirmModalOptions {
5
+ title: string;
6
+ message: string;
7
+ confirmText?: string;
8
+ cancelText?: string;
9
+ onConfirm: () => void;
10
+ onCancel?: () => void;
11
+ }
12
+
13
+ export class GenericConfirmModal extends Modal {
14
+ private options: ConfirmModalOptions;
15
+
16
+ constructor(app: App, options: ConfirmModalOptions) {
17
+ super(app);
18
+ this.options = options;
19
+ }
20
+
21
+ onOpen(): void {
22
+ const { contentEl } = this;
23
+ contentEl.empty();
24
+
25
+ // Title (using lodash capitalize for demo)
26
+ contentEl.createEl("h2", { text: capitalize(this.options.title) });
27
+
28
+ // Message
29
+ contentEl.createEl("p", { text: this.options.message });
30
+
31
+ // Buttons container
32
+ const buttonContainer = contentEl.createDiv("modal-button-container");
33
+
34
+ // Cancel button
35
+ const cancelBtn = buttonContainer.createEl("button", {
36
+ text: this.options.cancelText || "Cancel",
37
+ cls: "mod-cta"
38
+ });
39
+ cancelBtn.addEventListener("click", () => {
40
+ this.options.onCancel?.();
41
+ this.close();
42
+ });
43
+
44
+ // Confirm button
45
+ const confirmBtn = buttonContainer.createEl("button", {
46
+ text: this.options.confirmText || "Confirm",
47
+ cls: "mod-cta mod-warning"
48
+ });
49
+ confirmBtn.addEventListener("click", () => {
50
+ this.options.onConfirm();
51
+ this.close();
52
+ });
53
+
54
+ // Focus on confirm button
55
+ confirmBtn.focus();
56
+ }
57
+
58
+ onClose(): void {
59
+ const { contentEl } = this;
60
+ contentEl.empty();
61
+ }
62
+ }
63
+
64
+ // Utility function for quick usage
65
+ export function showConfirmModal(app: App, options: ConfirmModalOptions): void {
66
+ new GenericConfirmModal(app, options).open();
67
+ }
@@ -0,0 +1,3 @@
1
+ // Export all modals for easy importing
2
+ export { GenericConfirmModal, showConfirmModal } from './GenericConfirmModal.js';
3
+ export type { ConfirmModalOptions } from './GenericConfirmModal.js';
@@ -0,0 +1,23 @@
1
+ // Simple test file to verify centralized utils work
2
+ import { NoticeHelper } from "obsidian-plugin-config/utils";
3
+
4
+ export function testCentralizedUtils(): void {
5
+ console.log("๐Ÿงช Testing centralized utils...");
6
+
7
+ // Test different notice types
8
+ NoticeHelper.success("โœ… Centralized utils are working!");
9
+
10
+ setTimeout(() => {
11
+ NoticeHelper.info("โ„น๏ธ This notice comes from the centralized config");
12
+ }, 1000);
13
+
14
+ setTimeout(() => {
15
+ NoticeHelper.warning("โš ๏ธ This is a warning from centralized utils");
16
+ }, 2000);
17
+
18
+ setTimeout(() => {
19
+ NoticeHelper.custom("๐Ÿš€", "Custom notice with rocket emoji!");
20
+ }, 3000);
21
+
22
+ console.log("โœ… All centralized utils tests completed!");
23
+ }
@@ -0,0 +1,9 @@
1
+ // Simple tools for testing
2
+ export function showTestMessage(): string {
3
+ return "โœ… CENTRALIZED TOOLS WORK! This comes from obsidian-plugin-config!";
4
+ }
5
+
6
+ export function getRandomEmoji(): string {
7
+ const emojis = ["๐Ÿš€", "๐ŸŽ‰", "โœจ", "๐Ÿ”ฅ", "๐Ÿ’ฏ", "โšก", "๐ŸŽฏ", "๐ŸŒŸ"];
8
+ return emojis[Math.floor(Math.random() * emojis.length)];
9
+ }
@@ -0,0 +1,102 @@
1
+ import { Notice } from "obsidian";
2
+
3
+ /**
4
+ * Enhanced Notice helper with different types and durations
5
+ */
6
+ export class NoticeHelper {
7
+ private static readonly DEFAULT_DURATION = 5000;
8
+ private static readonly SUCCESS_DURATION = 3000;
9
+ private static readonly ERROR_DURATION = 8000;
10
+ private static readonly WARNING_DURATION = 6000;
11
+
12
+ /**
13
+ * Show a success notice with green styling
14
+ */
15
+ static success(message: string, duration?: number): Notice {
16
+ const notice = new Notice(
17
+ `โœ… ${message}`,
18
+ duration ?? this.SUCCESS_DURATION
19
+ );
20
+ return notice;
21
+ }
22
+
23
+ /**
24
+ * Show an error notice with red styling
25
+ */
26
+ static error(message: string, duration?: number): Notice {
27
+ const notice = new Notice(
28
+ `โŒ ${message}`,
29
+ duration ?? this.ERROR_DURATION
30
+ );
31
+ return notice;
32
+ }
33
+
34
+ /**
35
+ * Show a warning notice with yellow styling
36
+ */
37
+ static warning(message: string, duration?: number): Notice {
38
+ const notice = new Notice(
39
+ `โš ๏ธ ${message}`,
40
+ duration ?? this.WARNING_DURATION
41
+ );
42
+ return notice;
43
+ }
44
+
45
+ /**
46
+ * Show an info notice with blue styling
47
+ */
48
+ static info(message: string, duration?: number): Notice {
49
+ const notice = new Notice(
50
+ `โ„น๏ธ ${message}`,
51
+ duration ?? this.DEFAULT_DURATION
52
+ );
53
+ return notice;
54
+ }
55
+
56
+ /**
57
+ * Show a loading notice that can be updated
58
+ */
59
+ static loading(message: string): Notice {
60
+ return new Notice(`โณ ${message}`, 0); // 0 = permanent until manually hidden
61
+ }
62
+
63
+ /**
64
+ * Update a loading notice to success and auto-hide
65
+ */
66
+ static updateToSuccess(notice: Notice, message: string): void {
67
+ notice.setMessage(`โœ… ${message}`);
68
+ setTimeout(() => notice.hide(), this.SUCCESS_DURATION);
69
+ }
70
+
71
+ /**
72
+ * Update a loading notice to error and auto-hide
73
+ */
74
+ static updateToError(notice: Notice, message: string): void {
75
+ notice.setMessage(`โŒ ${message}`);
76
+ setTimeout(() => notice.hide(), this.ERROR_DURATION);
77
+ }
78
+
79
+ /**
80
+ * Show a notice with custom emoji and duration
81
+ */
82
+ static custom(emoji: string, message: string, duration?: number): Notice {
83
+ return new Notice(
84
+ `${emoji} ${message}`,
85
+ duration ?? this.DEFAULT_DURATION
86
+ );
87
+ }
88
+
89
+ /**
90
+ * Show a progress notice for long operations
91
+ */
92
+ static progress(message: string, current: number, total: number): Notice {
93
+ const percentage = Math.round((current / total) * 100);
94
+ const progressBar = "โ–ˆ".repeat(Math.floor(percentage / 5)) +
95
+ "โ–‘".repeat(20 - Math.floor(percentage / 5));
96
+
97
+ return new Notice(
98
+ `๐Ÿ”„ ${message}\n[${progressBar}] ${percentage}%`,
99
+ 0
100
+ );
101
+ }
102
+ }