@real1ty-obsidian-plugins/utils 2.0.0 → 2.1.0

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 (81) hide show
  1. package/dist/async-utils.d.ts +69 -0
  2. package/dist/async-utils.d.ts.map +1 -0
  3. package/dist/async-utils.js +108 -0
  4. package/dist/async-utils.js.map +1 -0
  5. package/dist/batch-operations.d.ts +11 -0
  6. package/dist/batch-operations.d.ts.map +1 -0
  7. package/dist/batch-operations.js +31 -0
  8. package/dist/batch-operations.js.map +1 -0
  9. package/dist/child-reference-utils.d.ts +9 -0
  10. package/dist/child-reference-utils.d.ts.map +1 -0
  11. package/dist/child-reference-utils.js +57 -0
  12. package/dist/child-reference-utils.js.map +1 -0
  13. package/dist/date-recurrence-utils.d.ts +30 -0
  14. package/dist/date-recurrence-utils.d.ts.map +1 -0
  15. package/dist/date-recurrence-utils.js +167 -0
  16. package/dist/date-recurrence-utils.js.map +1 -0
  17. package/dist/date-utils.d.ts +21 -0
  18. package/dist/date-utils.d.ts.map +1 -0
  19. package/dist/date-utils.js +105 -0
  20. package/dist/date-utils.js.map +1 -0
  21. package/dist/evaluator-base.d.ts +52 -0
  22. package/dist/evaluator-base.d.ts.map +1 -0
  23. package/dist/evaluator-base.js +84 -0
  24. package/dist/evaluator-base.js.map +1 -0
  25. package/dist/file-operations.d.ts +31 -0
  26. package/dist/file-operations.d.ts.map +1 -0
  27. package/dist/file-operations.js +160 -0
  28. package/dist/file-operations.js.map +1 -0
  29. package/dist/file-utils.d.ts +6 -0
  30. package/dist/file-utils.d.ts.map +1 -0
  31. package/dist/file-utils.js +25 -0
  32. package/dist/file-utils.js.map +1 -0
  33. package/dist/frontmatter-utils.d.ts +15 -0
  34. package/dist/frontmatter-utils.d.ts.map +1 -0
  35. package/dist/frontmatter-utils.js +68 -0
  36. package/dist/frontmatter-utils.js.map +1 -0
  37. package/dist/generate.d.ts +7 -0
  38. package/dist/generate.d.ts.map +1 -0
  39. package/dist/generate.js +13 -0
  40. package/dist/generate.js.map +1 -0
  41. package/dist/index.d.ts +16 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +16 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/link-parser.d.ts +9 -0
  46. package/dist/link-parser.d.ts.map +1 -0
  47. package/dist/link-parser.js +19 -0
  48. package/dist/link-parser.js.map +1 -0
  49. package/dist/settings-store.d.ts +19 -0
  50. package/dist/settings-store.d.ts.map +1 -0
  51. package/dist/settings-store.js +79 -0
  52. package/dist/settings-store.js.map +1 -0
  53. package/dist/settings-ui-builder.d.ts +58 -0
  54. package/dist/settings-ui-builder.d.ts.map +1 -0
  55. package/dist/settings-ui-builder.js +218 -0
  56. package/dist/settings-ui-builder.js.map +1 -0
  57. package/dist/string-utils.d.ts +5 -0
  58. package/dist/string-utils.d.ts.map +1 -0
  59. package/dist/string-utils.js +25 -0
  60. package/dist/string-utils.js.map +1 -0
  61. package/dist/templater-utils.d.ts +4 -0
  62. package/dist/templater-utils.d.ts.map +1 -0
  63. package/dist/templater-utils.js +51 -0
  64. package/dist/templater-utils.js.map +1 -0
  65. package/dist/testing/index.d.ts +5 -0
  66. package/dist/testing/index.d.ts.map +1 -0
  67. package/dist/testing/index.js +6 -0
  68. package/dist/testing/index.js.map +1 -0
  69. package/dist/testing/mocks/obsidian.d.ts +149 -0
  70. package/dist/testing/mocks/obsidian.d.ts.map +1 -0
  71. package/dist/testing/mocks/obsidian.js +220 -0
  72. package/dist/testing/mocks/obsidian.js.map +1 -0
  73. package/dist/testing/mocks/utils.d.ts +14 -0
  74. package/dist/testing/mocks/utils.d.ts.map +1 -0
  75. package/dist/testing/mocks/utils.js +85 -0
  76. package/dist/testing/mocks/utils.js.map +1 -0
  77. package/dist/testing/setup.d.ts +2 -0
  78. package/dist/testing/setup.d.ts.map +1 -0
  79. package/dist/testing/setup.js +18 -0
  80. package/dist/testing/setup.js.map +1 -0
  81. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter-utils.js","sourceRoot":"","sources":["../src/frontmatter-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAAc;IACvD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC3C,OAAO,EAAE,CAAC;IACX,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACd,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAC7D,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,WAAmB;IACxD,eAAe;IACf,IAAI,WAAW,KAAK,EAAE,EAAE,CAAC;QACxB,OAAO,EAAE,CAAC;IACX,CAAC;IAED,+CAA+C;IAC/C,IACC,CAAC,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC1D,CAAC,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC1D,WAAW,KAAK,MAAM,EACrB,CAAC;QACF,IAAI,CAAC;YACJ,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAChC,CAAC;QAAC,WAAM,CAAC;YACR,wCAAwC;YACxC,OAAO,WAAW,CAAC;QACpB,CAAC;IACF,CAAC;IAED,iBAAiB;IACjB,IAAI,WAAW,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACb,CAAC;IACD,IAAI,WAAW,KAAK,OAAO,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC;IACd,CAAC;IAED,gDAAgD;IAChD,IAAI,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;QAChC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,GAAG,CAAC;QACZ,CAAC;IACF,CAAC;IAED,4CAA4C;IAC5C,OAAO,WAAW,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAA8B;IACpE,MAAM,MAAM,GAA4B,EAAE,CAAC;IAE3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,MAAM,CAAC,GAAG,CAAC,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC","sourcesContent":["/**\n * Serializes a frontmatter value to a string representation for display in input fields.\n * Converts arrays, objects, booleans, and numbers to their string representations.\n */\nexport function serializeFrontmatterValue(value: unknown): string {\n\tif (value === null || value === undefined) {\n\t\treturn \"\";\n\t}\n\n\tif (typeof value === \"string\") {\n\t\treturn value;\n\t}\n\n\tif (typeof value === \"number\" || typeof value === \"boolean\") {\n\t\treturn String(value);\n\t}\n\n\tif (Array.isArray(value) || typeof value === \"object\") {\n\t\treturn JSON.stringify(value);\n\t}\n\n\treturn String(value);\n}\n\n/**\n * Parses a string value back to its original frontmatter type.\n * Detects and converts to: arrays, objects, booleans, numbers, or keeps as string.\n */\nexport function parseFrontmatterValue(stringValue: string): unknown {\n\t// Empty string\n\tif (stringValue === \"\") {\n\t\treturn \"\";\n\t}\n\n\t// Try to parse as JSON (arrays, objects, null)\n\tif (\n\t\t(stringValue.startsWith(\"[\") && stringValue.endsWith(\"]\")) ||\n\t\t(stringValue.startsWith(\"{\") && stringValue.endsWith(\"}\")) ||\n\t\tstringValue === \"null\"\n\t) {\n\t\ttry {\n\t\t\treturn JSON.parse(stringValue);\n\t\t} catch {\n\t\t\t// If JSON parse fails, return as string\n\t\t\treturn stringValue;\n\t\t}\n\t}\n\n\t// Parse booleans\n\tif (stringValue === \"true\") {\n\t\treturn true;\n\t}\n\tif (stringValue === \"false\") {\n\t\treturn false;\n\t}\n\n\t// Parse numbers (including integers and floats)\n\tif (/^-?\\d+(\\.\\d+)?$/.test(stringValue)) {\n\t\tconst num = Number(stringValue);\n\t\tif (!Number.isNaN(num)) {\n\t\t\treturn num;\n\t\t}\n\t}\n\n\t// Return as string if no other type matches\n\treturn stringValue;\n}\n\n/**\n * Converts a record of string values back to their original frontmatter types.\n */\nexport function parseFrontmatterRecord(record: Record<string, string>): Record<string, unknown> {\n\tconst parsed: Record<string, unknown> = {};\n\n\tfor (const [key, value] of Object.entries(record)) {\n\t\tparsed[key] = parseFrontmatterValue(value);\n\t}\n\n\treturn parsed;\n}\n"]}
@@ -0,0 +1,7 @@
1
+ export interface TimestampData {
2
+ startDate: string;
3
+ zettelId: number;
4
+ }
5
+ export declare const generateZettelId: () => number;
6
+ export declare const generateTimestamps: () => TimestampData;
7
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,gBAAgB,QAAO,MAMnC,CAAC;AAEF,eAAO,MAAM,kBAAkB,QAAO,aAQrC,CAAC"}
@@ -0,0 +1,13 @@
1
+ export const generateZettelId = () => {
2
+ const currentTimestamp = new Date();
3
+ const padWithZero = (number) => String(number).padStart(2, "0");
4
+ return Number(`${currentTimestamp.getFullYear()}${padWithZero(currentTimestamp.getMonth() + 1)}${padWithZero(currentTimestamp.getDate())}${padWithZero(currentTimestamp.getHours())}${padWithZero(currentTimestamp.getMinutes())}${padWithZero(currentTimestamp.getSeconds())}`);
5
+ };
6
+ export const generateTimestamps = () => {
7
+ const padWithZero = (number) => String(number).padStart(2, "0");
8
+ const currentDate = new Date();
9
+ const formattedStartDate = `${currentDate.getFullYear()}-${padWithZero(currentDate.getMonth() + 1)}-${padWithZero(currentDate.getDate())}`;
10
+ const uniqueZettelId = generateZettelId();
11
+ return { startDate: formattedStartDate, zettelId: uniqueZettelId };
12
+ };
13
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAW,EAAE;IAC5C,MAAM,gBAAgB,GAAG,IAAI,IAAI,EAAE,CAAC;IACpC,MAAM,WAAW,GAAG,CAAC,MAAc,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACxE,OAAO,MAAM,CACZ,GAAG,gBAAgB,CAAC,WAAW,EAAE,GAAG,WAAW,CAAC,gBAAgB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,GAAG,WAAW,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,GAAG,WAAW,CAAC,gBAAgB,CAAC,QAAQ,EAAE,CAAC,GAAG,WAAW,CAAC,gBAAgB,CAAC,UAAU,EAAE,CAAC,GAAG,WAAW,CAAC,gBAAgB,CAAC,UAAU,EAAE,CAAC,EAAE,CACjQ,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAkB,EAAE;IACrD,MAAM,WAAW,GAAG,CAAC,MAAc,EAAU,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAChF,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC;IAE/B,MAAM,kBAAkB,GAAW,GAAG,WAAW,CAAC,WAAW,EAAE,IAAI,WAAW,CAAC,WAAW,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,WAAW,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;IACnJ,MAAM,cAAc,GAAW,gBAAgB,EAAE,CAAC;IAElD,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC;AACpE,CAAC,CAAC","sourcesContent":["export interface TimestampData {\n\tstartDate: string;\n\tzettelId: number;\n}\n\nexport const generateZettelId = (): number => {\n\tconst currentTimestamp = new Date();\n\tconst padWithZero = (number: number) => String(number).padStart(2, \"0\");\n\treturn Number(\n\t\t`${currentTimestamp.getFullYear()}${padWithZero(currentTimestamp.getMonth() + 1)}${padWithZero(currentTimestamp.getDate())}${padWithZero(currentTimestamp.getHours())}${padWithZero(currentTimestamp.getMinutes())}${padWithZero(currentTimestamp.getSeconds())}`\n\t);\n};\n\nexport const generateTimestamps = (): TimestampData => {\n\tconst padWithZero = (number: number): string => String(number).padStart(2, \"0\");\n\tconst currentDate = new Date();\n\n\tconst formattedStartDate: string = `${currentDate.getFullYear()}-${padWithZero(currentDate.getMonth() + 1)}-${padWithZero(currentDate.getDate())}`;\n\tconst uniqueZettelId: number = generateZettelId();\n\n\treturn { startDate: formattedStartDate, zettelId: uniqueZettelId };\n};\n"]}
@@ -0,0 +1,16 @@
1
+ export * from "./async-utils";
2
+ export * from "./batch-operations";
3
+ export * from "./child-reference-utils";
4
+ export * from "./date-recurrence-utils";
5
+ export * from "./date-utils";
6
+ export * from "./evaluator-base";
7
+ export * from "./file-operations";
8
+ export * from "./file-utils";
9
+ export * from "./frontmatter-utils";
10
+ export * from "./generate";
11
+ export * from "./link-parser";
12
+ export * from "./settings-store";
13
+ export * from "./settings-ui-builder";
14
+ export * from "./string-utils";
15
+ export * from "./templater-utils";
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ export * from "./async-utils";
2
+ export * from "./batch-operations";
3
+ export * from "./child-reference-utils";
4
+ export * from "./date-recurrence-utils";
5
+ export * from "./date-utils";
6
+ export * from "./evaluator-base";
7
+ export * from "./file-operations";
8
+ export * from "./file-utils";
9
+ export * from "./frontmatter-utils";
10
+ export * from "./generate";
11
+ export * from "./link-parser";
12
+ export * from "./settings-store";
13
+ export * from "./settings-ui-builder";
14
+ export * from "./string-utils";
15
+ export * from "./templater-utils";
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC","sourcesContent":["export * from \"./async-utils\";\nexport * from \"./batch-operations\";\nexport * from \"./child-reference-utils\";\nexport * from \"./date-recurrence-utils\";\nexport * from \"./date-utils\";\nexport * from \"./evaluator-base\";\nexport * from \"./file-operations\";\nexport * from \"./file-utils\";\nexport * from \"./frontmatter-utils\";\nexport * from \"./generate\";\nexport * from \"./link-parser\";\nexport * from \"./settings-store\";\nexport * from \"./settings-ui-builder\";\nexport * from \"./string-utils\";\nexport * from \"./templater-utils\";\n"]}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Handles different link formats and edge cases:
3
+ * [[FileName]] -> FileName.md
4
+ * [[Folder/FileName]] -> Folder/FileName.md
5
+ * [[Folder/FileName|DisplayName]] -> Folder/FileName.md
6
+ * Normalizes paths and handles malformed links gracefully.
7
+ */
8
+ export declare const extractFilePathFromLink: (link: string) => string | null;
9
+ //# sourceMappingURL=link-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-parser.d.ts","sourceRoot":"","sources":["../src/link-parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GAAI,MAAM,MAAM,KAAG,MAAM,GAAG,IAU/D,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Handles different link formats and edge cases:
3
+ * [[FileName]] -> FileName.md
4
+ * [[Folder/FileName]] -> Folder/FileName.md
5
+ * [[Folder/FileName|DisplayName]] -> Folder/FileName.md
6
+ * Normalizes paths and handles malformed links gracefully.
7
+ */
8
+ export const extractFilePathFromLink = (link) => {
9
+ const match = link.match(/\[\[([^|\]]+?)(?:\|.*?)?\]\]/);
10
+ if (!match)
11
+ return null;
12
+ const filePath = match[1].trim();
13
+ if (!filePath)
14
+ return null;
15
+ if (filePath.includes("[[") || filePath.includes("]]"))
16
+ return null;
17
+ return filePath.endsWith(".md") ? filePath : `${filePath}.md`;
18
+ };
19
+ //# sourceMappingURL=link-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-parser.js","sourceRoot":"","sources":["../src/link-parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,IAAY,EAAiB,EAAE;IACtE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACzD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACjC,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE3B,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAEpE,OAAO,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,KAAK,CAAC;AAC/D,CAAC,CAAC","sourcesContent":["/**\n * Handles different link formats and edge cases:\n * [[FileName]] -> FileName.md\n * [[Folder/FileName]] -> Folder/FileName.md\n * [[Folder/FileName|DisplayName]] -> Folder/FileName.md\n * Normalizes paths and handles malformed links gracefully.\n */\nexport const extractFilePathFromLink = (link: string): string | null => {\n\tconst match = link.match(/\\[\\[([^|\\]]+?)(?:\\|.*?)?\\]\\]/);\n\tif (!match) return null;\n\n\tconst filePath = match[1].trim();\n\tif (!filePath) return null;\n\n\tif (filePath.includes(\"[[\") || filePath.includes(\"]]\")) return null;\n\n\treturn filePath.endsWith(\".md\") ? filePath : `${filePath}.md`;\n};\n"]}
@@ -0,0 +1,19 @@
1
+ import type { Plugin } from "obsidian";
2
+ import { BehaviorSubject } from "rxjs";
3
+ import type { z } from "zod";
4
+ export declare class SettingsStore<TSchema extends z.ZodTypeAny> {
5
+ private plugin;
6
+ private schema;
7
+ readonly settings$: BehaviorSubject<z.infer<TSchema>>;
8
+ constructor(plugin: Plugin, schema: TSchema);
9
+ get currentSettings(): z.infer<TSchema>;
10
+ loadSettings(): Promise<void>;
11
+ saveSettings(): Promise<void>;
12
+ updateSettings(updater: (settings: z.infer<TSchema>) => z.infer<TSchema>): Promise<void>;
13
+ resetSettings(): Promise<void>;
14
+ updateProperty<K extends keyof z.infer<TSchema>>(key: K, value: z.infer<TSchema>[K]): Promise<void>;
15
+ updateProperties(updates: Partial<z.infer<TSchema>>): Promise<void>;
16
+ getDefaults(): z.infer<TSchema>;
17
+ hasCustomizations(): boolean;
18
+ }
19
+ //# sourceMappingURL=settings-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-store.d.ts","sourceRoot":"","sources":["../src/settings-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,qBAAa,aAAa,CAAC,OAAO,SAAS,CAAC,CAAC,UAAU;IACtD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAU;IACxB,SAAgB,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;gBAEjD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO;IAM3C,IAAI,eAAe,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAEtC;IAEK,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB7B,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B,cAAc,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAaxF,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAK9B,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,EACpD,GAAG,EAAE,CAAC,EACN,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GACxB,OAAO,CAAC,IAAI,CAAC;IAQV,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzE,WAAW,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;IAI/B,iBAAiB,IAAI,OAAO;CAI5B"}
@@ -0,0 +1,79 @@
1
+ import { __awaiter } from "tslib";
2
+ import { BehaviorSubject } from "rxjs";
3
+ export class SettingsStore {
4
+ constructor(plugin, schema) {
5
+ this.plugin = plugin;
6
+ this.schema = schema;
7
+ this.settings$ = new BehaviorSubject(schema.parse({}));
8
+ }
9
+ get currentSettings() {
10
+ return this.settings$.value;
11
+ }
12
+ loadSettings() {
13
+ return __awaiter(this, void 0, void 0, function* () {
14
+ try {
15
+ const data = yield this.plugin.loadData();
16
+ const sanitized = this.schema.parse(data !== null && data !== void 0 ? data : {});
17
+ this.settings$.next(sanitized);
18
+ // Save back if data was sanitized/normalized
19
+ if (JSON.stringify(sanitized) !== JSON.stringify(data !== null && data !== void 0 ? data : {})) {
20
+ yield this.saveSettings();
21
+ }
22
+ }
23
+ catch (error) {
24
+ console.error("Failed to load settings, using defaults:", error);
25
+ this.settings$.next(this.schema.parse({}));
26
+ yield this.saveSettings();
27
+ }
28
+ });
29
+ }
30
+ saveSettings() {
31
+ return __awaiter(this, void 0, void 0, function* () {
32
+ yield this.plugin.saveData(this.currentSettings);
33
+ });
34
+ }
35
+ updateSettings(updater) {
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ try {
38
+ const newSettings = updater(this.currentSettings);
39
+ const validated = this.schema.parse(newSettings);
40
+ this.settings$.next(validated);
41
+ yield this.saveSettings();
42
+ }
43
+ catch (error) {
44
+ console.error("Failed to update settings:", error);
45
+ throw error;
46
+ }
47
+ });
48
+ }
49
+ resetSettings() {
50
+ return __awaiter(this, void 0, void 0, function* () {
51
+ this.settings$.next(this.schema.parse({}));
52
+ yield this.saveSettings();
53
+ });
54
+ }
55
+ updateProperty(key, value) {
56
+ return __awaiter(this, void 0, void 0, function* () {
57
+ yield this.updateSettings((settings) => {
58
+ const updated = Object.assign({}, settings);
59
+ updated[key] = value;
60
+ return updated;
61
+ });
62
+ });
63
+ }
64
+ updateProperties(updates) {
65
+ return __awaiter(this, void 0, void 0, function* () {
66
+ yield this.updateSettings((settings) => {
67
+ return Object.assign({}, settings, updates);
68
+ });
69
+ });
70
+ }
71
+ getDefaults() {
72
+ return this.schema.parse({});
73
+ }
74
+ hasCustomizations() {
75
+ const defaults = this.getDefaults();
76
+ return JSON.stringify(this.currentSettings) !== JSON.stringify(defaults);
77
+ }
78
+ }
79
+ //# sourceMappingURL=settings-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-store.js","sourceRoot":"","sources":["../src/settings-store.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Plugin } from \"obsidian\";\nimport { BehaviorSubject } from \"rxjs\";\nimport type { z } from \"zod\";\n\nexport class SettingsStore<TSchema extends z.ZodTypeAny> {\n\tprivate plugin: Plugin;\n\tprivate schema: TSchema;\n\tpublic readonly settings$: BehaviorSubject<z.infer<TSchema>>;\n\n\tconstructor(plugin: Plugin, schema: TSchema) {\n\t\tthis.plugin = plugin;\n\t\tthis.schema = schema;\n\t\tthis.settings$ = new BehaviorSubject<z.infer<TSchema>>(schema.parse({}));\n\t}\n\n\tget currentSettings(): z.infer<TSchema> {\n\t\treturn this.settings$.value;\n\t}\n\n\tasync loadSettings(): Promise<void> {\n\t\ttry {\n\t\t\tconst data = await this.plugin.loadData();\n\t\t\tconst sanitized = this.schema.parse(data ?? {});\n\t\t\tthis.settings$.next(sanitized);\n\n\t\t\t// Save back if data was sanitized/normalized\n\t\t\tif (JSON.stringify(sanitized) !== JSON.stringify(data ?? {})) {\n\t\t\t\tawait this.saveSettings();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to load settings, using defaults:\", error);\n\t\t\tthis.settings$.next(this.schema.parse({}));\n\t\t\tawait this.saveSettings();\n\t\t}\n\t}\n\n\tasync saveSettings(): Promise<void> {\n\t\tawait this.plugin.saveData(this.currentSettings);\n\t}\n\n\tasync updateSettings(updater: (settings: z.infer<TSchema>) => z.infer<TSchema>): Promise<void> {\n\t\ttry {\n\t\t\tconst newSettings = updater(this.currentSettings);\n\t\t\tconst validated = this.schema.parse(newSettings);\n\n\t\t\tthis.settings$.next(validated);\n\t\t\tawait this.saveSettings();\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to update settings:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync resetSettings(): Promise<void> {\n\t\tthis.settings$.next(this.schema.parse({}));\n\t\tawait this.saveSettings();\n\t}\n\n\tasync updateProperty<K extends keyof z.infer<TSchema>>(\n\t\tkey: K,\n\t\tvalue: z.infer<TSchema>[K]\n\t): Promise<void> {\n\t\tawait this.updateSettings((settings) => {\n\t\t\tconst updated = Object.assign({}, settings);\n\t\t\t(updated as any)[key] = value;\n\t\t\treturn updated;\n\t\t});\n\t}\n\n\tasync updateProperties(updates: Partial<z.infer<TSchema>>): Promise<void> {\n\t\tawait this.updateSettings((settings) => {\n\t\t\treturn Object.assign({}, settings, updates);\n\t\t});\n\t}\n\n\tgetDefaults(): z.infer<TSchema> {\n\t\treturn this.schema.parse({});\n\t}\n\n\thasCustomizations(): boolean {\n\t\tconst defaults = this.getDefaults();\n\t\treturn JSON.stringify(this.currentSettings) !== JSON.stringify(defaults);\n\t}\n}\n"]}
@@ -0,0 +1,58 @@
1
+ import type { ZodObject, ZodRawShape } from "zod";
2
+ import type { SettingsStore } from "./settings-store";
3
+ interface BaseSettingConfig {
4
+ key: string;
5
+ name: string;
6
+ desc: string;
7
+ }
8
+ interface TextSettingConfig extends BaseSettingConfig {
9
+ placeholder?: string;
10
+ }
11
+ interface SliderSettingConfig extends BaseSettingConfig {
12
+ min?: number;
13
+ max?: number;
14
+ step?: number;
15
+ }
16
+ interface ArraySettingConfig extends BaseSettingConfig {
17
+ placeholder?: string;
18
+ arrayDelimiter?: string;
19
+ multiline?: boolean;
20
+ }
21
+ interface ArrayManagerConfig extends BaseSettingConfig {
22
+ placeholder?: string;
23
+ addButtonText?: string;
24
+ removeButtonText?: string;
25
+ emptyArrayFallback?: unknown;
26
+ preventEmpty?: boolean;
27
+ itemDescriptionFn?: (item: unknown) => string;
28
+ onBeforeAdd?: (newItem: unknown, currentItems: unknown[]) => unknown[] | Promise<unknown[]>;
29
+ onBeforeRemove?: (itemToRemove: unknown, currentItems: unknown[]) => unknown[] | Promise<unknown[]>;
30
+ quickActions?: Array<{
31
+ name: string;
32
+ desc: string;
33
+ buttonText: string;
34
+ condition?: (currentItems: unknown[]) => boolean;
35
+ action: (currentItems: unknown[]) => unknown[] | Promise<unknown[]>;
36
+ }>;
37
+ }
38
+ export declare class SettingsUIBuilder<TSchema extends ZodObject<ZodRawShape>> {
39
+ private settingsStore;
40
+ constructor(settingsStore: SettingsStore<TSchema>);
41
+ private get settings();
42
+ private updateSetting;
43
+ addToggle(containerEl: HTMLElement, config: BaseSettingConfig): void;
44
+ addSlider(containerEl: HTMLElement, config: SliderSettingConfig): void;
45
+ addText(containerEl: HTMLElement, config: TextSettingConfig): void;
46
+ addTextArray(containerEl: HTMLElement, config: ArraySettingConfig): void;
47
+ /**
48
+ * Advanced array manager with add/remove buttons for each item
49
+ * Similar to the directory settings pattern
50
+ */
51
+ addArrayManager(containerEl: HTMLElement, config: ArrayManagerConfig): void;
52
+ /**
53
+ * Automatically detect the type from current value and create appropriate control
54
+ */
55
+ auto(containerEl: HTMLElement, config: TextSettingConfig & SliderSettingConfig & ArraySettingConfig): void;
56
+ }
57
+ export {};
58
+ //# sourceMappingURL=settings-ui-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-ui-builder.d.ts","sourceRoot":"","sources":["../src/settings-ui-builder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAK,MAAM,KAAK,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,UAAU,iBAAiB;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACb;AAED,UAAU,iBAAkB,SAAQ,iBAAiB;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,mBAAoB,SAAQ,iBAAiB;IACtD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,UAAU,kBAAmB,SAAQ,iBAAiB;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,UAAU,kBAAmB,SAAQ,iBAAiB;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM,CAAC;IAC9C,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAC5F,cAAc,CAAC,EAAE,CAChB,YAAY,EAAE,OAAO,EACrB,YAAY,EAAE,OAAO,EAAE,KACnB,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACpC,YAAY,CAAC,EAAE,KAAK,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,CAAC,YAAY,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;QACjD,MAAM,EAAE,CAAC,YAAY,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;KACpE,CAAC,CAAC;CACH;AAED,qBAAa,iBAAiB,CAAC,OAAO,SAAS,SAAS,CAAC,WAAW,CAAC;IACxD,OAAO,CAAC,aAAa;gBAAb,aAAa,EAAE,aAAa,CAAC,OAAO,CAAC;IAEzD,OAAO,KAAK,QAAQ,GAEnB;YAEa,aAAa;IAU3B,SAAS,CAAC,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAcpE,SAAS,CAAC,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,mBAAmB,GAAG,IAAI;IAoBtE,OAAO,CAAC,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAiBlE,YAAY,CAAC,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI;IA6CxE;;;OAGG;IACH,eAAe,CAAC,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAkI3E;;OAEG;IACH,IAAI,CACH,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,iBAAiB,GAAG,mBAAmB,GAAG,kBAAkB,GAClE,IAAI;CAgBP"}
@@ -0,0 +1,218 @@
1
+ import { __awaiter } from "tslib";
2
+ import { Setting } from "obsidian";
3
+ export class SettingsUIBuilder {
4
+ constructor(settingsStore) {
5
+ this.settingsStore = settingsStore;
6
+ }
7
+ get settings() {
8
+ return this.settingsStore.currentSettings;
9
+ }
10
+ updateSetting(key, value) {
11
+ return __awaiter(this, void 0, void 0, function* () {
12
+ yield this.settingsStore.updateSettings((s) => (Object.assign(Object.assign({}, s), { [key]: value })));
13
+ });
14
+ }
15
+ addToggle(containerEl, config) {
16
+ const { key, name, desc } = config;
17
+ const value = this.settings[key];
18
+ new Setting(containerEl)
19
+ .setName(name)
20
+ .setDesc(desc)
21
+ .addToggle((toggle) => toggle.setValue(Boolean(value)).onChange((newValue) => __awaiter(this, void 0, void 0, function* () {
22
+ yield this.updateSetting(key, newValue);
23
+ })));
24
+ }
25
+ addSlider(containerEl, config) {
26
+ const { key, name, desc, min = 0, max = 100, step = 1 } = config;
27
+ const value = this.settings[key];
28
+ new Setting(containerEl)
29
+ .setName(name)
30
+ .setDesc(desc)
31
+ .addSlider((slider) => {
32
+ slider
33
+ .setLimits(min, max, step)
34
+ .setValue(Number(value))
35
+ .setDynamicTooltip()
36
+ .onChange((newValue) => __awaiter(this, void 0, void 0, function* () {
37
+ yield this.updateSetting(key, newValue);
38
+ }));
39
+ return slider;
40
+ });
41
+ }
42
+ addText(containerEl, config) {
43
+ const { key, name, desc, placeholder = "" } = config;
44
+ const value = this.settings[key];
45
+ new Setting(containerEl)
46
+ .setName(name)
47
+ .setDesc(desc)
48
+ .addText((text) => text
49
+ .setPlaceholder(placeholder)
50
+ .setValue(String(value !== null && value !== void 0 ? value : ""))
51
+ .onChange((newValue) => __awaiter(this, void 0, void 0, function* () {
52
+ yield this.updateSetting(key, newValue);
53
+ })));
54
+ }
55
+ addTextArray(containerEl, config) {
56
+ const { key, name, desc, placeholder = "", arrayDelimiter = ", ", multiline = false } = config;
57
+ const value = this.settings[key];
58
+ const setting = new Setting(containerEl).setName(name).setDesc(desc);
59
+ if (multiline) {
60
+ setting.addTextArea((text) => {
61
+ text.setPlaceholder(placeholder);
62
+ text.setValue(Array.isArray(value) ? value.join("\n") : "");
63
+ const commit = (inputValue) => __awaiter(this, void 0, void 0, function* () {
64
+ const items = inputValue
65
+ .split("\n")
66
+ .map((s) => s.trim())
67
+ .filter((s) => s.length > 0);
68
+ yield this.updateSetting(key, items);
69
+ });
70
+ text.inputEl.addEventListener("blur", () => void commit(text.inputEl.value));
71
+ text.inputEl.addEventListener("keydown", (e) => {
72
+ if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
73
+ e.preventDefault();
74
+ void commit(text.inputEl.value);
75
+ }
76
+ });
77
+ text.inputEl.rows = 5;
78
+ text.inputEl.classList.add("settings-ui-builder-textarea");
79
+ });
80
+ }
81
+ else {
82
+ setting.addText((text) => {
83
+ text.setPlaceholder(placeholder);
84
+ text.setValue(Array.isArray(value) ? value.join(arrayDelimiter) : "");
85
+ text.onChange((inputValue) => __awaiter(this, void 0, void 0, function* () {
86
+ const items = inputValue
87
+ .split(",")
88
+ .map((s) => s.trim())
89
+ .filter((s) => s.length > 0);
90
+ yield this.updateSetting(key, items);
91
+ }));
92
+ });
93
+ }
94
+ }
95
+ /**
96
+ * Advanced array manager with add/remove buttons for each item
97
+ * Similar to the directory settings pattern
98
+ */
99
+ addArrayManager(containerEl, config) {
100
+ var _a;
101
+ const { key, name, desc, placeholder = "", addButtonText = "Add", removeButtonText = "Remove", emptyArrayFallback = [], preventEmpty = false, itemDescriptionFn, onBeforeAdd, onBeforeRemove, quickActions = [], } = config;
102
+ // Section heading
103
+ new Setting(containerEl).setName(name).setHeading();
104
+ // Description
105
+ if (desc) {
106
+ const descEl = containerEl.createDiv("setting-item-description");
107
+ descEl.setText(desc);
108
+ }
109
+ // Container for list items
110
+ const listContainer = containerEl.createDiv("settings-array-manager-list");
111
+ const render = () => {
112
+ var _a;
113
+ listContainer.empty();
114
+ const currentItems = (_a = this.settings[key]) !== null && _a !== void 0 ? _a : [];
115
+ for (const item of currentItems) {
116
+ const itemSetting = new Setting(listContainer).setName(String(item)).addButton((button) => button
117
+ .setButtonText(removeButtonText)
118
+ .setWarning()
119
+ .onClick(() => __awaiter(this, void 0, void 0, function* () {
120
+ let newItems = currentItems.filter((i) => i !== item);
121
+ // Apply custom logic before removal
122
+ if (onBeforeRemove) {
123
+ newItems = yield onBeforeRemove(item, currentItems);
124
+ }
125
+ // Prevent empty array if configured
126
+ if (preventEmpty && newItems.length === 0) {
127
+ newItems = Array.isArray(emptyArrayFallback)
128
+ ? emptyArrayFallback
129
+ : [emptyArrayFallback];
130
+ }
131
+ yield this.updateSetting(key, newItems);
132
+ render();
133
+ })));
134
+ // Add custom description for each item if provided
135
+ if (itemDescriptionFn) {
136
+ itemSetting.setDesc(itemDescriptionFn(item));
137
+ }
138
+ }
139
+ };
140
+ render();
141
+ // Add new item section
142
+ const inputId = `settings-array-manager-input-${key}`;
143
+ new Setting(containerEl)
144
+ .setName(`Add ${name.toLowerCase()}`)
145
+ .setDesc(`Enter a new value`)
146
+ .addText((text) => {
147
+ text.setPlaceholder(placeholder);
148
+ text.inputEl.id = inputId;
149
+ })
150
+ .addButton((button) => button
151
+ .setButtonText(addButtonText)
152
+ .setCta()
153
+ .onClick(() => __awaiter(this, void 0, void 0, function* () {
154
+ var _a;
155
+ const input = containerEl.querySelector(`#${inputId}`);
156
+ const newItem = input.value.trim();
157
+ if (!newItem) {
158
+ return;
159
+ }
160
+ const currentItems = (_a = this.settings[key]) !== null && _a !== void 0 ? _a : [];
161
+ let newItems = [...currentItems];
162
+ // Apply custom logic before adding
163
+ if (onBeforeAdd) {
164
+ newItems = yield onBeforeAdd(newItem, currentItems);
165
+ }
166
+ else {
167
+ // Default behavior: add if not exists
168
+ if (!newItems.includes(newItem)) {
169
+ newItems.push(newItem);
170
+ }
171
+ }
172
+ yield this.updateSetting(key, newItems);
173
+ input.value = "";
174
+ render();
175
+ })));
176
+ // Quick actions
177
+ for (const quickAction of quickActions) {
178
+ const currentItems = (_a = this.settings[key]) !== null && _a !== void 0 ? _a : [];
179
+ // Check condition if provided
180
+ if (quickAction.condition && !quickAction.condition(currentItems)) {
181
+ continue;
182
+ }
183
+ new Setting(containerEl)
184
+ .setName(quickAction.name)
185
+ .setDesc(quickAction.desc)
186
+ .addButton((button) => button.setButtonText(quickAction.buttonText).onClick(() => __awaiter(this, void 0, void 0, function* () {
187
+ var _a;
188
+ const currentItems = (_a = this.settings[key]) !== null && _a !== void 0 ? _a : [];
189
+ const newItems = yield quickAction.action(currentItems);
190
+ yield this.updateSetting(key, newItems);
191
+ render();
192
+ })));
193
+ }
194
+ }
195
+ /**
196
+ * Automatically detect the type from current value and create appropriate control
197
+ */
198
+ auto(containerEl, config) {
199
+ const value = this.settings[config.key];
200
+ // Detect type from current value
201
+ if (typeof value === "boolean") {
202
+ this.addToggle(containerEl, config);
203
+ }
204
+ else if (typeof value === "number") {
205
+ this.addSlider(containerEl, config);
206
+ }
207
+ else if (typeof value === "string") {
208
+ this.addText(containerEl, config);
209
+ }
210
+ else if (Array.isArray(value)) {
211
+ this.addTextArray(containerEl, config);
212
+ }
213
+ else {
214
+ console.warn(`Unsupported value type for key ${config.key}: ${typeof value}`);
215
+ }
216
+ }
217
+ }
218
+ //# sourceMappingURL=settings-ui-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-ui-builder.js","sourceRoot":"","sources":["../src/settings-ui-builder.ts"],"names":[],"mappings":"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","sourcesContent":["import { Setting } from \"obsidian\";\nimport type { ZodObject, ZodRawShape, z } from \"zod\";\nimport type { SettingsStore } from \"./settings-store\";\n\ninterface BaseSettingConfig {\n\tkey: string;\n\tname: string;\n\tdesc: string;\n}\n\ninterface TextSettingConfig extends BaseSettingConfig {\n\tplaceholder?: string;\n}\n\ninterface SliderSettingConfig extends BaseSettingConfig {\n\tmin?: number;\n\tmax?: number;\n\tstep?: number;\n}\n\ninterface ArraySettingConfig extends BaseSettingConfig {\n\tplaceholder?: string;\n\tarrayDelimiter?: string;\n\tmultiline?: boolean;\n}\n\ninterface ArrayManagerConfig extends BaseSettingConfig {\n\tplaceholder?: string;\n\taddButtonText?: string;\n\tremoveButtonText?: string;\n\temptyArrayFallback?: unknown;\n\tpreventEmpty?: boolean;\n\titemDescriptionFn?: (item: unknown) => string;\n\tonBeforeAdd?: (newItem: unknown, currentItems: unknown[]) => unknown[] | Promise<unknown[]>;\n\tonBeforeRemove?: (\n\t\titemToRemove: unknown,\n\t\tcurrentItems: unknown[]\n\t) => unknown[] | Promise<unknown[]>;\n\tquickActions?: Array<{\n\t\tname: string;\n\t\tdesc: string;\n\t\tbuttonText: string;\n\t\tcondition?: (currentItems: unknown[]) => boolean;\n\t\taction: (currentItems: unknown[]) => unknown[] | Promise<unknown[]>;\n\t}>;\n}\n\nexport class SettingsUIBuilder<TSchema extends ZodObject<ZodRawShape>> {\n\tconstructor(private settingsStore: SettingsStore<TSchema>) {}\n\n\tprivate get settings(): z.infer<TSchema> {\n\t\treturn this.settingsStore.currentSettings;\n\t}\n\n\tprivate async updateSetting(key: keyof z.infer<TSchema>, value: unknown): Promise<void> {\n\t\tawait this.settingsStore.updateSettings(\n\t\t\t(s) =>\n\t\t\t\t({\n\t\t\t\t\t...s,\n\t\t\t\t\t[key]: value,\n\t\t\t\t}) as z.infer<TSchema>\n\t\t);\n\t}\n\n\taddToggle(containerEl: HTMLElement, config: BaseSettingConfig): void {\n\t\tconst { key, name, desc } = config;\n\t\tconst value = this.settings[key as keyof z.infer<TSchema>];\n\n\t\tnew Setting(containerEl)\n\t\t\t.setName(name)\n\t\t\t.setDesc(desc)\n\t\t\t.addToggle((toggle) =>\n\t\t\t\ttoggle.setValue(Boolean(value)).onChange(async (newValue) => {\n\t\t\t\t\tawait this.updateSetting(key as keyof z.infer<TSchema>, newValue);\n\t\t\t\t})\n\t\t\t);\n\t}\n\n\taddSlider(containerEl: HTMLElement, config: SliderSettingConfig): void {\n\t\tconst { key, name, desc, min = 0, max = 100, step = 1 } = config;\n\t\tconst value = this.settings[key as keyof z.infer<TSchema>];\n\n\t\tnew Setting(containerEl)\n\t\t\t.setName(name)\n\t\t\t.setDesc(desc)\n\t\t\t.addSlider((slider) => {\n\t\t\t\tslider\n\t\t\t\t\t.setLimits(min, max, step)\n\t\t\t\t\t.setValue(Number(value))\n\t\t\t\t\t.setDynamicTooltip()\n\t\t\t\t\t.onChange(async (newValue) => {\n\t\t\t\t\t\tawait this.updateSetting(key as keyof z.infer<TSchema>, newValue);\n\t\t\t\t\t});\n\n\t\t\t\treturn slider;\n\t\t\t});\n\t}\n\n\taddText(containerEl: HTMLElement, config: TextSettingConfig): void {\n\t\tconst { key, name, desc, placeholder = \"\" } = config;\n\t\tconst value = this.settings[key as keyof z.infer<TSchema>];\n\n\t\tnew Setting(containerEl)\n\t\t\t.setName(name)\n\t\t\t.setDesc(desc)\n\t\t\t.addText((text) =>\n\t\t\t\ttext\n\t\t\t\t\t.setPlaceholder(placeholder)\n\t\t\t\t\t.setValue(String(value ?? \"\"))\n\t\t\t\t\t.onChange(async (newValue) => {\n\t\t\t\t\t\tawait this.updateSetting(key as keyof z.infer<TSchema>, newValue);\n\t\t\t\t\t})\n\t\t\t);\n\t}\n\n\taddTextArray(containerEl: HTMLElement, config: ArraySettingConfig): void {\n\t\tconst { key, name, desc, placeholder = \"\", arrayDelimiter = \", \", multiline = false } = config;\n\t\tconst value = this.settings[key as keyof z.infer<TSchema>] as string[];\n\n\t\tconst setting = new Setting(containerEl).setName(name).setDesc(desc);\n\n\t\tif (multiline) {\n\t\t\tsetting.addTextArea((text) => {\n\t\t\t\ttext.setPlaceholder(placeholder);\n\t\t\t\ttext.setValue(Array.isArray(value) ? value.join(\"\\n\") : \"\");\n\n\t\t\t\tconst commit = async (inputValue: string) => {\n\t\t\t\t\tconst items = inputValue\n\t\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t\t.map((s) => s.trim())\n\t\t\t\t\t\t.filter((s) => s.length > 0);\n\t\t\t\t\tawait this.updateSetting(key as keyof z.infer<TSchema>, items);\n\t\t\t\t};\n\n\t\t\t\ttext.inputEl.addEventListener(\"blur\", () => void commit(text.inputEl.value));\n\t\t\t\ttext.inputEl.addEventListener(\"keydown\", (e: KeyboardEvent) => {\n\t\t\t\t\tif (e.key === \"Enter\" && (e.ctrlKey || e.metaKey)) {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tvoid commit(text.inputEl.value);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\ttext.inputEl.rows = 5;\n\t\t\t\ttext.inputEl.classList.add(\"settings-ui-builder-textarea\");\n\t\t\t});\n\t\t} else {\n\t\t\tsetting.addText((text) => {\n\t\t\t\ttext.setPlaceholder(placeholder);\n\t\t\t\ttext.setValue(Array.isArray(value) ? value.join(arrayDelimiter) : \"\");\n\t\t\t\ttext.onChange(async (inputValue) => {\n\t\t\t\t\tconst items = inputValue\n\t\t\t\t\t\t.split(\",\")\n\t\t\t\t\t\t.map((s) => s.trim())\n\t\t\t\t\t\t.filter((s) => s.length > 0);\n\t\t\t\t\tawait this.updateSetting(key as keyof z.infer<TSchema>, items);\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Advanced array manager with add/remove buttons for each item\n\t * Similar to the directory settings pattern\n\t */\n\taddArrayManager(containerEl: HTMLElement, config: ArrayManagerConfig): void {\n\t\tconst {\n\t\t\tkey,\n\t\t\tname,\n\t\t\tdesc,\n\t\t\tplaceholder = \"\",\n\t\t\taddButtonText = \"Add\",\n\t\t\tremoveButtonText = \"Remove\",\n\t\t\temptyArrayFallback = [],\n\t\t\tpreventEmpty = false,\n\t\t\titemDescriptionFn,\n\t\t\tonBeforeAdd,\n\t\t\tonBeforeRemove,\n\t\t\tquickActions = [],\n\t\t} = config;\n\n\t\t// Section heading\n\t\tnew Setting(containerEl).setName(name).setHeading();\n\n\t\t// Description\n\t\tif (desc) {\n\t\t\tconst descEl = containerEl.createDiv(\"setting-item-description\");\n\t\t\tdescEl.setText(desc);\n\t\t}\n\n\t\t// Container for list items\n\t\tconst listContainer = containerEl.createDiv(\"settings-array-manager-list\");\n\n\t\tconst render = () => {\n\t\t\tlistContainer.empty();\n\n\t\t\tconst currentItems = (this.settings[key as keyof z.infer<TSchema>] as unknown[]) ?? [];\n\n\t\t\tfor (const item of currentItems) {\n\t\t\t\tconst itemSetting = new Setting(listContainer).setName(String(item)).addButton((button) =>\n\t\t\t\t\tbutton\n\t\t\t\t\t\t.setButtonText(removeButtonText)\n\t\t\t\t\t\t.setWarning()\n\t\t\t\t\t\t.onClick(async () => {\n\t\t\t\t\t\t\tlet newItems = currentItems.filter((i) => i !== item);\n\n\t\t\t\t\t\t\t// Apply custom logic before removal\n\t\t\t\t\t\t\tif (onBeforeRemove) {\n\t\t\t\t\t\t\t\tnewItems = await onBeforeRemove(item, currentItems);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Prevent empty array if configured\n\t\t\t\t\t\t\tif (preventEmpty && newItems.length === 0) {\n\t\t\t\t\t\t\t\tnewItems = Array.isArray(emptyArrayFallback)\n\t\t\t\t\t\t\t\t\t? emptyArrayFallback\n\t\t\t\t\t\t\t\t\t: [emptyArrayFallback];\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tawait this.updateSetting(key as keyof z.infer<TSchema>, newItems);\n\t\t\t\t\t\t\trender();\n\t\t\t\t\t\t})\n\t\t\t\t);\n\n\t\t\t\t// Add custom description for each item if provided\n\t\t\t\tif (itemDescriptionFn) {\n\t\t\t\t\titemSetting.setDesc(itemDescriptionFn(item));\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\trender();\n\n\t\t// Add new item section\n\t\tconst inputId = `settings-array-manager-input-${key}`;\n\t\tnew Setting(containerEl)\n\t\t\t.setName(`Add ${name.toLowerCase()}`)\n\t\t\t.setDesc(`Enter a new value`)\n\t\t\t.addText((text) => {\n\t\t\t\ttext.setPlaceholder(placeholder);\n\t\t\t\ttext.inputEl.id = inputId;\n\t\t\t})\n\t\t\t.addButton((button) =>\n\t\t\t\tbutton\n\t\t\t\t\t.setButtonText(addButtonText)\n\t\t\t\t\t.setCta()\n\t\t\t\t\t.onClick(async () => {\n\t\t\t\t\t\tconst input = containerEl.querySelector(`#${inputId}`) as HTMLInputElement;\n\t\t\t\t\t\tconst newItem = input.value.trim();\n\n\t\t\t\t\t\tif (!newItem) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst currentItems = (this.settings[key as keyof z.infer<TSchema>] as unknown[]) ?? [];\n\t\t\t\t\t\tlet newItems = [...currentItems];\n\n\t\t\t\t\t\t// Apply custom logic before adding\n\t\t\t\t\t\tif (onBeforeAdd) {\n\t\t\t\t\t\t\tnewItems = await onBeforeAdd(newItem, currentItems);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Default behavior: add if not exists\n\t\t\t\t\t\t\tif (!newItems.includes(newItem)) {\n\t\t\t\t\t\t\t\tnewItems.push(newItem);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tawait this.updateSetting(key as keyof z.infer<TSchema>, newItems);\n\t\t\t\t\t\tinput.value = \"\";\n\t\t\t\t\t\trender();\n\t\t\t\t\t})\n\t\t\t);\n\n\t\t// Quick actions\n\t\tfor (const quickAction of quickActions) {\n\t\t\tconst currentItems = (this.settings[key as keyof z.infer<TSchema>] as unknown[]) ?? [];\n\n\t\t\t// Check condition if provided\n\t\t\tif (quickAction.condition && !quickAction.condition(currentItems)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tnew Setting(containerEl)\n\t\t\t\t.setName(quickAction.name)\n\t\t\t\t.setDesc(quickAction.desc)\n\t\t\t\t.addButton((button) =>\n\t\t\t\t\tbutton.setButtonText(quickAction.buttonText).onClick(async () => {\n\t\t\t\t\t\tconst currentItems = (this.settings[key as keyof z.infer<TSchema>] as unknown[]) ?? [];\n\t\t\t\t\t\tconst newItems = await quickAction.action(currentItems);\n\t\t\t\t\t\tawait this.updateSetting(key as keyof z.infer<TSchema>, newItems);\n\t\t\t\t\t\trender();\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Automatically detect the type from current value and create appropriate control\n\t */\n\tauto(\n\t\tcontainerEl: HTMLElement,\n\t\tconfig: TextSettingConfig & SliderSettingConfig & ArraySettingConfig\n\t): void {\n\t\tconst value = this.settings[config.key as keyof z.infer<TSchema>];\n\n\t\t// Detect type from current value\n\t\tif (typeof value === \"boolean\") {\n\t\t\tthis.addToggle(containerEl, config);\n\t\t} else if (typeof value === \"number\") {\n\t\t\tthis.addSlider(containerEl, config);\n\t\t} else if (typeof value === \"string\") {\n\t\t\tthis.addText(containerEl, config);\n\t\t} else if (Array.isArray(value)) {\n\t\t\tthis.addTextArray(containerEl, config);\n\t\t} else {\n\t\t\tconsole.warn(`Unsupported value type for key ${config.key}: ${typeof value}`);\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,5 @@
1
+ export declare const capitalize: (str: string) => string;
2
+ export declare const generateDuplicatedTitle: (originalTitle: string) => string;
3
+ export declare const pluralize: (count: number) => string;
4
+ export declare const getWeekDirection: (weeks: number) => "next" | "previous";
5
+ //# sourceMappingURL=string-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"string-utils.d.ts","sourceRoot":"","sources":["../src/string-utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,KAAG,MAGxC,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,eAAe,MAAM,KAAG,MAY/D,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,OAAO,MAAM,KAAG,MAEzC,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,MAAM,GAAG,UAEzD,CAAC"}
@@ -0,0 +1,25 @@
1
+ export const capitalize = (str) => {
2
+ if (!str)
3
+ return str;
4
+ return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
5
+ };
6
+ export const generateDuplicatedTitle = (originalTitle) => {
7
+ // Check if title already has a counter pattern like " (2)", " - Copy", etc.
8
+ const counterMatch = originalTitle.match(/^(.*?)(?:\s*\((\d+)\)|\s*-?\s*Copy(?:\s*(\d+))?)$/);
9
+ if (counterMatch) {
10
+ const baseName = counterMatch[1];
11
+ const existingCounter = counterMatch[2] || counterMatch[3];
12
+ const nextCounter = existingCounter ? parseInt(existingCounter, 10) + 1 : 2;
13
+ return `${baseName} (${nextCounter})`;
14
+ }
15
+ else {
16
+ return `${originalTitle} (2)`;
17
+ }
18
+ };
19
+ export const pluralize = (count) => {
20
+ return count === 1 ? "" : "s";
21
+ };
22
+ export const getWeekDirection = (weeks) => {
23
+ return weeks > 0 ? "next" : "previous";
24
+ };
25
+ //# sourceMappingURL=string-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"string-utils.js","sourceRoot":"","sources":["../src/string-utils.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAW,EAAU,EAAE;IACjD,IAAI,CAAC,GAAG;QAAE,OAAO,GAAG,CAAC;IACrB,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;AACjE,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,aAAqB,EAAU,EAAE;IACxE,4EAA4E;IAC5E,MAAM,YAAY,GAAG,aAAa,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAC;IAE9F,IAAI,YAAY,EAAE,CAAC;QAClB,MAAM,QAAQ,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,eAAe,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC;QAC3D,MAAM,WAAW,GAAG,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,eAAe,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5E,OAAO,GAAG,QAAQ,KAAK,WAAW,GAAG,CAAC;IACvC,CAAC;SAAM,CAAC;QACP,OAAO,GAAG,aAAa,MAAM,CAAC;IAC/B,CAAC;AACF,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAa,EAAU,EAAE;IAClD,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;AAC/B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAa,EAAuB,EAAE;IACtE,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC;AACxC,CAAC,CAAC","sourcesContent":["export const capitalize = (str: string): string => {\n\tif (!str) return str;\n\treturn str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();\n};\n\nexport const generateDuplicatedTitle = (originalTitle: string): string => {\n\t// Check if title already has a counter pattern like \" (2)\", \" - Copy\", etc.\n\tconst counterMatch = originalTitle.match(/^(.*?)(?:\\s*\\((\\d+)\\)|\\s*-?\\s*Copy(?:\\s*(\\d+))?)$/);\n\n\tif (counterMatch) {\n\t\tconst baseName = counterMatch[1];\n\t\tconst existingCounter = counterMatch[2] || counterMatch[3];\n\t\tconst nextCounter = existingCounter ? parseInt(existingCounter, 10) + 1 : 2;\n\t\treturn `${baseName} (${nextCounter})`;\n\t} else {\n\t\treturn `${originalTitle} (2)`;\n\t}\n};\n\nexport const pluralize = (count: number): string => {\n\treturn count === 1 ? \"\" : \"s\";\n};\n\nexport const getWeekDirection = (weeks: number): \"next\" | \"previous\" => {\n\treturn weeks > 0 ? \"next\" : \"previous\";\n};\n"]}
@@ -0,0 +1,4 @@
1
+ import { type App, type TFile } from "obsidian";
2
+ export declare function isTemplaterAvailable(app: App): boolean;
3
+ export declare function createFromTemplate(app: App, templatePath: string, targetFolder?: string, filename?: string, openNewNote?: boolean): Promise<TFile | null>;
4
+ //# sourceMappingURL=templater-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templater-utils.d.ts","sourceRoot":"","sources":["../src/templater-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAyB,KAAK,KAAK,EAAE,MAAM,UAAU,CAAC;AAgCvE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAGtD;AAED,wBAAsB,kBAAkB,CACvC,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,MAAM,EACrB,QAAQ,CAAC,EAAE,MAAM,EACjB,WAAW,UAAQ,GACjB,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CA+BvB"}