shepherd-onboard 0.1.16 → 0.1.17

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.
@@ -1356,15 +1356,15 @@ function renderMessagesSelectorPage(chats, token, error = "") {
1356
1356
  <title>Select Messages Chats</title>
1357
1357
  <style>
1358
1358
  :root {
1359
- --bg: #fbfbfa;
1359
+ --bg: #ffffff;
1360
1360
  --fg: #141614;
1361
- --muted: #767d78;
1362
- --faint: #9aa09b;
1363
- --line: #dedfdd;
1361
+ --muted: #615d59;
1362
+ --faint: #a39e98;
1363
+ --line: #e6e6e6;
1364
1364
  --green: #1f5c2e;
1365
1365
  --green-hover: #246836;
1366
1366
  --radius: 8px;
1367
- --grid: 20px minmax(0, 1fr) 78px 104px;
1367
+ --grid: 20px minmax(0, 1fr) 76px 108px;
1368
1368
  }
1369
1369
  * { box-sizing: border-box; }
1370
1370
  body {
@@ -1377,9 +1377,9 @@ function renderMessagesSelectorPage(chats, token, error = "") {
1377
1377
  -webkit-font-smoothing: antialiased;
1378
1378
  }
1379
1379
  main {
1380
- width: min(680px, calc(100vw - 32px));
1380
+ width: min(640px, calc(100vw - 32px));
1381
1381
  margin: 0 auto;
1382
- padding: 48px 0 28px;
1382
+ padding: 56px 0 28px;
1383
1383
  }
1384
1384
  .header {
1385
1385
  display: grid;
@@ -1390,7 +1390,7 @@ function renderMessagesSelectorPage(chats, token, error = "") {
1390
1390
  .brand {
1391
1391
  display: grid;
1392
1392
  justify-items: center;
1393
- gap: 14px;
1393
+ gap: 16px;
1394
1394
  min-width: 0;
1395
1395
  }
1396
1396
  .logo {
@@ -1412,14 +1412,14 @@ function renderMessagesSelectorPage(chats, token, error = "") {
1412
1412
  }
1413
1413
  h1 {
1414
1414
  margin: 0;
1415
- font-size: 28px;
1415
+ font-size: 30px;
1416
1416
  line-height: 1.1;
1417
1417
  font-weight: 700;
1418
1418
  letter-spacing: 0;
1419
1419
  }
1420
1420
  .subtitle {
1421
1421
  margin: 8px 0 0;
1422
- font-size: 15px;
1422
+ font-size: 14px;
1423
1423
  line-height: 1.4;
1424
1424
  color: var(--muted);
1425
1425
  }
@@ -1431,11 +1431,11 @@ function renderMessagesSelectorPage(chats, token, error = "") {
1431
1431
  }
1432
1432
  .search {
1433
1433
  width: 100%;
1434
- border: 1px solid #d7d9d6;
1434
+ border: 1px solid var(--line);
1435
1435
  border-radius: 8px;
1436
1436
  background: #ffffff;
1437
1437
  color: var(--fg);
1438
- padding: 11px 12px;
1438
+ padding: 12px 13px;
1439
1439
  font: inherit;
1440
1440
  font-size: 15px;
1441
1441
  outline: none;
@@ -1463,6 +1463,7 @@ function renderMessagesSelectorPage(chats, token, error = "") {
1463
1463
  font-size: 11px;
1464
1464
  font-weight: 600;
1465
1465
  text-transform: uppercase;
1466
+ letter-spacing: 0.02em;
1466
1467
  }
1467
1468
  .list-head .right { text-align: left; }
1468
1469
  .chat-list { display: block; }
@@ -1476,7 +1477,7 @@ function renderMessagesSelectorPage(chats, token, error = "") {
1476
1477
  cursor: pointer;
1477
1478
  transition: background 120ms ease;
1478
1479
  }
1479
- .chat-row:hover { background: #f6f7f5; }
1480
+ .chat-row:hover { background: #f6f5f4; }
1480
1481
  input[type="checkbox"] {
1481
1482
  position: absolute;
1482
1483
  opacity: 0;
@@ -1514,7 +1515,7 @@ function renderMessagesSelectorPage(chats, token, error = "") {
1514
1515
  text-overflow: ellipsis;
1515
1516
  white-space: nowrap;
1516
1517
  font-size: 15px;
1517
- font-weight: 550;
1518
+ font-weight: 500;
1518
1519
  }
1519
1520
  .chat-people {
1520
1521
  color: var(--muted);
@@ -1528,9 +1529,9 @@ function renderMessagesSelectorPage(chats, token, error = "") {
1528
1529
  justify-self: start;
1529
1530
  color: var(--muted);
1530
1531
  font-size: 12.5px;
1531
- font-weight: 500;
1532
+ font-weight: 400;
1532
1533
  }
1533
- .chat-kind--group { color: var(--green); }
1534
+ .chat-kind--group { color: var(--fg); }
1534
1535
  .chat-meta {
1535
1536
  color: var(--muted);
1536
1537
  font-size: 12.5px;
@@ -1563,7 +1564,7 @@ function renderMessagesSelectorPage(chats, token, error = "") {
1563
1564
  button {
1564
1565
  appearance: none;
1565
1566
  border: 0;
1566
- border-radius: 6px;
1567
+ border-radius: 8px;
1567
1568
  background: var(--green);
1568
1569
  color: #FFFFFF;
1569
1570
  padding: 9px 13px;
@@ -1600,7 +1601,6 @@ function renderMessagesSelectorPage(chats, token, error = "") {
1600
1601
  </div>
1601
1602
  ${error ? `<p class="error">${html(error)}</p>` : ""}
1602
1603
  <div class="list-head">
1603
- <span></span>
1604
1604
  <span></span>
1605
1605
  <span>Name</span>
1606
1606
  <span>Type</span>
@@ -1685,7 +1685,9 @@ function renderChatPeople(chat) {
1685
1685
 
1686
1686
  function chatPeopleLine(chat) {
1687
1687
  if (chat.kind !== "group") return "";
1688
- const names = chat.participants?.map((participant) => participant.name ?? participant.handle).filter(Boolean) ?? [];
1688
+ const names = chat.participants
1689
+ ?.map((participant) => participant.name)
1690
+ .filter((name) => name && !looksLikeHandleList(name)) ?? [];
1689
1691
  const line = names.slice(0, 6).join(", ");
1690
1692
  if (!line || normalizeDisplayText(line) === normalizeDisplayText(chat.label)) return "";
1691
1693
  return line;
@@ -2022,8 +2024,8 @@ function createMessageSerializer(kit, contactLookup = emptyContactLookup()) {
2022
2024
  };
2023
2025
  }
2024
2026
 
2025
- function buildContactLookup() {
2026
- const contacts = loadContacts();
2027
+ function buildContactLookup(opts = {}) {
2028
+ const contacts = opts.loadAll === false ? [] : loadContacts();
2027
2029
  const myCard = loadMyCard();
2028
2030
  const handleToName = new Map();
2029
2031
  const selfHandles = new Set();
@@ -2070,6 +2072,9 @@ function emptyContactLookup() {
2070
2072
 
2071
2073
  function loadContacts() {
2072
2074
  if (platform() !== "darwin") return [];
2075
+ const sqliteContacts = loadContactsFromAddressBookDb();
2076
+ if (sqliteContacts.length > 0) return sqliteContacts;
2077
+
2073
2078
  const script = `
2074
2079
  set output to ""
2075
2080
  tell application "Contacts"
@@ -2093,14 +2098,71 @@ return output`;
2093
2098
  try {
2094
2099
  const raw = execFileSync("osascript", ["-e", script], {
2095
2100
  encoding: "utf8",
2096
- timeout: 30_000,
2101
+ timeout: 120_000,
2097
2102
  });
2098
2103
  return parseContacts(raw);
2099
- } catch {
2104
+ } catch (err) {
2105
+ if (args.debug === true) console.error("Could not load Contacts:", safeError(err));
2100
2106
  return [];
2101
2107
  }
2102
2108
  }
2103
2109
 
2110
+ function loadContactsFromAddressBookDb() {
2111
+ const contacts = new Map();
2112
+ for (const dbPath of addressBookDatabasePaths()) {
2113
+ const query = `
2114
+ select r.Z_PK,
2115
+ coalesce(nullif(r.ZNAME, ''), nullif(trim(coalesce(r.ZFIRSTNAME, '') || ' ' || coalesce(r.ZLASTNAME, '')), ''), nullif(r.ZORGANIZATION, ''), '') as display_name,
2116
+ coalesce(p.ZFULLNUMBER, '') as phone,
2117
+ '' as email
2118
+ from ZABCDRECORD r
2119
+ join ZABCDPHONENUMBER p on p.ZOWNER = r.Z_PK
2120
+ where p.ZFULLNUMBER is not null and p.ZFULLNUMBER != ''
2121
+ union all
2122
+ select r.Z_PK,
2123
+ coalesce(nullif(r.ZNAME, ''), nullif(trim(coalesce(r.ZFIRSTNAME, '') || ' ' || coalesce(r.ZLASTNAME, '')), ''), nullif(r.ZORGANIZATION, ''), '') as display_name,
2124
+ '' as phone,
2125
+ coalesce(e.ZADDRESS, '') as email
2126
+ from ZABCDRECORD r
2127
+ join ZABCDEMAILADDRESS e on e.ZOWNER = r.Z_PK
2128
+ where e.ZADDRESS is not null and e.ZADDRESS != '';`;
2129
+
2130
+ try {
2131
+ const raw = execFileSync("sqlite3", ["-separator", "\t", dbPath, query], {
2132
+ encoding: "utf8",
2133
+ timeout: 10_000,
2134
+ });
2135
+ for (const line of raw.split("\n").filter(Boolean)) {
2136
+ const [id, rawName, phone, email] = line.split("\t");
2137
+ const name = rawName?.trim();
2138
+ if (!id || !name) continue;
2139
+ const key = `${dbPath}:${id}`;
2140
+ const current = contacts.get(key) ?? { name, phones: [], emails: [] };
2141
+ if (phone) current.phones.push(phone.trim());
2142
+ if (email) current.emails.push(email.trim());
2143
+ contacts.set(key, current);
2144
+ }
2145
+ } catch (err) {
2146
+ if (args.debug === true) console.error(`Could not read Contacts DB ${dbPath}:`, safeError(err));
2147
+ }
2148
+ }
2149
+
2150
+ return [...contacts.values()].filter((contact) => contact.name);
2151
+ }
2152
+
2153
+ function addressBookDatabasePaths() {
2154
+ const addressBookDir = join(homedir(), "Library", "Application Support", "AddressBook");
2155
+ try {
2156
+ const raw = execFileSync("find", [addressBookDir, "-maxdepth", "4", "-name", "AddressBook-v22.abcddb"], {
2157
+ encoding: "utf8",
2158
+ timeout: 5_000,
2159
+ });
2160
+ return [...new Set(raw.split("\n").map((path) => path.trim()).filter(Boolean))];
2161
+ } catch {
2162
+ return [join(addressBookDir, "AddressBook-v22.abcddb")];
2163
+ }
2164
+ }
2165
+
2104
2166
  function loadMyCard() {
2105
2167
  if (platform() !== "darwin") return null;
2106
2168
  const script = `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shepherd-onboard",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "Customer-facing Shepherd raw sync onboarding CLI",
5
5
  "type": "module",
6
6
  "bin": {