dsh-agent-office 4.5.0 → 4.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-agent-office",
3
- "version": "4.5.0",
3
+ "version": "4.6.0",
4
4
  "description": "实时 3D 办公室可视化 DSH 会话;Catora 发布后拓展笔记、思维导图、私人知识库与 RAG 检索。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
package/panel-src/main.js CHANGED
@@ -1589,7 +1589,7 @@ function teamList(list) {
1589
1589
 
1590
1590
 
1591
1591
  /* ------------------------------------------------------------------ *
1592
- * 右侧 Annex:三张固定工位 + 讲台 + 小健身房 + 备考隔间
1592
+ * 右侧 Annex:教室布局(讲台 + 通用/备考/题库三席)
1593
1593
  * ------------------------------------------------------------------ */
1594
1594
  const ANNEX_X = 9.6
1595
1595
  const ANNEX_Z = 0.3
@@ -1602,6 +1602,7 @@ let mcpBeads = []
1602
1602
  let mcpDashes = []
1603
1603
  let mcpGroup = null
1604
1604
  let mcpSign = null
1605
+ let sciFiIcons = []
1605
1606
 
1606
1607
  function buildAnnex() {
1607
1608
  annex = new THREE.Group()
@@ -1646,11 +1647,12 @@ function buildAnnex() {
1646
1647
  box(0.06, 0.06, ANNEX_D, metal, ANNEX_W / 2 - 0.08, 2.38, 0)
1647
1648
  box(ANNEX_W - 1.0, 0.025, 0.02, cyan, 0, 2.31, -ANNEX_D / 2 + 0.16)
1648
1649
 
1649
- // 三张固定工位
1650
- function addDesk(x, z) {
1650
+ // 教室布局:讲台在后墙,三张工位面朝讲台
1651
+ // 1) 通用工位
1652
+ function addDesk(x, z, matTop = wood) {
1651
1653
  const g = new THREE.Group()
1652
1654
  g.position.set(x, 0, z)
1653
- box(1.16, 0.055, 0.62, wood, 0, 0.72, 0, g)
1655
+ box(1.16, 0.055, 0.62, matTop, 0, 0.72, 0, g)
1654
1656
  for (const [lx, lz] of [[-0.5, -0.24], [0.5, -0.24], [-0.5, 0.24], [0.5, 0.24]]) {
1655
1657
  box(0.055, 0.7, 0.055, dark, lx, 0.36, lz, g)
1656
1658
  }
@@ -1658,9 +1660,8 @@ function buildAnnex() {
1658
1660
  const screen = new THREE.Mesh(new THREE.PlaneGeometry(0.54, 0.3), cyan)
1659
1661
  screen.position.set(0, 0.95, -0.155)
1660
1662
  g.add(screen)
1661
- box(0.02, 0.06, 0.02, metal, 0, 0.73, 0.12, g)
1662
1663
  box(0.5, 0.055, 0.06, warm, 0, 0.76, 0.3, g)
1663
- // 椅子
1664
+ // 椅子(面向讲台 -z)
1664
1665
  box(0.42, 0.06, 0.4, dark, 0, 0.45, 0.55, g)
1665
1666
  box(0.42, 0.5, 0.06, dark, 0, 0.7, 0.75, g)
1666
1667
  const stem = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.42, 8), metal)
@@ -1671,13 +1672,147 @@ function buildAnnex() {
1671
1672
  g.add(base)
1672
1673
  annex.add(g)
1673
1674
  }
1674
- addDesk(-2.35, -1.55)
1675
- addDesk(0, -1.55)
1676
- addDesk(2.35, -1.55)
1675
+ addDesk(-2.35, 0.85)
1676
+
1677
+ // 2) 备考隔间(产品里的学习空间)
1678
+ const booth = new THREE.Group()
1679
+ booth.position.set(0, 0, 0.85)
1680
+ box(1.55, 1.78, 0.04, glass, 0, 0.9, -0.66, booth)
1681
+ box(0.04, 1.78, 1.28, glass, -0.76, 0.9, 0, booth)
1682
+ box(0.04, 1.78, 1.28, glass, 0.76, 0.9, 0, booth)
1683
+ box(1.58, 0.06, 0.06, metal, 0, 1.79, -0.66, booth)
1684
+ box(0.06, 0.06, 1.32, metal, -0.76, 1.79, 0, booth)
1685
+ box(0.06, 0.06, 1.32, metal, 0.76, 1.79, 0, booth)
1686
+ box(0.74, 0.055, 0.44, wood, 0, 0.7, -0.2, booth)
1687
+ box(0.56, 0.34, 0.04, dark, 0, 0.93, -0.4, booth)
1688
+ const boothScreen = new THREE.Mesh(new THREE.PlaneGeometry(0.48, 0.25), cyan)
1689
+ boothScreen.position.set(0, 0.93, -0.375)
1690
+ booth.add(boothScreen)
1691
+ box(0.42, 0.06, 0.38, dark, 0, 0.44, 0.2, booth)
1692
+ box(0.42, 0.5, 0.06, dark, 0, 0.69, 0.4, booth)
1693
+ const boothLamp = new THREE.Mesh(new THREE.SphereGeometry(0.05, 10, 10), warm)
1694
+ boothLamp.position.set(0.5, 1.34, -0.32)
1695
+ booth.add(boothLamp)
1696
+ annex.add(booth)
1697
+
1698
+ // 3) 题库工位:科幻桌面 + 浮动 OCR / 数据库 / 试卷投影
1699
+ const sci = new THREE.Group()
1700
+ sci.position.set(2.35, 0, 0.85)
1701
+ box(1.16, 0.07, 0.62, metal, 0, 0.72, 0, sci)
1702
+ for (const [lx, lz] of [[-0.5, -0.24], [0.5, -0.24], [-0.5, 0.24], [0.5, 0.24]]) {
1703
+ box(0.05, 0.7, 0.05, dark, lx, 0.36, lz, sci)
1704
+ }
1705
+ // 全息桌屏
1706
+ const holoCanvas = document.createElement('canvas')
1707
+ holoCanvas.width = 256
1708
+ holoCanvas.height = 128
1709
+ const hctx = holoCanvas.getContext('2d')
1710
+ hctx.clearRect(0, 0, 256, 128)
1711
+ hctx.strokeStyle = 'rgba(110,220,255,.7)'
1712
+ hctx.lineWidth = 2
1713
+ for (let x = 0; x <= 256; x += 24) {
1714
+ hctx.beginPath(); hctx.moveTo(x, 0); hctx.lineTo(x, 128); hctx.stroke()
1715
+ }
1716
+ for (let y = 0; y <= 128; y += 24) {
1717
+ hctx.beginPath(); hctx.moveTo(0, y); hctx.lineTo(256, y); hctx.stroke()
1718
+ }
1719
+ hctx.fillStyle = 'rgba(120,230,255,.16)'
1720
+ hctx.fillRect(16, 22, 224, 20)
1721
+ hctx.fillRect(16, 62, 150, 10)
1722
+ hctx.fillRect(16, 86, 190, 10)
1723
+ const holoTexture = new THREE.CanvasTexture(holoCanvas)
1724
+ holoTexture.colorSpace = THREE.SRGBColorSpace
1725
+ const holo = new THREE.Mesh(new THREE.PlaneGeometry(0.86, 0.42), new THREE.MeshBasicMaterial({ map: holoTexture, transparent: true, opacity: 0.55, depthWrite: false, toneMapped: false, side: THREE.DoubleSide }))
1726
+ holo.position.set(0, 1.08, -0.08)
1727
+ holo.rotation.x = -0.18
1728
+ sci.add(holo)
1729
+ box(0.5, 0.055, 0.06, cyan, 0, 0.76, 0.3, sci)
1730
+ // 椅子
1731
+ box(0.42, 0.06, 0.4, dark, 0, 0.45, 0.55, sci)
1732
+ box(0.42, 0.5, 0.06, dark, 0, 0.7, 0.75, sci)
1733
+ const sciStem = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.42, 8), metal)
1734
+ sciStem.position.set(0, 0.22, 0.55)
1735
+ sci.add(sciStem)
1736
+ const sciBase = new THREE.Mesh(new THREE.CylinderGeometry(0.23, 0.23, 0.03, 16), dark)
1737
+ sciBase.position.set(0, 0.02, 0.55)
1738
+ sci.add(sciBase)
1739
+ annex.add(sci)
1740
+ const halo = new THREE.Mesh(
1741
+ new THREE.RingGeometry(0.4, 0.44, 48),
1742
+ new THREE.MeshBasicMaterial({ color: 0x6fdcff, transparent: true, opacity: 0.35, side: THREE.DoubleSide, depthWrite: false, blending: THREE.AdditiveBlending }),
1743
+ )
1744
+ halo.rotation.x = -Math.PI / 2
1745
+ halo.position.set(2.35, 1.58, 0.98)
1746
+ annex.add(halo)
1747
+
1748
+ // 浮动图标:OCR 相机 / 数据库 / 试卷(billboard,远看也清楚)
1749
+ const iconCanvas = (draw) => {
1750
+ const canvas = document.createElement('canvas')
1751
+ canvas.width = canvas.height = 160
1752
+ const ctx = canvas.getContext('2d')
1753
+ ctx.clearRect(0, 0, 160, 160)
1754
+ ctx.strokeStyle = '#9fe7ff'
1755
+ ctx.fillStyle = '#9fe7ff'
1756
+ ctx.lineWidth = 7
1757
+ ctx.lineCap = 'round'
1758
+ ctx.lineJoin = 'round'
1759
+ ctx.shadowColor = 'rgba(80,200,255,.9)'
1760
+ ctx.shadowBlur = 10
1761
+ draw(ctx)
1762
+ const texture = new THREE.CanvasTexture(canvas)
1763
+ texture.colorSpace = THREE.SRGBColorSpace
1764
+ return texture
1765
+ }
1766
+ const iconSprite = (texture, x, y, z, scale = 0.4) => {
1767
+ const sprite = new THREE.Sprite(new THREE.SpriteMaterial({ map: texture, transparent: true, depthWrite: false, toneMapped: false }))
1768
+ sprite.scale.set(scale, scale, 1)
1769
+ sprite.position.set(2.35 + x, y, 0.85 + z)
1770
+ annex.add(sprite)
1771
+ sprite.userData.baseY = y
1772
+ sprite.userData.phase = sciFiIcons.length * 0.9
1773
+ sciFiIcons.push(sprite)
1774
+ return sprite
1775
+ }
1776
+
1777
+ const ocrTexture = iconCanvas(ctx => {
1778
+ ctx.beginPath(); ctx.roundRect(32, 44, 78, 58, 10); ctx.stroke()
1779
+ ctx.beginPath(); ctx.arc(73, 73, 17, 0, Math.PI * 2); ctx.stroke()
1780
+ ctx.beginPath(); ctx.arc(73, 73, 6, 0, Math.PI * 2); ctx.fill()
1781
+ ctx.fillRect(42, 34, 22, 10)
1782
+ ctx.font = '700 15px ui-monospace, Menlo, monospace'
1783
+ ctx.textAlign = 'center'
1784
+ ctx.fillText('OCR', 80, 128)
1785
+ })
1786
+ const dbTexture = iconCanvas(ctx => {
1787
+ for (let i = 0; i < 3; i++) {
1788
+ const y = 48 + i * 24
1789
+ ctx.beginPath(); ctx.ellipse(80, y, 28, 9, 0, 0, Math.PI * 2); ctx.stroke()
1790
+ ctx.beginPath(); ctx.moveTo(52, y); ctx.lineTo(52, y + 22); ctx.stroke()
1791
+ ctx.beginPath(); ctx.moveTo(108, y); ctx.lineTo(108, y + 22); ctx.stroke()
1792
+ ctx.beginPath(); ctx.ellipse(80, y + 22, 28, 9, 0, 0, Math.PI * 2); ctx.stroke()
1793
+ }
1794
+ ctx.font = '700 15px ui-monospace, Menlo, monospace'
1795
+ ctx.textAlign = 'center'
1796
+ ctx.fillText('DB', 80, 140)
1797
+ })
1798
+ const paperTexture = iconCanvas(ctx => {
1799
+ ctx.beginPath(); ctx.roundRect(46, 28, 68, 92, 5); ctx.stroke()
1800
+ ctx.beginPath(); ctx.moveTo(98, 28); ctx.lineTo(114, 44); ctx.lineTo(98, 44); ctx.closePath(); ctx.stroke()
1801
+ for (let i = 0; i < 5; i++) {
1802
+ ctx.beginPath(); ctx.moveTo(60, 54 + i * 13); ctx.lineTo(100, 54 + i * 13); ctx.stroke()
1803
+ }
1804
+ ctx.font = '700 15px ui-monospace, Menlo, monospace'
1805
+ ctx.textAlign = 'center'
1806
+ ctx.fillText('EXAM', 80, 142)
1807
+ })
1808
+
1809
+ iconSprite(ocrTexture, -0.5, 2.08, 0.12, 0.48)
1810
+ iconSprite(dbTexture, 0, 2.2, 0.12, 0.48)
1811
+ iconSprite(paperTexture, 0.5, 2.08, 0.12, 0.48)
1677
1812
 
1678
- // 讲台
1813
+ // 讲台(教室最前面,背靠后墙,面向三张工位)
1679
1814
  const podium = new THREE.Group()
1680
- podium.position.set(0, 0, 1.55)
1815
+ podium.position.set(0, 0, -1.95)
1681
1816
  box(1.35, 0.14, 0.95, dark, 0, 0.07, 0, podium)
1682
1817
  const lectern = box(0.7, 0.98, 0.5, wood, 0, 0.56, 0, podium)
1683
1818
  lectern.rotation.x = -0.04
@@ -1694,49 +1829,9 @@ function buildAnnex() {
1694
1829
  micHead.position.set(0.24, 1.4, 0.02)
1695
1830
  podium.add(micHead)
1696
1831
  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)
1832
+ // 讲台后的黑板/示教板
1833
+ box(3.3, 1.25, 0.04, dark, 0, 1.55, -2.86)
1834
+ box(3.1, 0.03, 0.05, cyan, 0, 2.18, -2.83)
1740
1835
 
1741
1836
  // 室内灯
1742
1837
  const lightWarm = new THREE.PointLight(0xffd8b0, 2.6, 11, 2)
@@ -1757,9 +1852,10 @@ function buildAnnex() {
1757
1852
  obj.position.set(x, y, z)
1758
1853
  annex.add(obj)
1759
1854
  }
1760
- tag('讲台', 0, 1.8, 1.55)
1761
- tag('健身', 2.45, 1.95, 1.5)
1762
- tag('备考', -2.55, 2.0, 1.5)
1855
+ tag('讲台', 0, 1.9, -1.95)
1856
+ tag('通用', -2.35, 1.45, 0.85)
1857
+ tag('备考', 0, 2.0, 0.85)
1858
+ tag('题库', 2.35, 1.27, 0.85)
1763
1859
 
1764
1860
  // MCP 虚线 + 流光 + 暂未接通标牌
1765
1861
  mcpGroup = new THREE.Group()
@@ -1989,6 +2085,9 @@ function tick() {
1989
2085
  dash.mesh.quaternion.setFromUnitVectors(axis, tangent.normalize())
1990
2086
  }
1991
2087
  if (mcpSign) mcpSign.position.y = 4.55 + Math.sin(flow * 0.9) * 0.04
2088
+ for (const icon of sciFiIcons) {
2089
+ icon.position.y = icon.userData.baseY + Math.sin(flow * 2.2 + icon.userData.phase) * 0.04
2090
+ }
1992
2091
  }
1993
2092
 
1994
2093
  const focusedTeam = focusFloor === null ? null : floors.find(floor => floor.index === focusFloor)?.team
Binary file
package/screenshot.png CHANGED
Binary file