accelr8 3.0.4 → 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.4" : "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-I5NIANY6.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) {
@@ -1186,23 +1186,41 @@ var DRAIN_KILL_MS = 2e3;
1186
1186
  var MAX_PLAYERS = 8;
1187
1187
  var STDERR_TAIL = 400;
1188
1188
  var VERIFY_MIN_BYTES = SAMPLE_RATE * 2 / 10;
1189
- var PLAYBACK_ARGS = [
1190
- "-hide_banner",
1191
- "-loglevel",
1192
- "error",
1193
- "-nodisp",
1194
- "-sync",
1195
- "audio",
1196
- "-infbuf",
1197
- "-f",
1198
- "s16le",
1199
- "-ar",
1200
- String(SAMPLE_RATE),
1201
- "-ch_layout",
1202
- "mono",
1203
- "-i",
1204
- "-"
1205
- ];
1189
+ function playbackArgs(ffplayPath) {
1190
+ const ch = ffplayChannelFlag(ffplayPath);
1191
+ return [
1192
+ "-hide_banner",
1193
+ "-loglevel",
1194
+ "error",
1195
+ "-nodisp",
1196
+ "-sync",
1197
+ "audio",
1198
+ "-infbuf",
1199
+ "-f",
1200
+ "s16le",
1201
+ "-ar",
1202
+ String(SAMPLE_RATE),
1203
+ ...ch,
1204
+ "-i",
1205
+ "-"
1206
+ ];
1207
+ }
1208
+ var chFlagCache = null;
1209
+ function ffplayChannelFlag(ffplayPath) {
1210
+ if (chFlagCache) return chFlagCache;
1211
+ let modern = true;
1212
+ try {
1213
+ const out = spawnSync(ffplayPath, ["-hide_banner", "-version"], { encoding: "utf8", timeout: 3e3 });
1214
+ const m = ((out.stdout || "") + (out.stderr || "")).match(/version\s+n?(\d+)\.(\d+)/i);
1215
+ if (m) {
1216
+ const major = +m[1], minor = +m[2];
1217
+ modern = major > 5 || major === 5 && minor >= 1;
1218
+ }
1219
+ } catch {
1220
+ }
1221
+ chFlagCache = modern ? ["-ch_layout", "mono"] : ["-ac", "1"];
1222
+ return chFlagCache;
1223
+ }
1206
1224
  function detectVoiceTools() {
1207
1225
  const ffmpegPath = which("ffmpeg");
1208
1226
  const ffplayPath = which("ffplay");
@@ -1422,7 +1440,7 @@ var VoicePlayback = class {
1422
1440
  spawnPlayer(speakerId) {
1423
1441
  let child;
1424
1442
  try {
1425
- child = spawn(this.tools.ffplayPath, PLAYBACK_ARGS, { stdio: ["pipe", "ignore", "ignore"] });
1443
+ child = spawn(this.tools.ffplayPath, playbackArgs(this.tools.ffplayPath), { stdio: ["pipe", "ignore", "ignore"] });
1426
1444
  } catch {
1427
1445
  return null;
1428
1446
  }
@@ -1529,6 +1547,256 @@ function errMsg(e) {
1529
1547
  return e instanceof Error ? e.message : String(e);
1530
1548
  }
1531
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
+
1532
1800
  // src/client.ts
1533
1801
  var term = tk.terminal;
1534
1802
  var ScreenBuffer = tk.ScreenBuffer;
@@ -1657,13 +1925,19 @@ var voicePlayback = null;
1657
1925
  var voiceDevice = process.env.ACCELR8_MIC || void 0;
1658
1926
  var voiceRetries = 0;
1659
1927
  function autoPickMic() {
1660
- if (process.platform !== "darwin") return void 0;
1661
- const mics = listMicDevices();
1662
- if (!mics.length) return void 0;
1663
- const builtin = mics.find((m) => /macbook|built.?in|internal/i.test(m.name));
1664
- if (builtin) return builtin.index;
1665
- const notPhone = mics.find((m) => !/iphone|ipad|continuity|desk view/i.test(m.name));
1666
- return notPhone?.index;
1928
+ if (process.platform === "darwin") {
1929
+ const mics = listMicDevices();
1930
+ if (!mics.length) return void 0;
1931
+ const builtin = mics.find((m) => /macbook|built.?in|internal/i.test(m.name));
1932
+ if (builtin) return builtin.index;
1933
+ const notPhone = mics.find((m) => !/iphone|ipad|continuity|desk view/i.test(m.name));
1934
+ return notPhone?.index;
1935
+ }
1936
+ if (process.platform === "win32") {
1937
+ const mics = listMicDevices();
1938
+ return mics[0]?.index;
1939
+ }
1940
+ return void 0;
1667
1941
  }
1668
1942
  function startCapture() {
1669
1943
  if (!voiceCapture) return;
@@ -1960,7 +2234,7 @@ function connect() {
1960
2234
  state.connected = true;
1961
2235
  state.status = "";
1962
2236
  lastPong = Date.now();
1963
- 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 })) });
1964
2238
  if (autoJoinCode && !state.project) sendMsg({ t: "joinProject", code: autoJoinCode });
1965
2239
  else if (state.project) {
1966
2240
  rejoining = true;
@@ -2958,10 +3232,45 @@ function renderSecrets(top) {
2958
3232
  puts(2, top + 3, "shared api keys and logins land here soon.", { color: C.dim, italic: true });
2959
3233
  puts(2, top + 4, "values will be masked, reveals audited, agents only ever see names.", { color: C.dim, italic: true });
2960
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
+ }
2961
3255
  function renderTeam(top) {
2962
3256
  if (!state.project) return;
3257
+ teamMap = null;
2963
3258
  let y = top + 1;
2964
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
+ }
2965
3274
  puts(2, y, `HUMANS \xB7 ${online.size} online`, { color: C.bright, bold: true });
2966
3275
  y += 1;
2967
3276
  for (const m of state.members) {
@@ -2972,6 +3281,8 @@ function renderTeam(top) {
2972
3281
  puts(4, y, m.name + (m.id === me.id ? " \xB7 you" : ""), { color: readableFg(m.color), bold: true });
2973
3282
  const where = on ? sess.some((s) => s.tab !== "away") ? "viewing " + (sess.find((s) => s.tab !== "away")?.tab ?? "chat") : "parked" : "offline";
2974
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 });
2975
3286
  y++;
2976
3287
  }
2977
3288
  y += 1;
@@ -3521,6 +3832,13 @@ function onMouse(name, data) {
3521
3832
  if (name === "MOUSE_DRAG" ? data?.left : painting) paintAt(mx, my);
3522
3833
  if (name === "MOUSE_DRAG" && data?.right) eraseAt(mx, my);
3523
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
+ }
3524
3842
  return;
3525
3843
  }
3526
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-I5NIANY6.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-I5NIANY6.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-QZIOVRDR.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-BVLUBSS7.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.4",
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",