accelr8 3.0.5 → 3.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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/protocol.ts
4
- var VERSION = true ? "3.0.5" : "3.0.2";
4
+ var VERSION = true ? "3.1.0" : "3.0.2";
5
5
  var PROTO = 3;
6
6
  var PORT = 8787;
7
7
  var DEFAULT_SERVER = "wss://accelr8-canvas.patonchain.workers.dev";
@@ -7,7 +7,7 @@ import {
7
7
  VERSION,
8
8
  decode,
9
9
  encode
10
- } from "./chunk-CZENTCH6.js";
10
+ } from "./chunk-HF4HSRPK.js";
11
11
  import {
12
12
  DEFAULT_MODEL,
13
13
  addAccount,
@@ -1130,16 +1130,16 @@ function statusColor(engine, frame2, info) {
1130
1130
  }
1131
1131
  function microLine(w, engine, frame2, info) {
1132
1132
  const dot = { text: "\u25C9", color: statusColor(engine, frame2, info) };
1133
- const project = info.project ? { text: info.project, color: BALL } : null;
1133
+ const project2 = info.project ? { text: info.project, color: BALL } : null;
1134
1134
  const counts = info.t0 > 0 ? { text: `${info.t0} new`, color: BRIGHT, bold: true } : null;
1135
1135
  const ticker = info.lastEvent ? { text: info.lastEvent, color: info.lastEventColor } : null;
1136
1136
  const onlineFull = { text: `${info.online} online`, color: BALL };
1137
1137
  const onlineNum = { text: `${info.online}`, color: BALL };
1138
1138
  const ladder = [
1139
- { brand: true, fields: [project, onlineFull, counts], ticker: "full" },
1140
- { fields: [project, onlineFull, counts], ticker: "trunc" },
1141
- { fields: [project, onlineFull, counts] },
1142
- { fields: [project, onlineNum, counts] },
1139
+ { brand: true, fields: [project2, onlineFull, counts], ticker: "full" },
1140
+ { fields: [project2, onlineFull, counts], ticker: "trunc" },
1141
+ { fields: [project2, onlineFull, counts] },
1142
+ { fields: [project2, onlineNum, counts] },
1143
1143
  { fields: [onlineNum, counts] }
1144
1144
  ];
1145
1145
  for (const rung of ladder) {
@@ -1547,6 +1547,256 @@ function errMsg(e) {
1547
1547
  return e instanceof Error ? e.message : String(e);
1548
1548
  }
1549
1549
 
1550
+ // src/map.ts
1551
+ var LAND = 240;
1552
+ var MIXED = 231;
1553
+ var LAND_CHAR = "\xB7";
1554
+ var DOT_CHAR = "\u25CF";
1555
+ var WORLD = [
1556
+ " ### ",
1557
+ // 90..81N arctic
1558
+ " ##### ###### ########### ",
1559
+ // 81..72N archipelago·Greenland·Siberia
1560
+ " ################# ###### # #### ####################### ",
1561
+ // 72..63N Alaska·Canada·Iceland·Scandinavia·Russia
1562
+ " ############ #### ## # ########################### ",
1563
+ // 63..54N Hudson Bay notch·Scotland·Baltic·Kamchatka
1564
+ " ############## ## ####################### ",
1565
+ // 54..45N US/Canada border·UK·Europe·steppe
1566
+ " ########## ### ###### ############# # ",
1567
+ // 45..36N US·Iberia·Med·central Asia·Japan
1568
+ " ######## ####################### ## ",
1569
+ // 36..27N south US·north Africa·Middle East·China
1570
+ " #### ## ######### ### ### ###### ",
1571
+ // 27..18N Mexico·Cuba·Sahara·Arabia·India·SE Asia
1572
+ " ### ## ############ ## ### # ",
1573
+ // 18..9N Cent. America·Sahel·Horn·Philippines
1574
+ " ##### ########### # ##### ",
1575
+ // 9..0N Colombia·Congo·Sri Lanka·Indonesia
1576
+ " ######## ####### ### #### ",
1577
+ // 0..9S Amazon·Tanzania·Java·New Guinea
1578
+ " ####### ##### # ##### ",
1579
+ // 9..18S Brazil·Angola·Madagascar·north Australia
1580
+ " ###### #### # ######## ",
1581
+ // 18..27S Paraguay·Namibia·Australia
1582
+ " #### ### ####### ",
1583
+ // 27..36S Argentina·South Africa·Sydney coast
1584
+ " ### # ## ",
1585
+ // 36..45S Patagonia·Tasmania·New Zealand
1586
+ " ## # ",
1587
+ // 45..54S
1588
+ " # ",
1589
+ // 54..63S Tierra del Fuego
1590
+ " ## ###### ####### ",
1591
+ // 63..72S Antarctic peninsula + coast
1592
+ " ######################################### ",
1593
+ // 72..81S Antarctica
1594
+ " ############################### "
1595
+ // 81..90S
1596
+ ];
1597
+ var WORLD_W = 60;
1598
+ var WORLD_H = 20;
1599
+ function landAt(x, y, W2, H2) {
1600
+ const bx = Math.min(WORLD_W - 1, Math.floor((x + 0.5) * WORLD_W / W2));
1601
+ const by = Math.min(WORLD_H - 1, Math.floor((y + 0.5) * WORLD_H / H2));
1602
+ return WORLD[by]?.charCodeAt(bx) === 35;
1603
+ }
1604
+ function project(lat, lon, w, h) {
1605
+ const la = Math.max(-90, Math.min(90, Number.isFinite(lat) ? lat : 0));
1606
+ const lo = Math.max(-180, Math.min(180, Number.isFinite(lon) ? lon : 0));
1607
+ const x = w > 1 ? Math.round((lo + 180) / 360 * (w - 1)) : 0;
1608
+ const y = h > 1 ? Math.round((90 - la) / 180 * (h - 1)) : 0;
1609
+ return { x, y };
1610
+ }
1611
+ function renderMap(w, h, dots) {
1612
+ const W2 = Math.max(0, Math.floor(w));
1613
+ const H2 = Math.max(0, Math.floor(h));
1614
+ const lines = [];
1615
+ const hits = [];
1616
+ if (W2 < 1) {
1617
+ while (lines.length < H2) lines.push({ text: "", spans: [] });
1618
+ return { lines, hits };
1619
+ }
1620
+ if (H2 >= 1) {
1621
+ const byCell = /* @__PURE__ */ new Map();
1622
+ for (const d of dots) {
1623
+ const { x, y } = project(d.lat, d.lon, W2, H2);
1624
+ const hit = byCell.get(y * W2 + x);
1625
+ if (!hit) {
1626
+ byCell.set(y * W2 + x, { x, y, names: [d.name], color: d.color });
1627
+ } else {
1628
+ hit.names.push(d.name);
1629
+ if (hit.color !== d.color) hit.color = MIXED;
1630
+ }
1631
+ }
1632
+ hits.push(...byCell.values());
1633
+ }
1634
+ const hitRows = /* @__PURE__ */ new Map();
1635
+ for (const hit of hits) {
1636
+ const row = hitRows.get(hit.y);
1637
+ if (row) row.push(hit);
1638
+ else hitRows.set(hit.y, [hit]);
1639
+ }
1640
+ for (let y = 0; y < H2; y++) {
1641
+ const chars = new Array(W2).fill(" ");
1642
+ const colors = new Array(W2).fill(-1);
1643
+ const isDot = new Array(W2).fill(false);
1644
+ for (let x = 0; x < W2; x++) {
1645
+ if (landAt(x, y, W2, H2)) {
1646
+ chars[x] = LAND_CHAR;
1647
+ colors[x] = LAND;
1648
+ }
1649
+ }
1650
+ for (const hit of hitRows.get(y) ?? []) {
1651
+ chars[hit.x] = DOT_CHAR;
1652
+ colors[hit.x] = hit.color;
1653
+ isDot[hit.x] = true;
1654
+ }
1655
+ const spans = [];
1656
+ for (let x = 0; x < W2; ) {
1657
+ if (colors[x] < 0) {
1658
+ x++;
1659
+ continue;
1660
+ }
1661
+ let end = x + 1;
1662
+ while (end < W2 && colors[end] === colors[x] && isDot[end] === isDot[x]) end++;
1663
+ spans.push(isDot[x] ? { start: x, end, color: colors[x], bold: true } : { start: x, end, color: colors[x] });
1664
+ x = end;
1665
+ }
1666
+ lines.push({ text: chars.join("").replace(/ +$/, ""), spans });
1667
+ }
1668
+ return { lines, hits };
1669
+ }
1670
+ function hover(rendered, x, y) {
1671
+ let best = null;
1672
+ let bestDist = 2;
1673
+ for (const hit of rendered.hits) {
1674
+ const dist = Math.max(Math.abs(hit.x - x), Math.abs(hit.y - y));
1675
+ if (dist <= 1 && dist < bestDist) {
1676
+ best = hit;
1677
+ bestDist = dist;
1678
+ }
1679
+ }
1680
+ return best ? { names: best.names } : null;
1681
+ }
1682
+
1683
+ // src/geo.ts
1684
+ function localTz() {
1685
+ try {
1686
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || "";
1687
+ } catch {
1688
+ return "";
1689
+ }
1690
+ }
1691
+ var TZ = {
1692
+ // north america
1693
+ "America/New_York": [40.7, -74],
1694
+ "America/Detroit": [42.3, -83],
1695
+ "America/Toronto": [43.7, -79.4],
1696
+ "America/Chicago": [41.9, -87.6],
1697
+ "America/Winnipeg": [49.9, -97.1],
1698
+ "America/Mexico_City": [19.4, -99.1],
1699
+ "America/Denver": [39.7, -104.99],
1700
+ "America/Edmonton": [53.5, -113.5],
1701
+ "America/Phoenix": [33.4, -112.1],
1702
+ "America/Los_Angeles": [34.05, -118.24],
1703
+ "America/Vancouver": [49.3, -123.1],
1704
+ "America/Anchorage": [61.2, -149.9],
1705
+ "Pacific/Honolulu": [21.3, -157.8],
1706
+ "America/Halifax": [44.6, -63.6],
1707
+ "America/St_Johns": [47.6, -52.7],
1708
+ // south + central america
1709
+ "America/Sao_Paulo": [-23.5, -46.6],
1710
+ "America/Argentina/Buenos_Aires": [-34.6, -58.4],
1711
+ "America/Santiago": [-33.5, -70.7],
1712
+ "America/Lima": [-12, -77],
1713
+ "America/Bogota": [4.7, -74.1],
1714
+ "America/Caracas": [10.5, -66.9],
1715
+ "America/Guatemala": [14.6, -90.5],
1716
+ // europe
1717
+ "Europe/London": [51.5, -0.13],
1718
+ "Europe/Dublin": [53.3, -6.3],
1719
+ "Europe/Lisbon": [38.7, -9.1],
1720
+ "Europe/Madrid": [40.4, -3.7],
1721
+ "Europe/Paris": [48.86, 2.35],
1722
+ "Europe/Amsterdam": [52.4, 4.9],
1723
+ "Europe/Brussels": [50.8, 4.4],
1724
+ "Europe/Zurich": [47.4, 8.5],
1725
+ "Europe/Berlin": [52.5, 13.4],
1726
+ "Europe/Rome": [41.9, 12.5],
1727
+ "Europe/Vienna": [48.2, 16.4],
1728
+ "Europe/Prague": [50.1, 14.4],
1729
+ "Europe/Copenhagen": [55.7, 12.6],
1730
+ "Europe/Stockholm": [59.3, 18.1],
1731
+ "Europe/Oslo": [59.9, 10.7],
1732
+ "Europe/Warsaw": [52.2, 21],
1733
+ "Europe/Athens": [38, 23.7],
1734
+ "Europe/Bucharest": [44.4, 26.1],
1735
+ "Europe/Kyiv": [50.5, 30.5],
1736
+ "Europe/Kiev": [50.5, 30.5],
1737
+ "Europe/Istanbul": [41, 28.98],
1738
+ "Europe/Moscow": [55.75, 37.6],
1739
+ "Europe/Helsinki": [60.2, 24.9],
1740
+ // africa + middle east
1741
+ "Africa/Casablanca": [33.6, -7.6],
1742
+ "Africa/Lagos": [6.5, 3.4],
1743
+ "Africa/Cairo": [30, 31.2],
1744
+ "Africa/Nairobi": [-1.3, 36.8],
1745
+ "Africa/Johannesburg": [-26.2, 28],
1746
+ "Asia/Jerusalem": [31.8, 35.2],
1747
+ "Asia/Dubai": [25.2, 55.3],
1748
+ "Asia/Tehran": [35.7, 51.4],
1749
+ "Asia/Riyadh": [24.7, 46.7],
1750
+ "Asia/Baghdad": [33.3, 44.4],
1751
+ // asia
1752
+ "Asia/Karachi": [24.9, 67],
1753
+ "Asia/Kolkata": [22.6, 88.4],
1754
+ "Asia/Calcutta": [22.6, 88.4],
1755
+ "Asia/Colombo": [6.9, 79.9],
1756
+ "Asia/Dhaka": [23.8, 90.4],
1757
+ "Asia/Bangkok": [13.8, 100.5],
1758
+ "Asia/Jakarta": [-6.2, 106.8],
1759
+ "Asia/Singapore": [1.35, 103.8],
1760
+ "Asia/Kuala_Lumpur": [3.1, 101.7],
1761
+ "Asia/Manila": [14.6, 121],
1762
+ "Asia/Hong_Kong": [22.3, 114.2],
1763
+ "Asia/Shanghai": [31.2, 121.5],
1764
+ "Asia/Taipei": [25, 121.5],
1765
+ "Asia/Seoul": [37.6, 127],
1766
+ "Asia/Tokyo": [35.7, 139.7],
1767
+ // oceania
1768
+ "Australia/Perth": [-31.95, 115.9],
1769
+ "Australia/Adelaide": [-34.9, 138.6],
1770
+ "Australia/Brisbane": [-27.5, 153],
1771
+ "Australia/Sydney": [-33.87, 151.2],
1772
+ "Australia/Melbourne": [-37.8, 145],
1773
+ "Pacific/Auckland": [-36.85, 174.76]
1774
+ };
1775
+ var REGION = {
1776
+ America: [15, -90],
1777
+ Europe: [50, 15],
1778
+ Africa: [3, 22],
1779
+ Asia: [35, 95],
1780
+ Australia: [-25, 135],
1781
+ Pacific: [-10, -160],
1782
+ Atlantic: [35, -35],
1783
+ Indian: [-15, 75],
1784
+ Antarctica: [-80, 0]
1785
+ };
1786
+ function tzToCoords(tz) {
1787
+ if (!tz) return null;
1788
+ const exact = TZ[tz];
1789
+ if (exact) return { lat: exact[0], lon: exact[1] };
1790
+ const region = REGION[tz.split("/")[0]];
1791
+ if (region) return { lat: region[0], lon: region[1] };
1792
+ return null;
1793
+ }
1794
+ function tzCity(tz) {
1795
+ if (!tz) return "";
1796
+ const parts = tz.split("/");
1797
+ return (parts[parts.length - 1] || tz).replace(/_/g, " ");
1798
+ }
1799
+
1550
1800
  // src/client.ts
1551
1801
  var term = tk.terminal;
1552
1802
  var ScreenBuffer = tk.ScreenBuffer;
@@ -1984,7 +2234,7 @@ function connect() {
1984
2234
  state.connected = true;
1985
2235
  state.status = "";
1986
2236
  lastPong = Date.now();
1987
- sendMsg({ t: "hello", v: PROTO, user: me, executes: myAccounts.map((a) => ({ handle: a.handle, provider: a.provider, model: a.model })) });
2237
+ sendMsg({ t: "hello", v: PROTO, user: me, tz: localTz(), executes: myAccounts.map((a) => ({ handle: a.handle, provider: a.provider, model: a.model })) });
1988
2238
  if (autoJoinCode && !state.project) sendMsg({ t: "joinProject", code: autoJoinCode });
1989
2239
  else if (state.project) {
1990
2240
  rejoining = true;
@@ -2982,10 +3232,45 @@ function renderSecrets(top) {
2982
3232
  puts(2, top + 3, "shared api keys and logins land here soon.", { color: C.dim, italic: true });
2983
3233
  puts(2, top + 4, "values will be masked, reveals audited, agents only ever see names.", { color: C.dim, italic: true });
2984
3234
  }
3235
+ function mapDotsFromSessions() {
3236
+ const seen = /* @__PURE__ */ new Set();
3237
+ const dots = [];
3238
+ for (const s of state.sessions) {
3239
+ if (seen.has(s.userId)) continue;
3240
+ const coords = tzToCoords(s.tz);
3241
+ if (!coords) continue;
3242
+ seen.add(s.userId);
3243
+ dots.push({ lat: coords.lat, lon: coords.lon, name: s.name, color: s.color });
3244
+ }
3245
+ return dots;
3246
+ }
3247
+ var teamMap = null;
3248
+ function blitMapLine(x0, y, line) {
3249
+ puts(x0, y, line.text, { color: C.rule, bgColor: C.bg });
3250
+ for (const sp of line.spans) {
3251
+ const seg = [...line.text].slice(sp.start, sp.end).join("");
3252
+ if (seg) puts(x0 + sp.start, y, seg, { color: readableFg(sp.color, C.bg, 2.6), bold: true, bgColor: C.bg });
3253
+ }
3254
+ }
2985
3255
  function renderTeam(top) {
2986
3256
  if (!state.project) return;
3257
+ teamMap = null;
2987
3258
  let y = top + 1;
2988
3259
  const online = onlineUserIds();
3260
+ const availH = H() - top - 3;
3261
+ if (W() >= 48 && availH >= 22) {
3262
+ const dots = mapDotsFromSessions();
3263
+ const mapW = Math.min(W() - 4, 130);
3264
+ const mapH = Math.min(15, Math.max(8, Math.floor(availH * 0.45)));
3265
+ const title = `WHERE \xB7 ${dots.length}/${online.size} placed`;
3266
+ puts(2, y, title, { color: C.bright, bold: true });
3267
+ puts(2 + title.length + 2, y, dots.length ? "hover a dot" : "timezones load as people connect", { color: C.dim, italic: true });
3268
+ y += 1;
3269
+ const rendered = renderMap(mapW, mapH, dots);
3270
+ for (let i = 0; i < rendered.lines.length; i++) blitMapLine(2, y + i, rendered.lines[i]);
3271
+ teamMap = { rendered, x0: 2, y0: y };
3272
+ y += rendered.lines.length + 1;
3273
+ }
2989
3274
  puts(2, y, `HUMANS \xB7 ${online.size} online`, { color: C.bright, bold: true });
2990
3275
  y += 1;
2991
3276
  for (const m of state.members) {
@@ -2996,6 +3281,8 @@ function renderTeam(top) {
2996
3281
  puts(4, y, m.name + (m.id === me.id ? " \xB7 you" : ""), { color: readableFg(m.color), bold: true });
2997
3282
  const where = on ? sess.some((s) => s.tab !== "away") ? "viewing " + (sess.find((s) => s.tab !== "away")?.tab ?? "chat") : "parked" : "offline";
2998
3283
  puts(24, y, where, { color: on ? C.text : C.dim });
3284
+ const city = tzCity(sess.find((s) => s.tz)?.tz);
3285
+ if (city && W() >= 56) puts(42, y, city.slice(0, W() - 44), { color: C.dim });
2999
3286
  y++;
3000
3287
  }
3001
3288
  y += 1;
@@ -3545,6 +3832,13 @@ function onMouse(name, data) {
3545
3832
  if (name === "MOUSE_DRAG" ? data?.left : painting) paintAt(mx, my);
3546
3833
  if (name === "MOUSE_DRAG" && data?.right) eraseAt(mx, my);
3547
3834
  }
3835
+ if (state.tab === "team" && teamMap) {
3836
+ const hit = hover(teamMap.rendered, mx - teamMap.x0, my - teamMap.y0);
3837
+ if (hit) {
3838
+ const city = tzCity(state.sessions.find((s) => hit.names.includes(s.name))?.tz);
3839
+ state.status = hit.names.join(" \xB7 ") + (city ? " \xB7 " + city : "");
3840
+ }
3841
+ }
3548
3842
  return;
3549
3843
  }
3550
3844
  if (name === "MOUSE_WHEEL_UP" || name === "MOUSE_WHEEL_DOWN") {
@@ -6,7 +6,7 @@ import {
6
6
  LIMITS,
7
7
  PORT,
8
8
  PROTO
9
- } from "./chunk-CZENTCH6.js";
9
+ } from "./chunk-HF4HSRPK.js";
10
10
 
11
11
  // src/server.ts
12
12
  import { WebSocketServer, WebSocket } from "ws";
@@ -203,6 +203,7 @@ var RoomCore = class {
203
203
  color: Number(u.color) || 7,
204
204
  projectId: null,
205
205
  tab: "home",
206
+ tz: typeof msg.tz === "string" ? msg.tz.slice(0, 40) : void 0,
206
207
  executes: Array.isArray(msg.executes) ? msg.executes.map((e) => String(e?.handle || "").slice(0, 20)).filter(Boolean).slice(0, 8) : []
207
208
  };
208
209
  this.host.setSession(conn, s2);
@@ -791,7 +792,7 @@ var RoomCore = class {
791
792
  return this.roomConns(projectId).filter((c) => c !== except);
792
793
  }
793
794
  sessionsFor(projectId) {
794
- return this.roomConns(projectId).map((c) => this.host.getSession(c)).filter((s) => !!s).map((s) => ({ sid: s.sid, userId: s.userId, name: s.name, color: s.color, tab: s.tab }));
795
+ return this.roomConns(projectId).map((c) => this.host.getSession(c)).filter((s) => !!s).map((s) => ({ sid: s.sid, userId: s.userId, name: s.name, color: s.color, tab: s.tab, tz: s.tz }));
795
796
  }
796
797
  broadcastSessions(projectId) {
797
798
  const sessions = this.sessionsFor(projectId);
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  DEFAULT_SERVER,
4
4
  PORT,
5
5
  VERSION
6
- } from "./chunks/chunk-CZENTCH6.js";
6
+ } from "./chunks/chunk-HF4HSRPK.js";
7
7
 
8
8
  // src/cli.ts
9
9
  var HELP = `
@@ -52,7 +52,7 @@ async function main() {
52
52
  console.error(`invalid port: ${argVal(args, "--port")}`);
53
53
  process.exit(1);
54
54
  }
55
- const { runServer } = await import("./chunks/server-OGAKQFOU.js");
55
+ const { runServer } = await import("./chunks/server-FM2XELG5.js");
56
56
  runServer(port);
57
57
  return;
58
58
  }
@@ -76,7 +76,7 @@ async function main() {
76
76
  const { cachedUpdate, refreshUpdateCache } = await import("./chunks/update-UEPNGB6Y.js");
77
77
  refreshUpdateCache();
78
78
  const update = cachedUpdate(VERSION);
79
- const { runClient } = await import("./chunks/client-DGDZ22XC.js");
79
+ const { runClient } = await import("./chunks/client-NRBXSOP3.js");
80
80
  runClient({
81
81
  name: argVal(args, "--name"),
82
82
  server: argVal(args, "--server"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "accelr8",
3
- "version": "3.0.5",
3
+ "version": "3.1.0",
4
4
  "description": "Collaborative ascii canvas in your terminal. Real multiplayer, live cursors, mouse-driven TUI.",
5
5
  "type": "module",
6
6
  "license": "ISC",
@@ -42,7 +42,7 @@
42
42
  "chattest": "tsx src/chattest.ts",
43
43
  "inputtest": "tsx src/inputtest.ts",
44
44
  "torturetest": "tsx src/torturetest.ts",
45
- "test": "for t in core chat input ladder boot orb crypto accounts executor agentcontext map voice glyph materialize update install contrast; do tsx src/${t}test.ts || exit 1; done && tsx src/rendertest.ts > /dev/null && echo RENDER OK",
45
+ "test": "for t in core chat input ladder boot orb crypto accounts executor agentcontext map voice glyph materialize update install geo contrast; do tsx src/${t}test.ts || exit 1; done && tsx src/rendertest.ts > /dev/null && echo RENDER OK",
46
46
  "laddertest": "tsx src/laddertest.ts",
47
47
  "boottest": "tsx src/boottest.ts",
48
48
  "reconnecttest": "tsx src/reconnecttest.ts",