let-them-talk 3.6.2 → 3.8.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.
- package/CHANGELOG.md +71 -0
- package/README.md +87 -3
- package/cli.js +1 -1
- package/dashboard.html +7480 -7399
- package/dashboard.js +8 -3
- package/office/agents.js +8 -5
- package/office/animation.js +2 -1
- package/office/campus-env.js +41 -19
- package/office/environment.js +60 -67
- package/office/index.js +50 -0
- package/office/monitors.js +12 -3
- package/office/player.js +436 -0
- package/office/spectator-camera.js +30 -21
- package/package.json +1 -1
- package/server.js +1114 -52
package/dashboard.js
CHANGED
|
@@ -284,17 +284,22 @@ function apiStats(query) {
|
|
|
284
284
|
const history = readJsonl(filePath('history.jsonl', projectPath));
|
|
285
285
|
const agents = readJson(filePath('agents.json', projectPath));
|
|
286
286
|
|
|
287
|
-
// Per-agent stats
|
|
287
|
+
// Per-agent stats — only count messages from agents still in agents.json
|
|
288
288
|
const perAgent = {};
|
|
289
|
-
|
|
289
|
+
const knownAgentNames = new Set(Object.keys(agents));
|
|
290
|
+
knownAgentNames.add('__system__');
|
|
291
|
+
knownAgentNames.add('Dashboard');
|
|
292
|
+
let totalMessages = 0;
|
|
290
293
|
const hourBuckets = new Array(24).fill(0);
|
|
291
294
|
|
|
292
295
|
for (let i = 0; i < history.length; i++) {
|
|
293
296
|
const m = history[i];
|
|
294
297
|
const from = m.from || 'unknown';
|
|
298
|
+
if (!knownAgentNames.has(from)) continue; // skip removed agents
|
|
295
299
|
if (!perAgent[from]) {
|
|
296
300
|
perAgent[from] = { messages: 0, responseTimes: [], hours: new Array(24).fill(0) };
|
|
297
301
|
}
|
|
302
|
+
totalMessages++;
|
|
298
303
|
perAgent[from].messages++;
|
|
299
304
|
const ts = new Date(m.timestamp);
|
|
300
305
|
const hour = ts.getHours();
|
|
@@ -313,7 +318,7 @@ function apiStats(query) {
|
|
|
313
318
|
}
|
|
314
319
|
}
|
|
315
320
|
|
|
316
|
-
// Build per-agent summary
|
|
321
|
+
// Build per-agent summary — only include agents currently in agents.json
|
|
317
322
|
const agentStats = {};
|
|
318
323
|
let busiestAgent = null;
|
|
319
324
|
let busiestCount = 0;
|
package/office/agents.js
CHANGED
|
@@ -156,11 +156,14 @@ function updateDeskScreen(deskIdx, status, isListening) {
|
|
|
156
156
|
function flashDeskScreen(deskIdx) {
|
|
157
157
|
var desk = S.deskMeshes[deskIdx];
|
|
158
158
|
if (!desk) return;
|
|
159
|
+
// Flash white briefly — the next syncAgents call (every 2s) will set the correct persistent color via updateDeskScreen
|
|
159
160
|
desk.screenMat.emissive.setHex(0xffffff);
|
|
160
161
|
desk.screenMat.emissiveIntensity = 1.5;
|
|
161
162
|
setTimeout(function() {
|
|
162
|
-
|
|
163
|
-
desk.screenMat.
|
|
163
|
+
// Force immediate red until next sync corrects it
|
|
164
|
+
desk.screenMat.emissive.setHex(0xef4444);
|
|
165
|
+
desk.screenMat.emissiveIntensity = 0.6;
|
|
166
|
+
desk.screenMat.color.setHex(0xef4444);
|
|
164
167
|
}, 300);
|
|
165
168
|
}
|
|
166
169
|
|
|
@@ -292,14 +295,14 @@ export function syncAgents() {
|
|
|
292
295
|
var wasListening = existing.isListening;
|
|
293
296
|
existing.isListening = !!(info.is_listening);
|
|
294
297
|
|
|
295
|
-
// Detect listen mode change
|
|
298
|
+
// Detect listen mode change — update screen color persistently
|
|
296
299
|
if (wasListening && !existing.isListening) {
|
|
297
|
-
// Left listen mode — flash
|
|
300
|
+
// Left listen mode — flash then stay red until next sync sets updateDeskScreen
|
|
298
301
|
existing.listenLostTimer = 3;
|
|
299
302
|
flashDeskScreen(existing.deskIdx);
|
|
300
303
|
}
|
|
301
304
|
if (!wasListening && existing.isListening) {
|
|
302
|
-
// Entered listen mode
|
|
305
|
+
// Entered listen mode — next updateDeskScreen will set green
|
|
303
306
|
existing.listenLostTimer = 0;
|
|
304
307
|
}
|
|
305
308
|
|
package/office/animation.js
CHANGED
|
@@ -154,7 +154,7 @@ export function updateAgent(agent, dt, time) {
|
|
|
154
154
|
var sittingTarget = agent.isSitting ? 1 : 0;
|
|
155
155
|
agent.sittingLerp += (sittingTarget - agent.sittingLerp) * Math.min(1, dt * 5);
|
|
156
156
|
|
|
157
|
-
agent.parts.group.position.y = agent.sittingLerp * 0.
|
|
157
|
+
agent.parts.group.position.y = agent.sittingLerp * 0.14;
|
|
158
158
|
var sitHip = -1.5 * agent.sittingLerp;
|
|
159
159
|
agent.parts.leftLeg.rotation.x = agent.parts.leftLeg.rotation.x * (1 - agent.sittingLerp) + sitHip * agent.sittingLerp;
|
|
160
160
|
agent.parts.rightLeg.rotation.x = agent.parts.rightLeg.rotation.x * (1 - agent.sittingLerp) + sitHip * agent.sittingLerp;
|
|
@@ -368,4 +368,5 @@ function disposeAgent(agent) {
|
|
|
368
368
|
if (agent.parts.zzzObjects) {
|
|
369
369
|
agent.parts.zzzObjects.forEach(function(z) { if (z.div.parentElement) z.div.remove(); });
|
|
370
370
|
}
|
|
371
|
+
if (agent._listenLostDiv && agent._listenLostDiv.parentElement) agent._listenLostDiv.remove();
|
|
371
372
|
}
|
package/office/campus-env.js
CHANGED
|
@@ -446,23 +446,45 @@ function buildLobby(marbleMat, chromeMat, goldMat, walnutMat) {
|
|
|
446
446
|
kb.position.set(-0.8, 1.2, lz - 0.4);
|
|
447
447
|
group.add(kb);
|
|
448
448
|
|
|
449
|
-
// ---
|
|
449
|
+
// --- Feature wall with big TV monitor (behind reception) ---
|
|
450
450
|
var logoWallMat = new THREE.MeshStandardMaterial({ color: 0x15181f, roughness: 0.7 });
|
|
451
|
-
var logoWall = new THREE.Mesh(new THREE.BoxGeometry(6,
|
|
452
|
-
logoWall.position.set(0, 2, lz + 1.5);
|
|
451
|
+
var logoWall = new THREE.Mesh(new THREE.BoxGeometry(6, 4, 0.15), logoWallMat);
|
|
452
|
+
logoWall.position.set(0, 2.5, lz + 1.5);
|
|
453
453
|
logoWall.castShadow = true;
|
|
454
454
|
group.add(logoWall);
|
|
455
|
-
|
|
455
|
+
|
|
456
|
+
// "LET THEM TALK" logo text above the TV
|
|
456
457
|
var logoDiv = document.createElement('div');
|
|
457
458
|
logoDiv.textContent = 'LET THEM TALK';
|
|
458
|
-
logoDiv.style.cssText = 'color:#ffffff;font-size:
|
|
459
|
+
logoDiv.style.cssText = 'color:#ffffff;font-size:14px;font-weight:900;font-family:Inter,sans-serif;letter-spacing:6px;text-shadow:0 0 20px rgba(88,166,255,0.6),0 0 40px rgba(88,166,255,0.3);';
|
|
459
460
|
var logoLabel = new CSS2DObject(logoDiv);
|
|
460
|
-
logoLabel.position.set(0,
|
|
461
|
+
logoLabel.position.set(0, 4.3, lz + 1.6);
|
|
461
462
|
group.add(logoLabel);
|
|
462
|
-
|
|
463
|
-
|
|
463
|
+
|
|
464
|
+
// Big TV screen (dynamic canvas dashboard) — facing INTO the room (-z)
|
|
465
|
+
var tvFrame = new THREE.Mesh(new THREE.BoxGeometry(5, 2.8, 0.06),
|
|
466
|
+
new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 }));
|
|
467
|
+
tvFrame.position.set(0, 2.2, lz + 1.4);
|
|
468
|
+
tvFrame.castShadow = true;
|
|
469
|
+
group.add(tvFrame);
|
|
470
|
+
// Animated canvas
|
|
471
|
+
var tvW = 960, tvH = 600;
|
|
472
|
+
var tvCvs = document.createElement('canvas');
|
|
473
|
+
tvCvs.width = tvW; tvCvs.height = tvH;
|
|
474
|
+
var tvTex = new THREE.CanvasTexture(tvCvs);
|
|
475
|
+
tvTex.minFilter = THREE.LinearFilter;
|
|
476
|
+
var tvScreenMat = new THREE.MeshStandardMaterial({
|
|
477
|
+
map: tvTex, emissive: 0x58a6ff, emissiveIntensity: 0.2, roughness: 0.1
|
|
478
|
+
});
|
|
479
|
+
var tvScreen = new THREE.Mesh(new THREE.PlaneGeometry(4.6, 2.5), tvScreenMat);
|
|
480
|
+
tvScreen.position.set(0, 2.2, lz + 1.36);
|
|
481
|
+
tvScreen.rotation.y = Math.PI;
|
|
482
|
+
group.add(tvScreen);
|
|
483
|
+
S._tvScreen = { canvas: tvCvs, texture: tvTex, tickerOffset: 0 };
|
|
484
|
+
|
|
485
|
+
// Accent light on the wall
|
|
464
486
|
var logoSpot = new THREE.PointLight(0x58a6ff, 0.5, 6);
|
|
465
|
-
logoSpot.position.set(0,
|
|
487
|
+
logoSpot.position.set(0, 4.2, lz + 1);
|
|
466
488
|
group.add(logoSpot);
|
|
467
489
|
|
|
468
490
|
// --- Water feature (low rectangular pool) ---
|
|
@@ -1241,16 +1263,16 @@ function buildRecCenter(x, z, walnutMat, chromeMat, carpetMat) {
|
|
|
1241
1263
|
S.furnitureGroup.add(bot);
|
|
1242
1264
|
});
|
|
1243
1265
|
|
|
1244
|
-
//
|
|
1245
|
-
var
|
|
1246
|
-
var
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
S.furnitureGroup.add(
|
|
1250
|
-
var
|
|
1251
|
-
new THREE.MeshStandardMaterial({ color:
|
|
1252
|
-
|
|
1253
|
-
S.furnitureGroup.add(
|
|
1266
|
+
// Static decorative TV (smaller, no dashboard — main TV is at reception)
|
|
1267
|
+
var tvMat2 = new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 });
|
|
1268
|
+
var tvBody = new THREE.Mesh(new THREE.BoxGeometry(2.5, 1.5, 0.08), tvMat2);
|
|
1269
|
+
tvBody.position.set(x, 2.3, z - 3.8);
|
|
1270
|
+
tvBody.castShadow = true;
|
|
1271
|
+
S.furnitureGroup.add(tvBody);
|
|
1272
|
+
var tvScr = new THREE.Mesh(new THREE.PlaneGeometry(2.3, 1.3),
|
|
1273
|
+
new THREE.MeshStandardMaterial({ color: 0x0a1520, emissive: 0x22c55e, emissiveIntensity: 0.15, roughness: 0.1 }));
|
|
1274
|
+
tvScr.position.set(x, 2.3, z - 3.75);
|
|
1275
|
+
S.furnitureGroup.add(tvScr);
|
|
1254
1276
|
|
|
1255
1277
|
// "REC ZONE" sign
|
|
1256
1278
|
var signDiv = document.createElement('div');
|
package/office/environment.js
CHANGED
|
@@ -425,94 +425,87 @@ export function updateTVScreen(time) {
|
|
|
425
425
|
var cvs = tv.canvas, ctx = cvs.getContext('2d');
|
|
426
426
|
var W = cvs.width, H = cvs.height;
|
|
427
427
|
|
|
428
|
-
//
|
|
428
|
+
// Clear
|
|
429
429
|
ctx.fillStyle = '#0a0e1a';
|
|
430
430
|
ctx.fillRect(0, 0, W, H);
|
|
431
431
|
|
|
432
432
|
// Top bar
|
|
433
433
|
ctx.fillStyle = '#111830';
|
|
434
|
-
ctx.fillRect(0, 0, W,
|
|
434
|
+
ctx.fillRect(0, 0, W, 40);
|
|
435
435
|
ctx.fillStyle = '#58a6ff';
|
|
436
|
-
ctx.font = 'bold
|
|
437
|
-
ctx.fillText('OFFICE DASHBOARD',
|
|
438
|
-
// Clock
|
|
436
|
+
ctx.font = 'bold 20px monospace';
|
|
437
|
+
ctx.fillText('OFFICE DASHBOARD', 16, 27);
|
|
439
438
|
var now = new Date();
|
|
440
439
|
var timeStr = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0') + ':' + now.getSeconds().toString().padStart(2, '0');
|
|
441
440
|
ctx.fillStyle = '#7ee787';
|
|
442
441
|
ctx.textAlign = 'right';
|
|
443
|
-
ctx.fillText(timeStr, W -
|
|
442
|
+
ctx.fillText(timeStr, W - 16, 27);
|
|
444
443
|
ctx.textAlign = 'left';
|
|
445
444
|
|
|
446
|
-
//
|
|
445
|
+
// Data
|
|
447
446
|
var agents = window.cachedAgents || {};
|
|
448
447
|
var history = window.cachedHistory || [];
|
|
449
448
|
var agentNames = Object.keys(agents);
|
|
450
449
|
var activeCount = 0, sleepCount = 0;
|
|
451
450
|
agentNames.forEach(function(n) {
|
|
452
|
-
|
|
453
|
-
if (
|
|
454
|
-
else if (st === 'sleeping') sleepCount++;
|
|
451
|
+
if (agents[n].status === 'active') activeCount++;
|
|
452
|
+
else if (agents[n].status === 'sleeping') sleepCount++;
|
|
455
453
|
});
|
|
456
454
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
// Stats
|
|
461
|
-
ctx.fillStyle = '#546178';
|
|
462
|
-
ctx.fillText(
|
|
463
|
-
ctx.fillStyle = '#
|
|
464
|
-
ctx.fillText(
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
ctx.
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
ctx.
|
|
474
|
-
ctx.
|
|
475
|
-
y +=
|
|
476
|
-
|
|
477
|
-
//
|
|
478
|
-
ctx.
|
|
479
|
-
ctx.
|
|
480
|
-
y +=
|
|
481
|
-
|
|
482
|
-
//
|
|
483
|
-
ctx.
|
|
484
|
-
|
|
485
|
-
ctx.fillText('RECENT ACTIVITY', 8, y);
|
|
486
|
-
y += 14;
|
|
487
|
-
|
|
488
|
-
var recentMsgs = history.slice(-5);
|
|
455
|
+
var y = 68;
|
|
456
|
+
ctx.font = '16px monospace';
|
|
457
|
+
|
|
458
|
+
// Stats row 1
|
|
459
|
+
ctx.fillStyle = '#546178'; ctx.fillText('AGENTS', 16, y);
|
|
460
|
+
ctx.fillStyle = '#d2a8ff'; ctx.fillText(String(agentNames.length), 110, y);
|
|
461
|
+
ctx.fillStyle = '#4ade80'; ctx.fillText(activeCount + ' active', 140, y);
|
|
462
|
+
ctx.fillStyle = '#facc15'; ctx.fillText(sleepCount + ' idle', 280, y);
|
|
463
|
+
y += 26;
|
|
464
|
+
|
|
465
|
+
// Stats row 2
|
|
466
|
+
ctx.fillStyle = '#546178'; ctx.fillText('MESSAGES', 16, y);
|
|
467
|
+
ctx.fillStyle = '#79c0ff'; ctx.fillText(String(history.length), 130, y);
|
|
468
|
+
y += 26;
|
|
469
|
+
|
|
470
|
+
// Separator
|
|
471
|
+
ctx.strokeStyle = '#1a2744'; ctx.lineWidth = 1;
|
|
472
|
+
ctx.beginPath(); ctx.moveTo(16, y); ctx.lineTo(W - 16, y); ctx.stroke();
|
|
473
|
+
y += 20;
|
|
474
|
+
|
|
475
|
+
// Activity header
|
|
476
|
+
ctx.fillStyle = '#546178'; ctx.font = '14px monospace';
|
|
477
|
+
ctx.fillText('RECENT ACTIVITY', 16, y);
|
|
478
|
+
y += 22;
|
|
479
|
+
|
|
480
|
+
// Messages
|
|
481
|
+
ctx.font = '13px monospace';
|
|
482
|
+
var recentMsgs = history.slice(-7);
|
|
489
483
|
for (var i = 0; i < recentMsgs.length; i++) {
|
|
484
|
+
if (y > H - 50) break;
|
|
490
485
|
var msg = recentMsgs[i];
|
|
491
486
|
var from = msg.from || '?';
|
|
492
|
-
var
|
|
493
|
-
var
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
ctx.
|
|
499
|
-
|
|
500
|
-
ctx.
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
487
|
+
var to2 = msg.to || 'all';
|
|
488
|
+
var content = msg.content || msg.message || '';
|
|
489
|
+
var maxLen = Math.floor((W - 40) / 7.5);
|
|
490
|
+
var snippet = content.length > maxLen ? content.substring(0, maxLen - 2) + '..' : content;
|
|
491
|
+
|
|
492
|
+
// From > To
|
|
493
|
+
ctx.fillStyle = '#7ee787'; ctx.fillText(from, 16, y);
|
|
494
|
+
var fromW = ctx.measureText(from).width;
|
|
495
|
+
ctx.fillStyle = '#546178'; ctx.fillText(' > ', 16 + fromW, y);
|
|
496
|
+
ctx.fillStyle = '#d2a8ff'; ctx.fillText(to2, 16 + fromW + ctx.measureText(' > ').width, y);
|
|
497
|
+
y += 18;
|
|
498
|
+
// Snippet
|
|
499
|
+
ctx.fillStyle = '#8892b0'; ctx.fillText(' ' + snippet, 16, y);
|
|
500
|
+
y += 22;
|
|
506
501
|
}
|
|
507
502
|
if (recentMsgs.length === 0) {
|
|
508
|
-
ctx.fillStyle = '#3d4663';
|
|
509
|
-
ctx.fillText(' Waiting for messages...', 8, y);
|
|
503
|
+
ctx.fillStyle = '#3d4663'; ctx.fillText(' Waiting for messages...', 16, y);
|
|
510
504
|
}
|
|
511
505
|
|
|
512
|
-
// Bottom ticker
|
|
506
|
+
// Bottom ticker
|
|
513
507
|
ctx.fillStyle = '#111830';
|
|
514
|
-
ctx.fillRect(0, H -
|
|
515
|
-
// Scrolling ticker
|
|
508
|
+
ctx.fillRect(0, H - 32, W, 32);
|
|
516
509
|
var tickerParts = [];
|
|
517
510
|
agentNames.forEach(function(n) {
|
|
518
511
|
var info = agents[n];
|
|
@@ -520,13 +513,13 @@ export function updateTVScreen(time) {
|
|
|
520
513
|
tickerParts.push(st + ' ' + (info.display_name || n));
|
|
521
514
|
});
|
|
522
515
|
var tickerText = tickerParts.length > 0 ? tickerParts.join(' \u2022 ') + ' \u2022 ' : 'No agents online';
|
|
523
|
-
|
|
516
|
+
ctx.font = '15px monospace';
|
|
517
|
+
var charW = 9;
|
|
518
|
+
tv.tickerOffset = (tv.tickerOffset + 1.5) % (tickerText.length * charW);
|
|
524
519
|
ctx.fillStyle = '#58a6ff';
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
ctx.fillText(tickerText, -tv.tickerOffset, H - 6);
|
|
529
|
-
ctx.fillText(tickerText, -tv.tickerOffset + fullW, H - 6);
|
|
520
|
+
var fullTW = tickerText.length * charW;
|
|
521
|
+
ctx.fillText(tickerText, -tv.tickerOffset, H - 10);
|
|
522
|
+
ctx.fillText(tickerText, -tv.tickerOffset + fullTW, H - 10);
|
|
530
523
|
|
|
531
524
|
// Scanline overlay
|
|
532
525
|
ctx.fillStyle = 'rgba(0,0,0,0.04)';
|
package/office/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { updateAgent } from './animation.js';
|
|
|
8
8
|
import { syncAgents, processMessages, walkTo, navigateTo, showBubble } from './agents.js';
|
|
9
9
|
// Side-effect: registers window.officeGetAppearance
|
|
10
10
|
import './appearance.js';
|
|
11
|
+
import { spawnPlayer, despawnPlayer, isPlayerMode, updatePlayer, savePlayerAppearance, getPlayerAppearance, getPlayer, invalidateColliders } from './player.js';
|
|
11
12
|
|
|
12
13
|
// Expose createCharacter + resolveAppearance for the character designer (Phase 3)
|
|
13
14
|
export { createCharacter } from './character.js';
|
|
@@ -226,6 +227,11 @@ function animate() {
|
|
|
226
227
|
updateAgent(S.agents3d[name], dt, time);
|
|
227
228
|
}
|
|
228
229
|
|
|
230
|
+
// Player avatar mode
|
|
231
|
+
if (isPlayerMode() && S.controls && S.controls.keys) {
|
|
232
|
+
updatePlayer(dt, time, S.controls.keys);
|
|
233
|
+
}
|
|
234
|
+
|
|
229
235
|
// Hide roof when camera is above ceiling height
|
|
230
236
|
if (S._roofGroup) {
|
|
231
237
|
S._roofGroup.visible = S.camera.position.y < 6.5;
|
|
@@ -244,6 +250,12 @@ function animate() {
|
|
|
244
250
|
if (Math.sqrt(adx * adx + adz * adz) < 3) { shouldOpen = true; break; }
|
|
245
251
|
}
|
|
246
252
|
}
|
|
253
|
+
// Also open for player
|
|
254
|
+
if (!shouldOpen && S._player) {
|
|
255
|
+
var pdx = S._player.pos.x - doorX;
|
|
256
|
+
var pdz = S._player.pos.z - doorZ;
|
|
257
|
+
if (Math.sqrt(pdx * pdx + pdz * pdz) < 3) shouldOpen = true;
|
|
258
|
+
}
|
|
247
259
|
S._managerDoorOpen = shouldOpen ? 1 : 0;
|
|
248
260
|
S._managerDoorLerp += (S._managerDoorOpen - S._managerDoorLerp) * Math.min(1, dt * 4);
|
|
249
261
|
S._managerDoor.position.x = S._managerDoorLerp * 1.3; // slide open to the right
|
|
@@ -345,7 +357,15 @@ window.office3dSetEnvironment = function(env) {
|
|
|
345
357
|
});
|
|
346
358
|
}
|
|
347
359
|
S.agents3d = {};
|
|
360
|
+
S._tvScreen = null;
|
|
361
|
+
S._roofGroup = null;
|
|
362
|
+
S._managerDoor = null;
|
|
363
|
+
S._managerDoorOpen = 0;
|
|
364
|
+
S._managerDoorLerp = 0;
|
|
365
|
+
S._managerOfficePos = null;
|
|
366
|
+
S._campusDeskPositions = null;
|
|
348
367
|
S.lastProcessedMsg = 0;
|
|
368
|
+
invalidateColliders();
|
|
349
369
|
buildEnvironment();
|
|
350
370
|
// syncAgents will recreate all agents with correct desk assignments
|
|
351
371
|
syncAgents();
|
|
@@ -357,6 +377,36 @@ window.office3dSetCamSpeed = function(speed) {
|
|
|
357
377
|
if (S.controls) S.controls.moveSpeed = speed;
|
|
358
378
|
};
|
|
359
379
|
|
|
380
|
+
// Player avatar API
|
|
381
|
+
window.office3dEnterWorld = function() {
|
|
382
|
+
spawnPlayer();
|
|
383
|
+
};
|
|
384
|
+
window.office3dExitWorld = function() {
|
|
385
|
+
despawnPlayer();
|
|
386
|
+
};
|
|
387
|
+
window.office3dIsPlayerMode = function() {
|
|
388
|
+
return isPlayerMode();
|
|
389
|
+
};
|
|
390
|
+
window.office3dSavePlayerAppearance = function(app) {
|
|
391
|
+
savePlayerAppearance(app);
|
|
392
|
+
};
|
|
393
|
+
window.office3dGetPlayerAppearance = function() {
|
|
394
|
+
return getPlayerAppearance();
|
|
395
|
+
};
|
|
396
|
+
window.office3dRebuildPlayer = function(appearance) {
|
|
397
|
+
if (!S._player) return;
|
|
398
|
+
savePlayerAppearance(appearance);
|
|
399
|
+
// Rebuild: despawn and respawn with new appearance
|
|
400
|
+
var pos = { x: S._player.pos.x, z: S._player.pos.z };
|
|
401
|
+
var facing = S._player.facing;
|
|
402
|
+
despawnPlayer();
|
|
403
|
+
var p = spawnPlayer();
|
|
404
|
+
p.pos.x = pos.x;
|
|
405
|
+
p.pos.z = pos.z;
|
|
406
|
+
p.facing = facing;
|
|
407
|
+
p.parts.group.position.set(pos.x, 0, pos.z);
|
|
408
|
+
};
|
|
409
|
+
|
|
360
410
|
// Handle visibility change for 3D mode
|
|
361
411
|
document.addEventListener('visibilitychange', function() {
|
|
362
412
|
if (document.hidden && S.running) {
|
package/office/monitors.js
CHANGED
|
@@ -3,7 +3,7 @@ import { S } from './state.js';
|
|
|
3
3
|
|
|
4
4
|
export function updateMonitorScreen(deskIdx, agentName, time) {
|
|
5
5
|
var desk = S.deskMeshes[deskIdx];
|
|
6
|
-
if (!desk) return;
|
|
6
|
+
if (!desk || !desk.screen) return;
|
|
7
7
|
var W = 256, H = 160;
|
|
8
8
|
if (!S.monitorCanvases[deskIdx]) {
|
|
9
9
|
var cvs = document.createElement('canvas');
|
|
@@ -37,7 +37,16 @@ export function updateMonitorScreen(deskIdx, agentName, time) {
|
|
|
37
37
|
var agentInfo = (window.cachedAgents || {})[agentName] || {};
|
|
38
38
|
var lines = [];
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
// Prominent warning when agent is NOT listening
|
|
41
|
+
if (agentInfo.status === 'active' && !agentInfo.is_listening) {
|
|
42
|
+
ctx.fillStyle = '#1a0808';
|
|
43
|
+
ctx.fillRect(0, 14, W, 14);
|
|
44
|
+
ctx.fillStyle = '#ef4444';
|
|
45
|
+
ctx.font = 'bold 10px monospace';
|
|
46
|
+
ctx.fillText('\u26A0 NOT LISTENING', 6, 25);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
var statusColor = agentInfo.is_listening ? '#28c840' : agentInfo.status === 'active' ? '#ef4444' : '#ffbd2e';
|
|
41
50
|
lines.push({ color: '#546178', text: '$ agent status' });
|
|
42
51
|
lines.push({ color: statusColor, text: ' ' + (agentInfo.status || 'unknown').toUpperCase() + (agentInfo.is_listening ? ' (listening)' : ' (working)') });
|
|
43
52
|
lines.push({ color: '#546178', text: '' });
|
|
@@ -100,7 +109,7 @@ export function updateMonitorScreen(deskIdx, agentName, time) {
|
|
|
100
109
|
|
|
101
110
|
export function setMonitorDim(deskIdx) {
|
|
102
111
|
var desk = S.deskMeshes[deskIdx];
|
|
103
|
-
if (!desk) return;
|
|
112
|
+
if (!desk || !desk.screen) return;
|
|
104
113
|
if (S.monitorCanvases[deskIdx]) {
|
|
105
114
|
S.monitorCanvases[deskIdx].texture.dispose();
|
|
106
115
|
if (desk.screen.material !== desk.screenMat) desk.screen.material.dispose();
|