dsh-agent-office 4.2.0 → 4.5.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/README.md +8 -3
- package/lib/client.js +52 -8
- package/lib/panel.html +302 -238
- package/package.json +2 -1
- package/panel-src/assets/exp-slits.png +0 -0
- package/panel-src/index.html +118 -54
- package/panel-src/main.js +428 -20
- package/screenshot-day.png +0 -0
- package/screenshot-focus.png +0 -0
- package/screenshot.png +0 -0
package/panel-src/main.js
CHANGED
|
@@ -13,6 +13,7 @@ import faceDizzy from './assets/exp-dizzy.png'
|
|
|
13
13
|
import faceSurprised from './assets/exp-surprised.png'
|
|
14
14
|
import faceThinking from './assets/exp-thinking.png'
|
|
15
15
|
import faceLoved from './assets/exp-loved.png'
|
|
16
|
+
import faceSlits from './assets/exp-slits.png'
|
|
16
17
|
|
|
17
18
|
const API = '/agent-office/api/state'
|
|
18
19
|
const FLOOR_GAP = 4.0
|
|
@@ -285,7 +286,7 @@ function applyThemeFrame() {
|
|
|
285
286
|
for (const material of cityMats) material.color.copy(NIGHT_THEME.city).lerp(DAY_THEME.city, mix)
|
|
286
287
|
const neonMaterial = neonSign?.material
|
|
287
288
|
if (neonMaterial) neonMaterial.opacity = NIGHT_THEME.neon + (DAY_THEME.neon - NIGHT_THEME.neon) * mix
|
|
288
|
-
for (const item of themeLights) item.light.intensity = item.night + (item.day - item.night) * mix
|
|
289
|
+
for (const item of [...themeLights, ...annexLights]) item.light.intensity = item.night + (item.day - item.night) * mix
|
|
289
290
|
}
|
|
290
291
|
|
|
291
292
|
function syncThemeByClock(immediate = false) {
|
|
@@ -350,6 +351,7 @@ const FACE_SRC = {
|
|
|
350
351
|
surprised: faceSurprised,
|
|
351
352
|
thinking: faceThinking,
|
|
352
353
|
loved: faceLoved,
|
|
354
|
+
slits: faceSlits,
|
|
353
355
|
}
|
|
354
356
|
const faceTextures = {}
|
|
355
357
|
const faceLoader = new THREE.TextureLoader()
|
|
@@ -359,13 +361,15 @@ for (const [name, src] of Object.entries(FACE_SRC)) {
|
|
|
359
361
|
faceTextures[name] = texture
|
|
360
362
|
}
|
|
361
363
|
const CUSTOM_FACE_KEY = 'agent-office-custom-face'
|
|
364
|
+
const GLASS_KEY = 'agent-office-ui-opacity'
|
|
362
365
|
let customFaceTexture = null
|
|
366
|
+
let customFaceData = ''
|
|
363
367
|
const FACE_FOR_STATUS = {
|
|
364
368
|
working: 'happy',
|
|
365
369
|
need_me: 'surprised',
|
|
366
370
|
stuck: 'dizzy',
|
|
367
371
|
done: 'loved',
|
|
368
|
-
resting: '
|
|
372
|
+
resting: 'slits',
|
|
369
373
|
}
|
|
370
374
|
const zTexture = (() => {
|
|
371
375
|
const canvas = document.createElement('canvas')
|
|
@@ -384,6 +388,63 @@ const zTexture = (() => {
|
|
|
384
388
|
return texture
|
|
385
389
|
})()
|
|
386
390
|
|
|
391
|
+
|
|
392
|
+
const sweatTexture = (() => {
|
|
393
|
+
const canvas = document.createElement('canvas')
|
|
394
|
+
canvas.width = 64
|
|
395
|
+
canvas.height = 96
|
|
396
|
+
const ctx = canvas.getContext('2d')
|
|
397
|
+
ctx.clearRect(0, 0, 64, 96)
|
|
398
|
+
ctx.fillStyle = '#bfeaff'
|
|
399
|
+
ctx.beginPath()
|
|
400
|
+
ctx.moveTo(32, 8)
|
|
401
|
+
ctx.bezierCurveTo(46, 30, 55, 48, 55, 62)
|
|
402
|
+
ctx.bezierCurveTo(55, 79, 45, 89, 32, 89)
|
|
403
|
+
ctx.bezierCurveTo(19, 89, 9, 79, 9, 62)
|
|
404
|
+
ctx.bezierCurveTo(9, 48, 18, 30, 32, 8)
|
|
405
|
+
ctx.fill()
|
|
406
|
+
ctx.fillStyle = 'rgba(255,255,255,.85)'
|
|
407
|
+
ctx.beginPath()
|
|
408
|
+
ctx.ellipse(23, 58, 5, 9, -0.35, 0, Math.PI * 2)
|
|
409
|
+
ctx.fill()
|
|
410
|
+
const texture = new THREE.CanvasTexture(canvas)
|
|
411
|
+
texture.colorSpace = THREE.SRGBColorSpace
|
|
412
|
+
return texture
|
|
413
|
+
})()
|
|
414
|
+
|
|
415
|
+
const scribbleTexture = (() => {
|
|
416
|
+
const canvas = document.createElement('canvas')
|
|
417
|
+
canvas.width = 256
|
|
418
|
+
canvas.height = 160
|
|
419
|
+
const ctx = canvas.getContext('2d')
|
|
420
|
+
ctx.clearRect(0, 0, 256, 160)
|
|
421
|
+
const strokes = [
|
|
422
|
+
[[34, 92], [64, 34], [92, 112], [124, 40], [154, 108], [186, 48], [224, 96]],
|
|
423
|
+
[[48, 54], [92, 78], [60, 126], [126, 92], [172, 124], [208, 66]],
|
|
424
|
+
[[78, 24], [104, 66], [144, 26], [168, 74], [208, 32]],
|
|
425
|
+
]
|
|
426
|
+
ctx.lineCap = 'round'
|
|
427
|
+
ctx.lineJoin = 'round'
|
|
428
|
+
for (const pass of [{ color: 'rgba(255,255,255,.18)', width: 13 }, { color: '#0b0d10', width: 8 }]) {
|
|
429
|
+
ctx.strokeStyle = pass.color
|
|
430
|
+
ctx.lineWidth = pass.width
|
|
431
|
+
for (const points of strokes) {
|
|
432
|
+
ctx.beginPath()
|
|
433
|
+
ctx.moveTo(points[0][0], points[0][1])
|
|
434
|
+
for (let i = 1; i < points.length - 1; i += 2) {
|
|
435
|
+
const [cx, cy] = points[i]
|
|
436
|
+
const [x, y] = points[i + 1]
|
|
437
|
+
ctx.quadraticCurveTo(cx, cy, x, y)
|
|
438
|
+
}
|
|
439
|
+
ctx.stroke()
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
const texture = new THREE.CanvasTexture(canvas)
|
|
443
|
+
texture.colorSpace = THREE.SRGBColorSpace
|
|
444
|
+
return texture
|
|
445
|
+
})()
|
|
446
|
+
const OVERLOAD_COLOR = new THREE.Color(0xff5a5a)
|
|
447
|
+
|
|
387
448
|
const floorTexture = (() => {
|
|
388
449
|
const canvas = document.createElement('canvas')
|
|
389
450
|
canvas.width = canvas.height = 256
|
|
@@ -414,7 +475,7 @@ function disposeGroup(group) {
|
|
|
414
475
|
if (object.material) {
|
|
415
476
|
const materials = Array.isArray(object.material) ? object.material : [object.material]
|
|
416
477
|
for (const material of materials) {
|
|
417
|
-
if (material.map && material.map !== glowTexture && material.map !== shadowTexture && material.map !== floorTexture) material.map.dispose()
|
|
478
|
+
if (material.map && material.map !== glowTexture && material.map !== shadowTexture && material.map !== floorTexture && material.map !== sweatTexture && material.map !== scribbleTexture) material.map.dispose()
|
|
418
479
|
material.dispose()
|
|
419
480
|
}
|
|
420
481
|
}
|
|
@@ -456,6 +517,25 @@ function robotFor(agent, index = 0, sitY = 0) {
|
|
|
456
517
|
face.position.y = baseY
|
|
457
518
|
group.add(face)
|
|
458
519
|
|
|
520
|
+
// 正常工作:大汗淋漓;长时间工作:红温 + 头顶乱线
|
|
521
|
+
const sweat = []
|
|
522
|
+
for (let i = 0; i < 4; i++) {
|
|
523
|
+
const mat = new THREE.SpriteMaterial({ map: sweatTexture, transparent: true, opacity: 0, depthWrite: false, toneMapped: false, color: 0xbfeaff })
|
|
524
|
+
const drop = new THREE.Sprite(mat)
|
|
525
|
+
drop.scale.set(0.095, 0.145, 1)
|
|
526
|
+
drop.visible = false
|
|
527
|
+
group.add(drop)
|
|
528
|
+
sweat.push({ sprite: drop, phase: i / 4, x: i % 2 === 0 ? -0.27 : 0.27, drift: i < 2 ? -0.04 : 0.04 })
|
|
529
|
+
}
|
|
530
|
+
const scribbleGlow = new THREE.Sprite(new THREE.SpriteMaterial({ map: glowTexture, color: 0xff4a4a, transparent: true, opacity: 0, depthWrite: false, blending: THREE.AdditiveBlending }))
|
|
531
|
+
scribbleGlow.scale.set(1.0, 0.62, 1)
|
|
532
|
+
scribbleGlow.position.set(0, baseY + 1.55, 0.03)
|
|
533
|
+
group.add(scribbleGlow)
|
|
534
|
+
const scribble = new THREE.Sprite(new THREE.SpriteMaterial({ map: scribbleTexture, transparent: true, opacity: 0, depthWrite: false, toneMapped: false }))
|
|
535
|
+
scribble.scale.set(0.84, 0.5, 1)
|
|
536
|
+
scribble.position.set(0, baseY + 1.55, 0.04)
|
|
537
|
+
group.add(scribble)
|
|
538
|
+
|
|
459
539
|
// 猫咪上下浮动的 GSAP 循环(用统一相位对象驱动,tick 里再乘幅度)
|
|
460
540
|
const motion = { bob: 0.5 }
|
|
461
541
|
if (!reduceMotion) {
|
|
@@ -567,7 +647,7 @@ function robotFor(agent, index = 0, sitY = 0) {
|
|
|
567
647
|
group.userData = {
|
|
568
648
|
agentId: agent.id,
|
|
569
649
|
index,
|
|
570
|
-
parts: { face, faceMat, ring, glow, badge, label, zzz, ripples, motion, seatShadow, shadow, tint, baseY },
|
|
650
|
+
parts: { face, faceMat, ring, glow, badge, label, zzz, ripples, motion, seatShadow, shadow, tint, baseY, sweat, scribble, scribbleGlow },
|
|
571
651
|
}
|
|
572
652
|
return group
|
|
573
653
|
}
|
|
@@ -1099,7 +1179,7 @@ function buildFloor(team, index, teamAgents) {
|
|
|
1099
1179
|
|
|
1100
1180
|
// 彩色货架(参考原版的彩色格子)
|
|
1101
1181
|
const shelf = new THREE.Group()
|
|
1102
|
-
shelf.position.set(-W / 2 + 0.
|
|
1182
|
+
shelf.position.set(-W / 2 + 0.38, 0, -0.4)
|
|
1103
1183
|
shelf.rotation.y = Math.PI / 2
|
|
1104
1184
|
const shelfMat = new THREE.MeshStandardMaterial({ color: 0x1b2430, roughness: 0.7, metalness: 0.3 })
|
|
1105
1185
|
for (const y of [0.5, 1.15, 1.8]) {
|
|
@@ -1215,6 +1295,10 @@ function applyCustomFaceToAll() {
|
|
|
1215
1295
|
parts.faceMat.color.set(customFaceTexture ? 0xffffff : parts.tint ?? 0xffffff)
|
|
1216
1296
|
parts.faceMat.needsUpdate = true
|
|
1217
1297
|
}
|
|
1298
|
+
if (selectedId !== null) {
|
|
1299
|
+
const entry = agentsById.get(selectedId)
|
|
1300
|
+
if (entry) renderCard(entry.agent)
|
|
1301
|
+
}
|
|
1218
1302
|
}
|
|
1219
1303
|
|
|
1220
1304
|
function loadCustomFace() {
|
|
@@ -1222,9 +1306,11 @@ function loadCustomFace() {
|
|
|
1222
1306
|
try { data = localStorage.getItem(CUSTOM_FACE_KEY) ?? '' } catch {}
|
|
1223
1307
|
if (!data) {
|
|
1224
1308
|
customFaceTexture = null
|
|
1309
|
+
customFaceData = ''
|
|
1225
1310
|
applyCustomFaceToAll()
|
|
1226
1311
|
return
|
|
1227
1312
|
}
|
|
1313
|
+
customFaceData = data
|
|
1228
1314
|
faceLoader.load(data, texture => {
|
|
1229
1315
|
texture.colorSpace = THREE.SRGBColorSpace
|
|
1230
1316
|
customFaceTexture = texture
|
|
@@ -1232,8 +1318,20 @@ function loadCustomFace() {
|
|
|
1232
1318
|
})
|
|
1233
1319
|
}
|
|
1234
1320
|
|
|
1321
|
+
function applyGlassSetting() {
|
|
1322
|
+
let raw = ''
|
|
1323
|
+
try { raw = localStorage.getItem(GLASS_KEY) ?? '' } catch {}
|
|
1324
|
+
const value = Number.parseFloat(raw)
|
|
1325
|
+
if (Number.isFinite(value)) {
|
|
1326
|
+
document.documentElement.style.setProperty('--glass-alpha', String(Math.min(0.98, Math.max(0.5, value / 100))))
|
|
1327
|
+
} else {
|
|
1328
|
+
document.documentElement.style.removeProperty('--glass-alpha')
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1235
1332
|
window.addEventListener('storage', event => {
|
|
1236
1333
|
if (event.key === CUSTOM_FACE_KEY) loadCustomFace()
|
|
1334
|
+
if (event.key === GLASS_KEY) applyGlassSetting()
|
|
1237
1335
|
})
|
|
1238
1336
|
let usage = { tokens_week: 0, tokens_today: 0 }
|
|
1239
1337
|
let selectedId = null
|
|
@@ -1258,8 +1356,8 @@ function globalPose(floorsCount) {
|
|
|
1258
1356
|
const height = Math.max(0, (floorsCount - 1) * FLOOR_GAP)
|
|
1259
1357
|
const centerX = ((floorsCount - 1) * OFFSET_X) / 2
|
|
1260
1358
|
const centerZ = ((floorsCount - 1) * OFFSET_Z) / 2
|
|
1261
|
-
const dist = (
|
|
1262
|
-
const target = new THREE.Vector3(centerX, height / 2 + 1.
|
|
1359
|
+
const dist = (19 + floorsCount * 3) * viewScale()
|
|
1360
|
+
const target = new THREE.Vector3(centerX + 3.6, height / 2 + 1.0, centerZ + 0.8)
|
|
1263
1361
|
return {
|
|
1264
1362
|
target,
|
|
1265
1363
|
position: new THREE.Vector3(centerX + dist * 0.42, target.y + dist * 0.82, centerZ + dist * 0.9),
|
|
@@ -1302,6 +1400,8 @@ function setFloorFocus(index) {
|
|
|
1302
1400
|
for (const child of floor.children) child.robot.userData.parts.label.visible = visible
|
|
1303
1401
|
}
|
|
1304
1402
|
neonSign.visible = !focused
|
|
1403
|
+
if (annex) annex.visible = !focused
|
|
1404
|
+
if (mcpGroup) mcpGroup.visible = !focused
|
|
1305
1405
|
const chip = $('#focusChip')
|
|
1306
1406
|
if (focused) {
|
|
1307
1407
|
const floor = floors.find(item => item.index === index)
|
|
@@ -1409,7 +1509,7 @@ function renderChecks() {
|
|
|
1409
1509
|
]
|
|
1410
1510
|
const done = checks.filter(item => item.ok).length
|
|
1411
1511
|
$('#checkScore').textContent = `${done}/${checks.length}`
|
|
1412
|
-
$('#checkBody').innerHTML = checks.map(item => `<div class="check ${item.ok ? 'ok' : ''}"><i
|
|
1512
|
+
$('#checkBody').innerHTML = checks.map(item => `<div class="check ${item.ok ? 'ok' : ''}"><i><svg viewBox="0 0 16 16" aria-hidden="true"><path d="M3.5 8.5l3 3 6-6"/></svg></i><span>${item.text}</span></div>`).join('')
|
|
1413
1513
|
}
|
|
1414
1514
|
|
|
1415
1515
|
function renderMeter() {
|
|
@@ -1432,31 +1532,36 @@ function renderFeed() {
|
|
|
1432
1532
|
}).join('')
|
|
1433
1533
|
}
|
|
1434
1534
|
|
|
1535
|
+
let cardHideTimer = null
|
|
1536
|
+
|
|
1435
1537
|
function renderCard(agent) {
|
|
1436
1538
|
const card = $('#card')
|
|
1437
1539
|
if (agent === null) {
|
|
1438
|
-
card.classList.
|
|
1540
|
+
if (!card.classList.contains('show') || card.classList.contains('is-out')) return
|
|
1541
|
+
card.classList.add('is-out')
|
|
1542
|
+
clearTimeout(cardHideTimer)
|
|
1543
|
+
cardHideTimer = setTimeout(() => card.classList.remove('show', 'is-out'), 160)
|
|
1439
1544
|
return
|
|
1440
1545
|
}
|
|
1546
|
+
clearTimeout(cardHideTimer)
|
|
1547
|
+
card.classList.remove('is-out')
|
|
1441
1548
|
card.classList.add('show')
|
|
1442
1549
|
const status = STATUS[agent.status] ?? STATUS.resting
|
|
1443
1550
|
const floor = floors.find(item => item.team === agent.team)
|
|
1444
1551
|
$('#cardTeam').textContent = floor === undefined ? agent.team : `楼层 ${floor.index + 1} · ${agent.team}`
|
|
1445
1552
|
const glyphEl = $('#cardGlyph')
|
|
1446
1553
|
glyphEl.textContent = ''
|
|
1447
|
-
glyphEl.style.backgroundImage = `url(${faceDefault})`
|
|
1554
|
+
glyphEl.style.backgroundImage = `url(${customFaceData || faceDefault})`
|
|
1448
1555
|
glyphEl.style.backgroundSize = '82% 82%'
|
|
1449
1556
|
glyphEl.style.backgroundRepeat = 'no-repeat'
|
|
1450
1557
|
glyphEl.style.backgroundPosition = 'center'
|
|
1451
|
-
glyphEl.style.backgroundColor = `${agent.color}22`
|
|
1452
|
-
glyphEl.style.border = `1px solid ${agent.color}66`
|
|
1558
|
+
glyphEl.style.backgroundColor = customFaceData ? 'transparent' : `${agent.color}22`
|
|
1559
|
+
glyphEl.style.border = customFaceData ? '1px solid var(--line-2)' : `1px solid ${agent.color}66`
|
|
1453
1560
|
$('#cardName').textContent = agent.name
|
|
1454
1561
|
$('#cardSub').textContent = agent.schedule
|
|
1455
1562
|
const statusEl = $('#cardStatus')
|
|
1456
1563
|
statusEl.textContent = `● ${status.label}`
|
|
1457
|
-
statusEl.
|
|
1458
|
-
statusEl.style.borderColor = status.color + '66'
|
|
1459
|
-
statusEl.style.background = status.color + '18'
|
|
1564
|
+
statusEl.dataset.status = agent.status
|
|
1460
1565
|
$('#cardRuns').textContent = String(agent.runs_today ?? 0)
|
|
1461
1566
|
$('#cardShare').textContent = `${Math.round(agent.usage_pct || 0)}%`
|
|
1462
1567
|
$('#cardTokens').textContent = fmt(agent.tokens_today || 0)
|
|
@@ -1482,6 +1587,255 @@ function teamList(list) {
|
|
|
1482
1587
|
return teams.slice(0, 4)
|
|
1483
1588
|
}
|
|
1484
1589
|
|
|
1590
|
+
|
|
1591
|
+
/* ------------------------------------------------------------------ *
|
|
1592
|
+
* 右侧 Annex:三张固定工位 + 讲台 + 小健身房 + 备考隔间
|
|
1593
|
+
* ------------------------------------------------------------------ */
|
|
1594
|
+
const ANNEX_X = 9.6
|
|
1595
|
+
const ANNEX_Z = 0.3
|
|
1596
|
+
const ANNEX_W = 7.2
|
|
1597
|
+
const ANNEX_D = 6.0
|
|
1598
|
+
let annex = null
|
|
1599
|
+
let annexLights = []
|
|
1600
|
+
let mcpCurve = null
|
|
1601
|
+
let mcpBeads = []
|
|
1602
|
+
let mcpDashes = []
|
|
1603
|
+
let mcpGroup = null
|
|
1604
|
+
let mcpSign = null
|
|
1605
|
+
|
|
1606
|
+
function buildAnnex() {
|
|
1607
|
+
annex = new THREE.Group()
|
|
1608
|
+
annex.position.set(ANNEX_X, 0, ANNEX_Z)
|
|
1609
|
+
annex.scale.setScalar(0.84)
|
|
1610
|
+
scene.add(annex)
|
|
1611
|
+
|
|
1612
|
+
const wood = new THREE.MeshStandardMaterial({ color: 0x2a2119, roughness: 0.72, metalness: 0.2 })
|
|
1613
|
+
const dark = new THREE.MeshStandardMaterial({ color: 0x17212b, roughness: 0.5, metalness: 0.5 })
|
|
1614
|
+
const metal = new THREE.MeshStandardMaterial({ color: 0x334654, roughness: 0.35, metalness: 0.75 })
|
|
1615
|
+
const glass = new THREE.MeshStandardMaterial({ color: 0x8fc8ff, transparent: true, opacity: 0.06, roughness: 0.08, metalness: 0.1, depthWrite: false, side: THREE.DoubleSide })
|
|
1616
|
+
const cyan = new THREE.MeshBasicMaterial({ color: 0x7fd7ff, toneMapped: false })
|
|
1617
|
+
const warm = new THREE.MeshBasicMaterial({ color: 0xffcf9a, toneMapped: false })
|
|
1618
|
+
const box = (w, h, d, mat, x, y, z, parent = annex) => {
|
|
1619
|
+
const mesh = new THREE.Mesh(new THREE.BoxGeometry(w, h, d), mat)
|
|
1620
|
+
mesh.position.set(x, y, z)
|
|
1621
|
+
parent.add(mesh)
|
|
1622
|
+
return mesh
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
// 地板 / 边线
|
|
1626
|
+
box(ANNEX_W, 0.3, ANNEX_D, wood, 0, -0.15, 0)
|
|
1627
|
+
const top = new THREE.Mesh(new THREE.PlaneGeometry(ANNEX_W - 0.18, ANNEX_D - 0.18), new THREE.MeshStandardMaterial({ map: floorTexture, roughness: 0.8, metalness: 0.25 }))
|
|
1628
|
+
top.rotation.x = -Math.PI / 2
|
|
1629
|
+
top.position.y = 0.012
|
|
1630
|
+
annex.add(top)
|
|
1631
|
+
const edge = new THREE.LineSegments(
|
|
1632
|
+
new THREE.EdgesGeometry(new THREE.BoxGeometry(ANNEX_W + 0.04, 0.34, ANNEX_D + 0.04)),
|
|
1633
|
+
new THREE.LineBasicMaterial({ color: 0x8fd4ff, transparent: true, opacity: 0.75 }),
|
|
1634
|
+
)
|
|
1635
|
+
annex.add(edge)
|
|
1636
|
+
|
|
1637
|
+
// 玻璃外墙(正面留空)
|
|
1638
|
+
box(ANNEX_W, 2.35, 0.05, glass, 0, 1.18, -ANNEX_D / 2 + 0.08)
|
|
1639
|
+
box(0.05, 2.35, ANNEX_D, glass, -ANNEX_W / 2 + 0.08, 1.18, 0)
|
|
1640
|
+
box(0.05, 2.35, ANNEX_D, glass, ANNEX_W / 2 - 0.08, 1.18, 0)
|
|
1641
|
+
for (const [x, z] of [[-ANNEX_W / 2 + 0.08, -ANNEX_D / 2 + 0.08], [ANNEX_W / 2 - 0.08, -ANNEX_D / 2 + 0.08], [-ANNEX_W / 2 + 0.08, ANNEX_D / 2 - 0.08], [ANNEX_W / 2 - 0.08, ANNEX_D / 2 - 0.08]]) {
|
|
1642
|
+
box(0.09, 2.45, 0.09, dark, x, 1.22, z)
|
|
1643
|
+
}
|
|
1644
|
+
box(ANNEX_W, 0.06, 0.06, metal, 0, 2.38, -ANNEX_D / 2 + 0.08)
|
|
1645
|
+
box(0.06, 0.06, ANNEX_D, metal, -ANNEX_W / 2 + 0.08, 2.38, 0)
|
|
1646
|
+
box(0.06, 0.06, ANNEX_D, metal, ANNEX_W / 2 - 0.08, 2.38, 0)
|
|
1647
|
+
box(ANNEX_W - 1.0, 0.025, 0.02, cyan, 0, 2.31, -ANNEX_D / 2 + 0.16)
|
|
1648
|
+
|
|
1649
|
+
// 三张固定工位
|
|
1650
|
+
function addDesk(x, z) {
|
|
1651
|
+
const g = new THREE.Group()
|
|
1652
|
+
g.position.set(x, 0, z)
|
|
1653
|
+
box(1.16, 0.055, 0.62, wood, 0, 0.72, 0, g)
|
|
1654
|
+
for (const [lx, lz] of [[-0.5, -0.24], [0.5, -0.24], [-0.5, 0.24], [0.5, 0.24]]) {
|
|
1655
|
+
box(0.055, 0.7, 0.055, dark, lx, 0.36, lz, g)
|
|
1656
|
+
}
|
|
1657
|
+
box(0.62, 0.4, 0.045, dark, 0, 0.95, -0.18, g)
|
|
1658
|
+
const screen = new THREE.Mesh(new THREE.PlaneGeometry(0.54, 0.3), cyan)
|
|
1659
|
+
screen.position.set(0, 0.95, -0.155)
|
|
1660
|
+
g.add(screen)
|
|
1661
|
+
box(0.02, 0.06, 0.02, metal, 0, 0.73, 0.12, g)
|
|
1662
|
+
box(0.5, 0.055, 0.06, warm, 0, 0.76, 0.3, g)
|
|
1663
|
+
// 椅子
|
|
1664
|
+
box(0.42, 0.06, 0.4, dark, 0, 0.45, 0.55, g)
|
|
1665
|
+
box(0.42, 0.5, 0.06, dark, 0, 0.7, 0.75, g)
|
|
1666
|
+
const stem = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.42, 8), metal)
|
|
1667
|
+
stem.position.set(0, 0.22, 0.55)
|
|
1668
|
+
g.add(stem)
|
|
1669
|
+
const base = new THREE.Mesh(new THREE.CylinderGeometry(0.23, 0.23, 0.03, 16), dark)
|
|
1670
|
+
base.position.set(0, 0.02, 0.55)
|
|
1671
|
+
g.add(base)
|
|
1672
|
+
annex.add(g)
|
|
1673
|
+
}
|
|
1674
|
+
addDesk(-2.35, -1.55)
|
|
1675
|
+
addDesk(0, -1.55)
|
|
1676
|
+
addDesk(2.35, -1.55)
|
|
1677
|
+
|
|
1678
|
+
// 讲台
|
|
1679
|
+
const podium = new THREE.Group()
|
|
1680
|
+
podium.position.set(0, 0, 1.55)
|
|
1681
|
+
box(1.35, 0.14, 0.95, dark, 0, 0.07, 0, podium)
|
|
1682
|
+
const lectern = box(0.7, 0.98, 0.5, wood, 0, 0.56, 0, podium)
|
|
1683
|
+
lectern.rotation.x = -0.04
|
|
1684
|
+
const topPlate = box(0.86, 0.06, 0.58, metal, 0, 1.08, 0, podium)
|
|
1685
|
+
topPlate.rotation.x = -0.16
|
|
1686
|
+
const panel = new THREE.Mesh(new THREE.PlaneGeometry(0.46, 0.26), cyan)
|
|
1687
|
+
panel.position.set(0, 0.64, 0.256)
|
|
1688
|
+
podium.add(panel)
|
|
1689
|
+
const mic = new THREE.Mesh(new THREE.CylinderGeometry(0.014, 0.014, 0.36, 6), metal)
|
|
1690
|
+
mic.position.set(0.2, 1.22, 0.02)
|
|
1691
|
+
mic.rotation.z = -0.22
|
|
1692
|
+
podium.add(mic)
|
|
1693
|
+
const micHead = new THREE.Mesh(new THREE.SphereGeometry(0.04, 10, 10), cyan)
|
|
1694
|
+
micHead.position.set(0.24, 1.4, 0.02)
|
|
1695
|
+
podium.add(micHead)
|
|
1696
|
+
annex.add(podium)
|
|
1697
|
+
|
|
1698
|
+
// 小健身房
|
|
1699
|
+
const gym = new THREE.Group()
|
|
1700
|
+
gym.position.set(2.45, 0, 1.5)
|
|
1701
|
+
const belt = box(0.95, 0.14, 0.48, dark, 0, 0.16, 0, gym)
|
|
1702
|
+
box(0.72, 0.025, 0.34, new THREE.MeshStandardMaterial({ color: 0x101820, roughness: 0.9 }), 0, 0.24, 0, gym)
|
|
1703
|
+
for (const side of [-0.5, 0.5]) {
|
|
1704
|
+
box(0.05, 0.62, 0.05, metal, side, 0.46, -0.17, gym)
|
|
1705
|
+
box(0.05, 0.05, 0.5, metal, side, 0.76, -0.02, gym)
|
|
1706
|
+
}
|
|
1707
|
+
box(0.6, 0.05, 0.05, metal, 0, 0.72, -0.17, gym)
|
|
1708
|
+
box(0.3, 0.2, 0.04, dark, 0, 0.83, -0.22, gym)
|
|
1709
|
+
const mat = new THREE.Mesh(new THREE.PlaneGeometry(1.2, 0.8), new THREE.MeshBasicMaterial({ color: 0x1c3b3d, transparent: true, opacity: 0.85 }))
|
|
1710
|
+
mat.rotation.x = -Math.PI / 2
|
|
1711
|
+
mat.position.set(-0.7, 0.018, 0.15)
|
|
1712
|
+
gym.add(mat)
|
|
1713
|
+
// 哑铃架
|
|
1714
|
+
box(0.65, 0.24, 0.3, dark, -0.15, 0.12, -0.62, gym)
|
|
1715
|
+
for (const [dx, w] of [[-0.2, 0.26], [0, 0.3], [0.2, 0.22]]) {
|
|
1716
|
+
const bar = new THREE.Mesh(new THREE.CylinderGeometry(0.035, 0.035, w, 8), metal)
|
|
1717
|
+
bar.rotation.z = Math.PI / 2
|
|
1718
|
+
bar.position.set(-0.15 + dx, 0.3, -0.62)
|
|
1719
|
+
gym.add(bar)
|
|
1720
|
+
}
|
|
1721
|
+
annex.add(gym)
|
|
1722
|
+
|
|
1723
|
+
// 备考隔间
|
|
1724
|
+
const study = new THREE.Group()
|
|
1725
|
+
study.position.set(-2.55, 0, 1.5)
|
|
1726
|
+
box(1.55, 1.78, 0.04, glass, 0, 0.9, -0.66, study)
|
|
1727
|
+
box(0.04, 1.78, 1.28, glass, -0.76, 0.9, 0, study)
|
|
1728
|
+
box(0.04, 1.78, 1.28, glass, 0.76, 0.9, 0, study)
|
|
1729
|
+
box(1.55, 0.06, 0.06, metal, 0, 1.79, -0.66, study)
|
|
1730
|
+
box(0.06, 0.06, 1.32, metal, -0.76, 1.79, 0, study)
|
|
1731
|
+
box(0.06, 0.06, 1.32, metal, 0.76, 1.79, 0, study)
|
|
1732
|
+
box(0.7, 0.05, 0.42, wood, 0, 0.7, -0.28, study)
|
|
1733
|
+
box(0.7, 0.58, 0.04, dark, 0, 0.95, -0.49, study)
|
|
1734
|
+
box(0.4, 0.05, 0.36, dark, 0, 0.43, 0.14, study)
|
|
1735
|
+
box(0.4, 0.42, 0.05, dark, 0, 0.65, 0.32, study)
|
|
1736
|
+
const lamp = new THREE.Mesh(new THREE.SphereGeometry(0.05, 10, 10), warm)
|
|
1737
|
+
lamp.position.set(0.48, 1.35, -0.3)
|
|
1738
|
+
study.add(lamp)
|
|
1739
|
+
annex.add(study)
|
|
1740
|
+
|
|
1741
|
+
// 室内灯
|
|
1742
|
+
const lightWarm = new THREE.PointLight(0xffd8b0, 2.6, 11, 2)
|
|
1743
|
+
lightWarm.position.set(0, 2.15, 0.4)
|
|
1744
|
+
annex.add(lightWarm)
|
|
1745
|
+
annexLights.push({ light: lightWarm, night: 2.6, day: 1.1 })
|
|
1746
|
+
const lightCool = new THREE.PointLight(0x8fc8ff, 1.8, 10, 2)
|
|
1747
|
+
lightCool.position.set(0, 2.3, -1.8)
|
|
1748
|
+
annex.add(lightCool)
|
|
1749
|
+
annexLights.push({ light: lightCool, night: 1.8, day: 0.8 })
|
|
1750
|
+
|
|
1751
|
+
// 区域标签
|
|
1752
|
+
const tag = (text, x, y, z) => {
|
|
1753
|
+
const el = document.createElement('div')
|
|
1754
|
+
el.className = 'officeTag'
|
|
1755
|
+
el.textContent = text
|
|
1756
|
+
const obj = new CSS2DObject(el)
|
|
1757
|
+
obj.position.set(x, y, z)
|
|
1758
|
+
annex.add(obj)
|
|
1759
|
+
}
|
|
1760
|
+
tag('讲台', 0, 1.8, 1.55)
|
|
1761
|
+
tag('健身', 2.45, 1.95, 1.5)
|
|
1762
|
+
tag('备考', -2.55, 2.0, 1.5)
|
|
1763
|
+
|
|
1764
|
+
// MCP 虚线 + 流光 + 暂未接通标牌
|
|
1765
|
+
mcpGroup = new THREE.Group()
|
|
1766
|
+
scene.add(mcpGroup)
|
|
1767
|
+
mcpCurve = new THREE.CatmullRomCurve3([
|
|
1768
|
+
new THREE.Vector3(4.55, 2.55, 0.35),
|
|
1769
|
+
new THREE.Vector3(5.15, 3.35, 0.55),
|
|
1770
|
+
new THREE.Vector3(5.8, 3.05, 0.8),
|
|
1771
|
+
new THREE.Vector3(6.58, 2.3, 1.14),
|
|
1772
|
+
])
|
|
1773
|
+
const pts = mcpCurve.getPoints(90)
|
|
1774
|
+
const cableGeo = new THREE.BufferGeometry().setFromPoints(pts)
|
|
1775
|
+
const cable = new THREE.Line(cableGeo, new THREE.LineDashedMaterial({
|
|
1776
|
+
color: 0x2f7fff, dashSize: 0.2, gapSize: 0.16, transparent: true, opacity: 0.9,
|
|
1777
|
+
}))
|
|
1778
|
+
cable.computeLineDistances()
|
|
1779
|
+
mcpGroup.add(cable)
|
|
1780
|
+
|
|
1781
|
+
// 沿曲线流动的短划,形成可见的“流光”
|
|
1782
|
+
const dashGeo = new THREE.BoxGeometry(0.18, 0.05, 0.05)
|
|
1783
|
+
const dashMat = new THREE.MeshBasicMaterial({ color: 0x2f7fff, transparent: true, opacity: 0.95, depthWrite: false, toneMapped: false })
|
|
1784
|
+
for (let i = 0; i < 8; i++) {
|
|
1785
|
+
const dash = new THREE.Mesh(dashGeo, dashMat)
|
|
1786
|
+
mcpGroup.add(dash)
|
|
1787
|
+
mcpDashes.push({ mesh: dash, phase: i / 8 })
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
const beadGeo = new THREE.SphereGeometry(0.042, 8, 8)
|
|
1791
|
+
const beadMat = new THREE.MeshBasicMaterial({ color: 0x66b8ff, transparent: true, opacity: 0.95, depthWrite: false, toneMapped: false })
|
|
1792
|
+
for (let i = 0; i < 9; i++) {
|
|
1793
|
+
const bead = new THREE.Mesh(beadGeo, beadMat)
|
|
1794
|
+
mcpGroup.add(bead)
|
|
1795
|
+
mcpBeads.push({ mesh: bead, phase: i / 9 })
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
const signCanvas = document.createElement('canvas')
|
|
1799
|
+
signCanvas.width = 512
|
|
1800
|
+
signCanvas.height = 160
|
|
1801
|
+
const sctx = signCanvas.getContext('2d')
|
|
1802
|
+
sctx.clearRect(0, 0, 512, 160)
|
|
1803
|
+
sctx.fillStyle = 'rgba(5,16,28,.88)'
|
|
1804
|
+
sctx.beginPath()
|
|
1805
|
+
sctx.roundRect(6, 6, 500, 148, 24)
|
|
1806
|
+
sctx.fill()
|
|
1807
|
+
sctx.strokeStyle = 'rgba(84,168,255,.55)'
|
|
1808
|
+
sctx.lineWidth = 2
|
|
1809
|
+
sctx.stroke()
|
|
1810
|
+
sctx.strokeStyle = '#54a8ff'
|
|
1811
|
+
sctx.lineWidth = 13
|
|
1812
|
+
sctx.lineCap = 'round'
|
|
1813
|
+
const drawX = (cx, cy, size) => {
|
|
1814
|
+
sctx.beginPath(); sctx.moveTo(cx - size, cy - size); sctx.lineTo(cx + size, cy + size); sctx.stroke()
|
|
1815
|
+
sctx.beginPath(); sctx.moveTo(cx + size, cy - size); sctx.lineTo(cx - size, cy + size); sctx.stroke()
|
|
1816
|
+
}
|
|
1817
|
+
drawX(58, 70, 19)
|
|
1818
|
+
drawX(454, 70, 19)
|
|
1819
|
+
sctx.fillStyle = '#d8efff'
|
|
1820
|
+
sctx.textAlign = 'center'
|
|
1821
|
+
sctx.font = '700 58px ui-monospace, Menlo, monospace'
|
|
1822
|
+
sctx.fillText('MCP', 256, 89)
|
|
1823
|
+
sctx.fillStyle = 'rgba(120,190,255,.82)'
|
|
1824
|
+
sctx.font = '500 22px ui-monospace, Menlo, monospace'
|
|
1825
|
+
sctx.fillText('暂未接通', 256, 128)
|
|
1826
|
+
const signTex = new THREE.CanvasTexture(signCanvas)
|
|
1827
|
+
signTex.colorSpace = THREE.SRGBColorSpace
|
|
1828
|
+
mcpSign = new THREE.Sprite(new THREE.SpriteMaterial({ map: signTex, transparent: true, depthWrite: false, toneMapped: false }))
|
|
1829
|
+
mcpSign.position.set(5.6, 4.55, 1.18)
|
|
1830
|
+
mcpSign.scale.set(3.35, 1.05, 1)
|
|
1831
|
+
mcpGroup.add(mcpSign)
|
|
1832
|
+
|
|
1833
|
+
const signGlow = new THREE.Sprite(new THREE.SpriteMaterial({ map: glowTexture, color: 0x3f9dff, transparent: true, opacity: 0.24, blending: THREE.AdditiveBlending, depthWrite: false }))
|
|
1834
|
+
signGlow.position.copy(mcpSign.position)
|
|
1835
|
+
signGlow.scale.set(4.4, 1.8, 1)
|
|
1836
|
+
mcpGroup.add(signGlow)
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1485
1839
|
function rebuild(list) {
|
|
1486
1840
|
for (const [, entry] of agentsById) {
|
|
1487
1841
|
const motion = entry.robot?.userData?.parts?.motion
|
|
@@ -1510,6 +1864,8 @@ function rebuild(list) {
|
|
|
1510
1864
|
neonSign.position.set((teams.length - 1) * OFFSET_X * 0.5, height + 5.2, (teams.length - 1) * OFFSET_Z * 0.5)
|
|
1511
1865
|
selectedId = null
|
|
1512
1866
|
focusFloor = null
|
|
1867
|
+
if (annex) annex.visible = true
|
|
1868
|
+
if (mcpGroup) mcpGroup.visible = true
|
|
1513
1869
|
$('#focusChip').hidden = true
|
|
1514
1870
|
const pose = globalPose(teams.length)
|
|
1515
1871
|
controls.target.copy(pose.target)
|
|
@@ -1615,6 +1971,25 @@ function tick() {
|
|
|
1615
1971
|
const t = clock.getElapsedTime()
|
|
1616
1972
|
requestAnimationFrame(tick)
|
|
1617
1973
|
applyThemeFrame()
|
|
1974
|
+
if (mcpCurve && mcpBeads.length > 0) {
|
|
1975
|
+
const flow = clock.getElapsedTime()
|
|
1976
|
+
for (const bead of mcpBeads) {
|
|
1977
|
+
const p = mcpCurve.getPointAt((flow * 0.11 + bead.phase) % 1)
|
|
1978
|
+
bead.mesh.position.copy(p)
|
|
1979
|
+
bead.mesh.scale.setScalar(0.75 + 0.35 * Math.sin(flow * 5 + bead.phase * 18))
|
|
1980
|
+
}
|
|
1981
|
+
const axis = new THREE.Vector3(1, 0, 0)
|
|
1982
|
+
const point = new THREE.Vector3()
|
|
1983
|
+
const tangent = new THREE.Vector3()
|
|
1984
|
+
for (const dash of mcpDashes) {
|
|
1985
|
+
const t = (flow * 0.075 + dash.phase) % 1
|
|
1986
|
+
mcpCurve.getPointAt(t, point)
|
|
1987
|
+
mcpCurve.getTangentAt(t, tangent)
|
|
1988
|
+
dash.mesh.position.copy(point)
|
|
1989
|
+
dash.mesh.quaternion.setFromUnitVectors(axis, tangent.normalize())
|
|
1990
|
+
}
|
|
1991
|
+
if (mcpSign) mcpSign.position.y = 4.55 + Math.sin(flow * 0.9) * 0.04
|
|
1992
|
+
}
|
|
1618
1993
|
|
|
1619
1994
|
const focusedTeam = focusFloor === null ? null : floors.find(floor => floor.index === focusFloor)?.team
|
|
1620
1995
|
for (const [id, entry] of agentsById) {
|
|
@@ -1628,6 +2003,13 @@ function tick() {
|
|
|
1628
2003
|
const working = status === 'working'
|
|
1629
2004
|
const sleeping = status === 'resting'
|
|
1630
2005
|
const phase = (id.charCodeAt(2) || 0) * 0.7
|
|
2006
|
+
const nowMs = Date.now()
|
|
2007
|
+
if (working) {
|
|
2008
|
+
if (!entry.workingSince) entry.workingSince = nowMs
|
|
2009
|
+
} else {
|
|
2010
|
+
entry.workingSince = 0
|
|
2011
|
+
}
|
|
2012
|
+
const overloaded = working && entry.workingSince > 0 && nowMs - entry.workingSince > LOAD_WARN_MS
|
|
1631
2013
|
const amp = working ? 0.09 : 0.055
|
|
1632
2014
|
const bob = reduceMotion ? 0 : (parts.motion.bob * 2 - 1) * amp
|
|
1633
2015
|
parts.face.position.y = parts.baseY + bob
|
|
@@ -1638,7 +2020,7 @@ function tick() {
|
|
|
1638
2020
|
}
|
|
1639
2021
|
|
|
1640
2022
|
// 表情只随状态切换;有自定义猫咪图片时优先使用
|
|
1641
|
-
const expr = FACE_FOR_STATUS[status] ?? 'default'
|
|
2023
|
+
const expr = overloaded ? 'dizzy' : (FACE_FOR_STATUS[status] ?? 'default')
|
|
1642
2024
|
if (customFaceTexture) {
|
|
1643
2025
|
if (parts.faceMat.map !== customFaceTexture) {
|
|
1644
2026
|
parts.faceMat.map = customFaceTexture
|
|
@@ -1650,18 +2032,41 @@ function tick() {
|
|
|
1650
2032
|
parts.faceMat.map = faceTextures[expr]
|
|
1651
2033
|
parts.faceMat.needsUpdate = true
|
|
1652
2034
|
}
|
|
1653
|
-
parts.faceMat.color.copy(parts.tint)
|
|
2035
|
+
parts.faceMat.color.copy(parts.tint).lerp(OVERLOAD_COLOR, overloaded ? 0.55 : 0)
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
// 正常工作:汗滴;长时间工作:红温乱线
|
|
2039
|
+
const sweaty = working && !overloaded
|
|
2040
|
+
for (const drop of parts.sweat) {
|
|
2041
|
+
drop.sprite.visible = sweaty
|
|
2042
|
+
if (!sweaty) continue
|
|
2043
|
+
const p = (t * 0.55 + drop.phase) % 1
|
|
2044
|
+
drop.sprite.position.set(drop.x + Math.sin(t * 2 + drop.phase * 8) * drop.drift, parts.baseY + 0.26 - p * 0.34, 0.12)
|
|
2045
|
+
drop.sprite.material.opacity = Math.sin(p * Math.PI) * 0.92
|
|
2046
|
+
drop.sprite.scale.set(0.085 + p * 0.03, 0.13 + p * 0.05, 1)
|
|
2047
|
+
}
|
|
2048
|
+
parts.scribble.visible = overloaded
|
|
2049
|
+
parts.scribbleGlow.visible = overloaded
|
|
2050
|
+
if (overloaded) {
|
|
2051
|
+
const jitter = Math.sin(t * 17 + phase) * 0.012
|
|
2052
|
+
parts.scribble.position.set(jitter, parts.baseY + 1.55 + Math.sin(t * 3.2 + phase) * 0.03, 0.04)
|
|
2053
|
+
parts.scribble.rotation = Math.sin(t * 6 + phase) * 0.08
|
|
2054
|
+
parts.scribble.material.opacity = 0.96
|
|
2055
|
+
parts.scribbleGlow.position.copy(parts.scribble.position)
|
|
2056
|
+
parts.scribbleGlow.position.z = 0.02
|
|
2057
|
+
parts.scribbleGlow.material.opacity = 0.26 + Math.sin(t * 8 + phase) * 0.08
|
|
1654
2058
|
}
|
|
1655
2059
|
|
|
1656
2060
|
// 呼吸/浮动
|
|
1657
2061
|
const breathe = sleeping ? 1 + Math.sin(t * 1.5 + phase) * 0.02 : 1 + Math.sin(t * 2.2 + phase) * 0.012
|
|
1658
2062
|
parts.face.scale.set(0.6 * breathe, 0.6 * breathe, 1)
|
|
1659
2063
|
|
|
1660
|
-
const statusColor = STATUS[status]?.color ?? STATUS.resting.color
|
|
2064
|
+
const statusColor = overloaded ? '#ff5a5a' : (STATUS[status]?.color ?? STATUS.resting.color)
|
|
1661
2065
|
parts.ring.material.color.set(statusColor)
|
|
1662
2066
|
parts.glow.material.color.set(statusColor)
|
|
1663
2067
|
parts.ring.rotation.z = status === 'stuck' ? t * 2.6 : 0
|
|
1664
|
-
const ringPulse =
|
|
2068
|
+
const ringPulse = overloaded ? 1 + Math.sin(t * 6.5 + phase) * 0.12
|
|
2069
|
+
: working ? 1 + Math.sin(t * 3.4 + phase) * 0.08
|
|
1665
2070
|
: status === 'need_me' ? 1 + Math.sin(t * 5 + phase) * 0.1 : 1
|
|
1666
2071
|
parts.ring.scale.setScalar(ringPulse * (id === selectedId ? 1.24 : 1))
|
|
1667
2072
|
for (let k = 0; k < parts.ripples.length; k++) {
|
|
@@ -1673,7 +2078,8 @@ function tick() {
|
|
|
1673
2078
|
ripple.scale.setScalar(1 + p * 1.1)
|
|
1674
2079
|
ripple.material.opacity = (1 - p) * 0.3
|
|
1675
2080
|
}
|
|
1676
|
-
parts.glow.material.opacity =
|
|
2081
|
+
parts.glow.material.opacity = overloaded ? 0.38 + Math.sin(t * 6.5 + phase) * 0.08
|
|
2082
|
+
: working ? 0.34 + Math.sin(t * 3.4 + phase) * 0.1
|
|
1677
2083
|
: status === 'need_me' ? 0.32 : status === 'stuck' ? 0.32 : 0.14
|
|
1678
2084
|
|
|
1679
2085
|
// 空闲睡觉:飘 Z
|
|
@@ -1855,6 +2261,8 @@ window.__NS = {
|
|
|
1855
2261
|
rebuild([])
|
|
1856
2262
|
$('#offline').classList.add('show')
|
|
1857
2263
|
$('#offline').textContent = '正在读取本机会话…'
|
|
2264
|
+
buildAnnex()
|
|
2265
|
+
applyGlassSetting()
|
|
1858
2266
|
loadCustomFace()
|
|
1859
2267
|
poll()
|
|
1860
2268
|
setInterval(poll, 2000)
|
|
Binary file
|
package/screenshot-focus.png
CHANGED
|
Binary file
|
package/screenshot.png
CHANGED
|
Binary file
|