Files
StudyDiary/.obsidian/plugins/sync-contacts-macos/main.js

1014 lines
35 KiB
JavaScript
Raw Normal View History

2025-10-25 20:35:10 +08:00
/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// node_modules/foldline/foldline.js
var require_foldline = __commonJS({
"node_modules/foldline/foldline.js"(exports, module2) {
var CRLF = "\r\n";
var SP = " ";
var MAX_LINE_LENGTH = 998;
var DEFAULT_LINE_LENGTH = 78;
var MIN_LINE_LENGTH = 2;
function foldLine(input, maxLength, hardWrap) {
if (maxLength != null && maxLength < MIN_LINE_LENGTH) {
throw new Error("Maximum length must not be less than " + MIN_LINE_LENGTH);
}
if (maxLength != null && maxLength > MAX_LINE_LENGTH) {
throw new Error("Maximum length must not exceed " + MAX_LINE_LENGTH);
}
maxLength = maxLength || DEFAULT_LINE_LENGTH;
input = input.replace(/[\r\n]+/g, "");
if (input.length <= maxLength) {
return input;
}
var output = "";
var index = 0;
var nextIndex = 0;
var length = input.length;
var line = 0;
var trim = 0;
while (index < length) {
if (!hardWrap && ~(nextIndex = input.lastIndexOf(SP, index + maxLength))) {
if (nextIndex > index) {
output += input.slice(index, nextIndex) + CRLF + SP;
index = nextIndex;
} else {
output += input.slice(index, index + maxLength - trim) + CRLF + SP;
index = index + maxLength - trim;
hardWrap = true;
}
} else {
output += input.slice(index, index + maxLength - trim) + CRLF + SP;
index = index + maxLength - trim;
}
if (length - index < maxLength) {
output += input.slice(index);
break;
}
if (line === 0) {
trim = 1;
}
line++;
}
return output;
}
foldLine.unfold = function unfold(input) {
return input.replace(/\r\n\s/gm, "");
};
module2.exports = foldLine;
}
});
// node_modules/camelcase/index.js
var require_camelcase = __commonJS({
"node_modules/camelcase/index.js"(exports, module2) {
"use strict";
var preserveCamelCase = (string) => {
let isLastCharLower = false;
let isLastCharUpper = false;
let isLastLastCharUpper = false;
for (let i = 0; i < string.length; i++) {
const character = string[i];
if (isLastCharLower && /[a-zA-Z]/.test(character) && character.toUpperCase() === character) {
string = string.slice(0, i) + "-" + string.slice(i);
isLastCharLower = false;
isLastLastCharUpper = isLastCharUpper;
isLastCharUpper = true;
i++;
} else if (isLastCharUpper && isLastLastCharUpper && /[a-zA-Z]/.test(character) && character.toLowerCase() === character) {
string = string.slice(0, i - 1) + "-" + string.slice(i - 1);
isLastLastCharUpper = isLastCharUpper;
isLastCharUpper = false;
isLastCharLower = true;
} else {
isLastCharLower = character.toLowerCase() === character && character.toUpperCase() !== character;
isLastLastCharUpper = isLastCharUpper;
isLastCharUpper = character.toUpperCase() === character && character.toLowerCase() !== character;
}
}
return string;
};
var camelCase = (input, options) => {
if (!(typeof input === "string" || Array.isArray(input))) {
throw new TypeError("Expected the input to be `string | string[]`");
}
options = Object.assign({
pascalCase: false
}, options);
const postProcess = (x) => options.pascalCase ? x.charAt(0).toUpperCase() + x.slice(1) : x;
if (Array.isArray(input)) {
input = input.map((x) => x.trim()).filter((x) => x.length).join("-");
} else {
input = input.trim();
}
if (input.length === 0) {
return "";
}
if (input.length === 1) {
return options.pascalCase ? input.toUpperCase() : input.toLowerCase();
}
const hasUpperCase = input !== input.toLowerCase();
if (hasUpperCase) {
input = preserveCamelCase(input);
}
input = input.replace(/^[_.\- ]+/, "").toLowerCase().replace(/[_.\- ]+(\w|$)/g, (_, p1) => p1.toUpperCase()).replace(/\d+(\w|$)/g, (m) => m.toUpperCase());
return postProcess(input);
};
module2.exports = camelCase;
module2.exports.default = camelCase;
}
});
// node_modules/vcf/lib/property.js
var require_property = __commonJS({
"node_modules/vcf/lib/property.js"(exports, module2) {
function Property(field, value, params) {
if (!(this instanceof Property))
return new Property(value);
if (params != null)
Object.assign(this, params);
this._field = field;
this._data = value;
Object.defineProperty(this, "_field", { enumerable: false });
Object.defineProperty(this, "_data", { enumerable: false });
}
Property.fromJSON = function(data) {
var field = data[0];
var params = data[1];
if (!/text/i.test(data[2]))
params.value = data[2];
var value = Array.isArray(data[3]) ? data[3].join(";") : data[3];
return new Property(field, value, params);
};
function capitalDashCase(value) {
return value.replace(/([A-Z])/g, "-$1").toUpperCase();
}
Property.prototype = {
constructor: Property,
/**
* Check whether the property is of a given type
* @param {String} type
* @return {Boolean}
*/
is: function(type) {
type = (type + "").toLowerCase();
return Array.isArray(this.type) ? this.type.indexOf(type) >= 0 : this.type === type;
},
/**
* Check whether the property is empty
* @return {Boolean}
*/
isEmpty: function() {
return this._data == null && Object.keys(this).length === 0;
},
/**
* Clone the property
* @return {Property}
*/
clone: function() {
return new Property(this._field, this._data, this);
},
/**
* Format the property as vcf with given version
* @param {String} version
* @return {String}
*/
toString: function(version) {
var propName = (this.group ? this.group + "." : "") + capitalDashCase(this._field);
var keys = Object.keys(this);
var params = [];
for (var i = 0; i < keys.length; i++) {
if (keys[i] === "group")
continue;
switch (propName) {
case "TEL":
case "ADR":
case "EMAIL":
if (version === "2.1") {
if (Array.isArray(this[keys[i]]))
params.push(this[keys[i]].join(";"));
else
params.push(this[keys[i]]);
} else
params.push(capitalDashCase(keys[i]) + "=" + this[keys[i]]);
break;
default:
params.push(capitalDashCase(keys[i]) + "=" + this[keys[i]]);
}
}
if (version === "2.1" || version === "3.0")
return propName + (params.length ? ";" + params.join(";").toUpperCase() : params.toString().toUpperCase()) + ":" + (Array.isArray(this._data) ? this._data.join(";") : this._data);
else
return propName + (params.length ? ";" + params.join(";") : params) + ":" + (Array.isArray(this._data) ? this._data.join(";") : this._data);
},
/**
* Get the property's value
* @return {String}
*/
valueOf: function() {
return this._data;
},
/**
* Format the property as jCard data
* @return {Array}
*/
toJSON: function() {
var params = Object.assign({}, this);
if (params.value === "text") {
params.value = void 0;
delete params.value;
}
var data = [this._field, params, this.value || "text"];
switch (this._field) {
default:
data.push(this._data);
break;
case "adr":
case "n":
data.push(this._data.split(";"));
}
return data;
}
};
module2.exports = Property;
}
});
// node_modules/vcf/lib/parse-lines.js
var require_parse_lines = __commonJS({
"node_modules/vcf/lib/parse-lines.js"(exports, module2) {
var camelCase = require_camelcase();
var Property = require_property();
function set(object, key, value) {
if (Array.isArray(object[key])) {
object[key].push(value);
} else if (object[key] != null) {
object[key] = [object[key], value];
} else {
object[key] = value;
}
}
function createParams(params, param) {
var parts = param.split("=");
var k = camelCase(parts[0]);
var value = parts[1];
if (value == null || value === "") {
value = parts[0];
k = "type";
}
if (k === "type") {
if (value[0] === '"' && value[value.length - 1] === '"' && value.indexOf(",") !== -1)
value = value.slice(1, -1);
value.toLowerCase().split(",").forEach(function(value2) {
set(params, k, value2);
});
return params;
}
set(params, k, value);
return params;
}
function parseLines(lines) {
var data = {};
var line = null;
var pattern = /^([^;:]+)((?:;(?:[^;:]+))*)(?:\:([\s\S]+))?$/i;
var len = lines.length - 1;
for (var i = 1; i < len; i++) {
line = lines[i];
var match = pattern.exec(line);
if (!match)
continue;
var name = match[1].split(".");
var property = name.pop();
var group = name.pop();
var value = match[3];
var params = match[2] ? match[2].replace(/^;|;$/g, "").split(";") : [];
var propParams = params.reduce(createParams, group ? { group } : {});
var propName = camelCase(property);
var propVal = new Property(propName, value, propParams);
set(data, propName, propVal);
}
return data;
}
module2.exports = parseLines;
}
});
// node_modules/vcf/lib/vcard.js
var require_vcard = __commonJS({
"node_modules/vcf/lib/vcard.js"(exports, module2) {
function vCard() {
if (!(this instanceof vCard))
return new vCard();
this.version = vCard.versions[vCard.versions.length - 1];
this.data = {};
}
vCard.mimeType = "text/vcard";
vCard.extension = ".vcf";
vCard.versions = ["2.1", "3.0", "4.0"];
vCard.EOL = "\r\n";
vCard.foldLine = require_foldline();
vCard.normalize = function(input) {
return (input + "").replace(/^[\s\r\n]+|[\s\r\n]+$/g, "").replace(/(\r\n)[\x09\x20]?(\r\n)|$/g, "$1").replace(/\r\n[\x20\x09]/g, "");
};
vCard.isSupported = function(version) {
return /^\d\.\d$/.test(version) && vCard.versions.indexOf(version) !== -1;
};
vCard.parse = function(value) {
var objects = (value + "").split(/(?=BEGIN\:VCARD)/gi);
var cards = [];
for (var i = 0; i < objects.length; i++) {
cards.push(new vCard().parse(objects[i]));
}
return cards;
};
vCard.parseLines = require_parse_lines();
vCard.fromJSON = function(jcard) {
jcard = typeof jcard === "string" ? JSON.parse(jcard) : jcard;
if (jcard == null || !Array.isArray(jcard))
return new vCard();
if (!/vcard/i.test(jcard[0]))
throw new Error("Object not in jCard format");
var card = new vCard();
jcard[1].forEach(function(prop) {
card.addProperty(vCard.Property.fromJSON(prop));
});
return card;
};
vCard.format = function(card, version) {
version = version || card.version || vCard.versions[vCard.versions.length - 1];
if (!vCard.isSupported(version))
throw new Error('Unsupported vCard version "' + version + '"');
var vcf3 = [];
vcf3.push("BEGIN:VCARD");
vcf3.push("VERSION:" + version);
var props = Object.keys(card.data);
var prop = "";
for (var i = 0; i < props.length; i++) {
if (props[i] === "version")
continue;
prop = card.data[props[i]];
if (Array.isArray(prop)) {
for (var k = 0; k < prop.length; k++) {
if (prop[k].isEmpty())
continue;
vcf3.push(vCard.foldLine(prop[k].toString(version), 75));
}
} else if (!prop.isEmpty()) {
vcf3.push(vCard.foldLine(prop.toString(version), 75));
}
}
vcf3.push("END:VCARD");
return vcf3.join(vCard.EOL);
};
vCard.Property = require_property();
vCard.prototype = {
constructor: vCard,
/**
* Get a vCard property
* @param {String} key
* @return {Object|Array}
*/
get: function(key) {
if (this.data[key] == null) {
return this.data[key];
}
if (Array.isArray(this.data[key])) {
return this.data[key].map(function(prop) {
return prop.clone();
});
} else {
return this.data[key].clone();
}
},
/**
* Set a vCard property
* @param {String} key
* @param {String} value
* @param {Object} params
*/
set: function(key, value, params) {
return this.setProperty(new vCard.Property(key, value, params));
},
/**
* Add a vCard property
* @param {String} key
* @param {String} value
* @param {Object} params
*/
add: function(key, value, params) {
var prop = new vCard.Property(key, value, params);
this.addProperty(prop);
return this;
},
/**
* Set a vCard property from an already
* constructed vCard.Property
* @param {vCard.Property} prop
*/
setProperty: function(prop) {
this.data[prop._field] = prop;
return this;
},
/**
* Add a vCard property from an already
* constructed vCard.Property
* @param {vCard.Property} prop
*/
addProperty: function(prop) {
var key = prop._field;
if (Array.isArray(this.data[key])) {
this.data[key].push(prop);
} else if (this.data[key] != null) {
this.data[key] = [this.data[key], prop];
} else {
this.data[key] = prop;
}
return this;
},
/**
* Parse a vcf formatted vCard
* @param {String} value
* @return {vCard}
*/
parse: function(value) {
var lines = vCard.normalize(value).split(/\r\n/g);
var begin = lines[0];
var version = lines[1];
var end = lines[lines.length - 1];
const regexp_version = /VERSION:\d\.\d/i;
if (!/BEGIN:VCARD/i.test(begin))
throw new SyntaxError('Invalid vCard: Expected "BEGIN:VCARD" but found "' + begin + '"');
if (!/END:VCARD/i.test(end))
throw new SyntaxError('Invalid vCard: Expected "END:VCARD" but found "' + end + '"');
if (!regexp_version.test(version)) {
if (!(version = lines.find((line) => regexp_version.test(line))))
throw new SyntaxError('Invalid vCard: Expected "VERSION:\\d.\\d" but none found');
}
this.version = version.substring(8, 11);
if (!vCard.isSupported(this.version))
throw new Error('Unsupported version "' + this.version + '"');
this.data = vCard.parseLines(lines);
return this;
},
/**
* Format the vCard as vcf with given version
* @param {String} version
* @param {String} charset
* @return {String}
*/
toString: function(version, charset) {
version = version || this.version;
return vCard.format(this, version);
},
/**
* Format the card as jCard
* @param {String} version='4.0'
* @return {Array} jCard
*/
toJCard: function(version) {
version = version || "4.0";
var keys = Object.keys(this.data);
var data = [["version", {}, "text", version]];
var prop = null;
for (var i = 0; i < keys.length; i++) {
if (keys[i] === "version")
continue;
prop = this.data[keys[i]];
if (Array.isArray(prop)) {
for (var k = 0; k < prop.length; k++) {
data.push(prop[k].toJSON());
}
} else {
data.push(prop.toJSON());
}
}
return ["vcard", data];
},
/**
* Format the card as jCard
* @return {Array} jCard
*/
toJSON: function() {
return this.toJCard(this.version);
}
};
module2.exports = vCard;
}
});
// src/main.ts
var main_exports = {};
__export(main_exports, {
default: () => ContactsPlugin
});
module.exports = __toCommonJS(main_exports);
var import_obsidian2 = require("obsidian");
// src/vcard.ts
var vcf = require_vcard();
var VCard = class {
constructor(card) {
var _a, _b, _c, _d, _e;
this.version = card.version;
this.fn = (_a = card.get("fn")) == null ? void 0 : _a.valueOf().replace(/\\,/g, ",");
this.nickname = (_c = (_b = card.get("nickname")) == null ? void 0 : _b.valueOf()) != null ? _c : void 0;
this.organization = this.parseOrganization(card.get("org"));
this.title = (_e = (_d = card.get("title")) == null ? void 0 : _d.valueOf()) != null ? _e : void 0;
this.telephones = this.parseTelephones(card.get("tel"));
this.addresses = this.parseAddresses(card.get("adr"));
this.emails = this.parseEmails(card.get("email"));
this.birthdate = this.parseBirthdate(card.get("bday"));
this.URLs = this.parseURLs(card.get("url"));
this.notes = this.parseNotes(card.get("note"));
this.xabuid = this.parseXAbuid(card.get("xAbuid"));
if (this.fn == void 0) {
console.debug(`Found vCard without name:
${JSON.stringify(this, null, 2)}`);
}
}
getFilename() {
var _a;
return (_a = this.fn) != null ? _a : "NO_NAME";
}
toMarkdown(enabledFields) {
var _a;
let markdown = `## \u{1F464} ${this.fn}
[Open in Contacts](addressbook://${(_a = this.xabuid) == null ? void 0 : _a.replace(":", "%3A")})
`;
this.getVCardFields().forEach((field) => {
var _a2;
if (!enabledFields.includes(field))
return;
markdown += (_a2 = this.fieldToMarkdown(field)) != null ? _a2 : "";
});
return markdown.trim();
}
fieldToMarkdown(field) {
var _a, _b, _c, _d, _e;
if (typeof this[field] === "undefined")
return void 0;
switch (field) {
case "nickname":
return `- Nickname: ${this.nickname}
`;
case "notes":
return `- \u{1F4DD} Notes:
${this.notes}
`;
case "birthdate":
return `- \u{1F382} Birthday: ${(_a = this.birthdate) == null ? void 0 : _a.toLocaleDateString()}
`;
case "organization":
return `- \u{1F3E2} Organization: ${this.organization}
`;
case "title":
return `- \u{1F454} Title: ${this.title}
`;
case "emails":
return (_b = this.emails) == null ? void 0 : _b.map((mail) => {
return `- \u{1F4E7} [${mail}](mailto:${mail})
`;
}).join("");
case "URLs":
return (_c = this.URLs) == null ? void 0 : _c.map((URL) => {
return `- \u{1F310} Website: [${URL}](${URL})
`;
}).join("");
case "telephones":
return (_d = this.telephones) == null ? void 0 : _d.map(([type, tel]) => {
let emotes = ``;
const types = type.split(",");
switch (types[0]) {
case "cell":
emotes += "\u{1F4F1}";
break;
case "home":
emotes += "\u{1F3E0}";
break;
case "work":
emotes += "\u{1F3E2}";
break;
default:
emotes += "\u260E\uFE0F";
}
switch (types[1]) {
case "voice":
emotes += "\u{1F4DE}";
break;
case "fax":
emotes += "\u{1F4E0}";
break;
default:
}
return `- ${emotes} [${tel}](tel:${tel.replace(" ", "")})
`;
}).join("");
case "addresses":
return (_e = this.addresses) == null ? void 0 : _e.map(([type, adr]) => {
let emotes = ``;
switch (type) {
case "home":
emotes += "\u{1F3E0}";
break;
case "work":
emotes += "\u{1F3E2}";
break;
default:
emotes += "\u260E\uFE0F";
}
let [_, __, street, city, ___, postcode, country] = adr.valueOf().split(";");
return `- ${emotes} ${type} address:
${street}
${postcode} ${city}
${country}
`;
}).join("");
default:
console.error(`Error: Unknown field or markdown convertion: ${field}`);
return void 0;
}
}
parseBirthdate(bday) {
let birthdate = void 0;
if (bday) {
birthdate = new Date(Date.parse(bday.valueOf()));
}
return birthdate;
}
parseURLs(URLs) {
let links = void 0;
if (URLs) {
links = new Array();
if (!Array.isArray(URLs)) {
URLs = [URLs];
}
for (let url of URLs) {
links.push(url.valueOf());
}
}
return links;
}
parseNotes(notes) {
var _a;
let notesString = void 0;
if (notes) {
notesString = (_a = notes.valueOf()) == null ? void 0 : _a.split("\\n").map((line) => line.endsWith("\\") ? line.slice(0, line.length - 2) : line).join("\n ");
}
return notesString;
}
parseXAbuid(xabuid) {
let xabuidString = void 0;
if (xabuid) {
xabuidString = xabuid.valueOf();
}
return xabuidString;
}
parseEmails(email) {
let emails = void 0;
if (email) {
emails = new Array();
if (!Array.isArray(email)) {
email = [email];
}
for (let mail of email) {
emails.push(mail.valueOf());
}
}
return emails;
}
parseAddresses(adr) {
var _a, _b, _c, _d, _e;
let addresses = void 0;
if (adr) {
addresses = new Array();
if (!Array.isArray(adr)) {
adr = [adr];
}
for (let address of adr) {
let type;
if (typeof address.type == "string")
type = (_b = (_a = address.type) == null ? void 0 : _a.toLowerCase()) != null ? _b : "home";
else if (Array.isArray(address.type))
type = (_e = (_d = (_c = address.type) == null ? void 0 : _c[0]) == null ? void 0 : _d.toLowerCase()) != null ? _e : "home";
addresses.push([type, address.valueOf()]);
}
}
return addresses;
}
parseTelephones(tel) {
var _a, _b, _c, _d, _e, _f;
let telephones = void 0;
if (tel) {
telephones = new Array();
if (!Array.isArray(tel)) {
tel = [tel];
}
for (let telephone of tel) {
let type = ((_c = (_b = (_a = telephone.type) == null ? void 0 : _a[0]) == null ? void 0 : _b.toLowerCase()) != null ? _c : "phone") + "," + ((_f = (_e = (_d = telephone.type) == null ? void 0 : _d[1]) == null ? void 0 : _e.toLowerCase()) != null ? _f : "voice");
telephones.push([type, telephone.valueOf()]);
}
}
return telephones;
}
parseOrganization(org) {
let organization = void 0;
if (org) {
organization = org.valueOf().replace(";", ", ").replace(/\\,/g, ",");
}
return organization;
}
static getVCardFields() {
const JSON_VCARD = ["vcard", [["version", {}, "text", "4.0"], ["fn", {}, "text", "name"]]];
const inst = new VCard(vcf.fromJSON(JSON_VCARD));
return inst.getVCardFields();
}
getVCardFields() {
return Object.getOwnPropertyNames(this).filter((prop) => prop !== "constructor" && typeof this[prop] !== "function" && prop !== "version" && prop !== "fn");
}
};
// src/osascriptService.ts
var { spawn } = require("child_process");
var OsaScriptService = class {
async executeScript(JXA_script) {
return new Promise((resolve, reject) => {
const osascript = spawn("osascript", ["-l", "JavaScript", "-e", JXA_script]);
let output = "";
osascript.stdout.on("data", (data) => {
output += data.toString("utf-8");
});
osascript.stderr.on("data", (data) => {
const errorMsg = data.toString("utf-8");
reject(new Error(`Error executing JXA script:
${errorMsg}`));
});
osascript.on("close", (code) => {
if (code === 0) {
resolve(output);
} else {
reject(new Error(`Error executing JXA script:
${output}`));
}
});
});
}
};
// src/contactsService.ts
var vcf2 = require_vcard();
var ContactsService = class {
constructor(groupName, enabledContactFields, osaScriptService = new OsaScriptService()) {
this.GROUP_NOT_DEFINED_ERROR = "GROUP NOT DEFINED";
this.groupName = groupName;
this.enabledContactFields = enabledContactFields;
this.osaScriptService = osaScriptService;
}
async loadContacts() {
let vCards = await this.getVCards();
vCards = vCards.filter((vcard) => {
return vcard.fn != void 0;
});
const filenameToMarkdown = /* @__PURE__ */ new Map();
for (let vcard of vCards) {
filenameToMarkdown.set(vcard.getFilename(), vcard.toMarkdown(this.enabledContactFields));
}
return filenameToMarkdown;
}
async getNumberOfContacts() {
const JXA_SCRIPT = `
let Contacts = Application('Contacts');
Contacts.includeStandardAdditions = true;
let groups = Contacts.groups.whose({ name: '${this.groupName}'});
if (groups.length === 0 || groups === undefined || groups === null)
throw new Error('${this.GROUP_NOT_DEFINED_ERROR}');
groups[0].people.length;
`;
let resultPromise = this.osaScriptService.executeScript(JXA_SCRIPT).then((resultStr) => {
let resultInt = parseInt(resultStr);
if (isNaN(resultInt)) {
throw new Error(`Non-numeric result from JXA script: ${resultStr}`);
}
return resultInt;
});
return await resultPromise;
}
async getVCards() {
const JXA_SCRIPT = `
ObjC.import('Foundation');
const stdout = $.NSFileHandle.fileHandleWithStandardOutput;
let Contacts = Application('Contacts');
Contacts.includeStandardAdditions = true;
let groups = Contacts.groups.whose({ name: '${this.groupName}'});
if (groups.length === 0 || groups === undefined || groups === null)
throw new Error('${this.GROUP_NOT_DEFINED_ERROR}');
for (let vcard of groups[0].people.vcard()) {
// Write to stdout
const nsString = $.NSString.alloc.initWithUTF8String(vcard);
const data = nsString.dataUsingEncoding($.NSUTF8StringEncoding);
stdout.writeData(data);
}
`;
const vCardRegex = /BEGIN:VCARD[\s\S]*?END:VCARD/g;
let resultPromise = this.osaScriptService.executeScript(JXA_SCRIPT).then((vCardStr) => {
let matches = vCardStr.match(vCardRegex);
let vCards = [];
for (let match of matches != null ? matches : []) {
const card = new vcf2().parse(match);
const vCardObj = new VCard(card);
vCards.push(vCardObj);
}
return vCards;
});
return await resultPromise;
}
};
// src/fileService.ts
var import_obsidian = require("obsidian");
var ContentSeperator = class {
constructor(startTag, startText, stopTag, stopText, commented = true) {
this.startTag = startTag;
this.startText = startText;
this.endTag = stopTag;
this.endText = stopText;
this.commented = commented;
this.commentStart = commented ? "<!--" : "";
this.commentEnd = commented ? "-->" : "";
}
buildContentString(content) {
return `${this.commentStart} ${this.startTag} ${this.startText} ${this.commentEnd}
${content}
${this.commentStart} ${this.endTag} ${this.endText} ${this.commentEnd}`;
}
};
var FileService = class {
async createFolder(folderPath, app) {
if (await app.vault.adapter.exists((0, import_obsidian.normalizePath)(folderPath)) == false)
await app.vault.createFolder(folderPath);
}
async saveFile(filePath, content, app) {
return app.vault.create(filePath, content);
}
async updateFile(file, updatedContent, seperator, app) {
return app.vault.process(file, (oldContent) => {
let oldLines = oldContent.split("\n");
let startIndex = oldLines.findIndex((line) => line.contains(seperator.startTag));
let stopIndex = oldLines.findIndex((line) => line.contains(seperator.endTag));
let newLines = Array();
for (let i = Math.min(0, startIndex); i < oldLines.length; i++) {
if (i == startIndex) {
newLines.push(updatedContent);
i = stopIndex;
continue;
}
newLines.push(oldLines[i]);
}
return newLines.join("\n");
});
}
};
// src/main.ts
var SettingTab = class extends import_obsidian2.PluginSettingTab {
constructor(app, plugin) {
super(app, plugin);
this.plugin = plugin;
}
display() {
const { containerEl } = this;
containerEl.empty();
new import_obsidian2.Setting(containerEl).setName("Contacts folder").setDesc("Select the folder in which your contacts will stored").addText((text) => text.setPlaceholder("Contacts").setValue(this.plugin.settings.contactsFolder).onChange(async (value) => {
this.plugin.settings.contactsFolder = value;
await this.plugin.saveSettings();
}));
new import_obsidian2.Setting(containerEl).setName("Contacts group").setDesc('Enter the name of the group ("Smart List") in which your contacts are stored in the MacOS Contacts app').addText((text) => text.setPlaceholder("Obsidian").setValue(this.plugin.settings.contactsGroup).onChange(async (value) => {
this.plugin.settings.contactsGroup = value;
await this.plugin.saveSettings();
}));
new import_obsidian2.Setting(containerEl).setName("Configure the shown contact fields below").setDesc("To update the shown contact fields, re-sync your contacts");
for (let attribute of VCard.getVCardFields()) {
new import_obsidian2.Setting(containerEl).setName(`${attribute}`).addToggle((toggle) => {
toggle.setValue(this.plugin.settings.enabledContactFields.includes(attribute));
toggle.onChange(async (value) => {
this.plugin.settings.enabledContactFields = this.toggleEnabledField(attribute, value);
await this.plugin.saveSettings();
});
});
}
}
toggleEnabledField(field, value) {
let enabledFields = this.plugin.settings.enabledContactFields.split(",");
if (value) {
enabledFields.push(field);
} else {
enabledFields = enabledFields.filter((enabledField) => enabledField != field);
}
return enabledFields.join(",");
}
};
var DEFAULT_SETTINGS = {
contactsGroup: "Obsidian",
contactsFolder: "Contacts",
autogenerationStartTag: "START",
autogenerationStartText: "Content BELOW this line is AUTOGENERATED and will be REPLACED.",
autogenerationEndTag: "END",
autogenerationEndText: "Content ABOVE this line is AUTOGENERATED and will be REPLACED.",
enabledContactFields: "nickname,emails,title,organization,telephones,addresses,birthdate,URLs,notes"
};
var ContactsPlugin = class extends import_obsidian2.Plugin {
async onload() {
await this.loadSettings();
this.addCommand({
id: "sync-contacts",
name: "Sync contacts",
callback: async () => {
if (!import_obsidian2.Platform.isMacOS)
return new import_obsidian2.Notice("Error: This plugin only works on MacOS");
new import_obsidian2.Notice("Syncing...");
let loadContactsLogic = new ContactsService(this.settings.contactsGroup, this.settings.enabledContactFields);
let numContactsPromise = loadContactsLogic.getNumberOfContacts().then((numContacts2) => {
new import_obsidian2.Notice(`Found ${numContacts2} Contacts in group ${this.settings.contactsGroup}`);
return numContacts2;
});
let fileService = new FileService();
let createFolderPromise = fileService.createFolder(this.settings.contactsFolder, this.app);
let contentSeperator = new ContentSeperator(
this.settings.autogenerationStartTag,
this.settings.autogenerationStartText,
this.settings.autogenerationEndTag,
this.settings.autogenerationEndText
);
let [numContacts, _] = await Promise.all([numContactsPromise, createFolderPromise]);
let markdownResults = await loadContactsLogic.loadContacts();
let successfulContacts = 0;
let promises = [];
for (let [filename, markdown] of markdownResults) {
let filePath = (0, import_obsidian2.normalizePath)(`${this.settings.contactsFolder}/${filename}.md`);
let file = this.app.vault.getAbstractFileByPath(filePath);
let newContactInfo = contentSeperator.buildContentString(markdown);
if (file instanceof import_obsidian2.TFolder) {
console.error(`Error: ${filePath} is a folder`);
new import_obsidian2.Notice(`Error: ${filePath} is a folder`);
} else if (file === null) {
promises.push(
fileService.saveFile(filePath, newContactInfo, this.app).then((_2) => successfulContacts++).catch((error) => console.error(`Error syncing ${filename}
${error}`))
);
} else if (file instanceof import_obsidian2.TFile) {
promises.push(
// extract the old contact info and replace it with the new data
fileService.updateFile(file, newContactInfo, new ContentSeperator(this.settings.autogenerationStartTag, this.settings.autogenerationStartText, this.settings.autogenerationEndTag, this.settings.autogenerationEndText), this.app).then((_2) => successfulContacts++).catch((error) => console.error(`Error syncing ${filename}
${error}`))
);
}
}
await Promise.all(promises).catch((error) => {
new import_obsidian2.Notice("Error syncing contacts!");
console.error(error);
}).finally(() => {
new import_obsidian2.Notice(`Successfully synced ${successfulContacts} of ${numContacts} Contacts`);
console.info(`Successfully synced ${successfulContacts} of ${numContacts} Contacts`);
});
}
});
this.addSettingTab(new SettingTab(this.app, this));
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}
};
/* nosourcemap */