let-them-talk 5.3.0 → 5.4.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.
Files changed (166) hide show
  1. package/CHANGELOG.md +3 -1
  2. package/README.md +158 -592
  3. package/SECURITY.md +3 -3
  4. package/USAGE.md +151 -0
  5. package/agent-contracts.js +447 -0
  6. package/api-agents.js +760 -0
  7. package/autonomy/decision-v2.js +380 -0
  8. package/autonomy/watchdog-policy.js +572 -0
  9. package/cli.js +454 -298
  10. package/conversation-templates/autonomous-feature.json +83 -22
  11. package/conversation-templates/code-review.json +69 -21
  12. package/conversation-templates/debug-squad.json +69 -21
  13. package/conversation-templates/feature-build.json +69 -21
  14. package/conversation-templates/research-write.json +69 -21
  15. package/dashboard.html +3148 -174
  16. package/dashboard.js +823 -786
  17. package/data-dir.js +58 -0
  18. package/docs/architecture/branch-semantics.md +157 -0
  19. package/docs/architecture/canonical-event-schema.md +88 -0
  20. package/docs/architecture/markdown-workspace.md +183 -0
  21. package/docs/architecture/runtime-contract.md +459 -0
  22. package/docs/architecture/runtime-migration-hardening.md +64 -0
  23. package/events/hooks.js +154 -0
  24. package/events/log.js +457 -0
  25. package/events/replay.js +33 -0
  26. package/events/schema.js +432 -0
  27. package/managed-team-integration.js +261 -0
  28. package/office/agents.js +704 -597
  29. package/office/animation.js +1 -1
  30. package/office/assets/arcade-cabinet.js +141 -0
  31. package/office/assets/archway.js +77 -0
  32. package/office/assets/bar-counter.js +91 -0
  33. package/office/assets/bar-stool.js +71 -0
  34. package/office/assets/beanbag.js +64 -0
  35. package/office/assets/bench.js +99 -0
  36. package/office/assets/bollard.js +87 -0
  37. package/office/assets/cactus.js +100 -0
  38. package/office/assets/carpet-tile.js +46 -0
  39. package/office/assets/chair.js +123 -0
  40. package/office/assets/chandelier.js +107 -0
  41. package/office/assets/coffee-machine.js +95 -0
  42. package/office/assets/coffee-table.js +81 -0
  43. package/office/assets/column.js +95 -0
  44. package/office/assets/desk-lamp.js +102 -0
  45. package/office/assets/desk.js +76 -0
  46. package/office/assets/dining-table.js +105 -0
  47. package/office/assets/door.js +70 -0
  48. package/office/assets/dual-monitor.js +72 -0
  49. package/office/assets/fence.js +76 -0
  50. package/office/assets/filing-cabinet.js +111 -0
  51. package/office/assets/floor-lamp.js +69 -0
  52. package/office/assets/floor-tile.js +54 -0
  53. package/office/assets/flower-pot.js +76 -0
  54. package/office/assets/foosball.js +95 -0
  55. package/office/assets/fridge.js +99 -0
  56. package/office/assets/gaming-chair.js +154 -0
  57. package/office/assets/gaming-desk.js +105 -0
  58. package/office/assets/glass-door.js +72 -0
  59. package/office/assets/glass-wall.js +64 -0
  60. package/office/assets/half-wall.js +49 -0
  61. package/office/assets/hanging-plant.js +112 -0
  62. package/office/assets/index.js +151 -0
  63. package/office/assets/indoor-tree.js +90 -0
  64. package/office/assets/l-sofa.js +153 -0
  65. package/office/assets/marble-floor.js +64 -0
  66. package/office/assets/materials.js +40 -0
  67. package/office/assets/meeting-table.js +88 -0
  68. package/office/assets/microwave.js +94 -0
  69. package/office/assets/monitor.js +67 -0
  70. package/office/assets/neon-strip.js +73 -0
  71. package/office/assets/painting.js +84 -0
  72. package/office/assets/palm-tree.js +108 -0
  73. package/office/assets/pc-tower.js +91 -0
  74. package/office/assets/pendant-light.js +67 -0
  75. package/office/assets/ping-pong.js +114 -0
  76. package/office/assets/plant.js +72 -0
  77. package/office/assets/planter-box.js +95 -0
  78. package/office/assets/pool-table.js +94 -0
  79. package/office/assets/printer.js +113 -0
  80. package/office/assets/reception-desk.js +133 -0
  81. package/office/assets/rug.js +78 -0
  82. package/office/assets/sculpture.js +85 -0
  83. package/office/assets/server-rack.js +98 -0
  84. package/office/assets/sink.js +109 -0
  85. package/office/assets/sofa.js +106 -0
  86. package/office/assets/speaker.js +83 -0
  87. package/office/assets/spotlight.js +83 -0
  88. package/office/assets/street-lamp.js +97 -0
  89. package/office/assets/trash-can.js +83 -0
  90. package/office/assets/treadmill.js +126 -0
  91. package/office/assets/trophy.js +89 -0
  92. package/office/assets/tv-screen.js +79 -0
  93. package/office/assets/vase.js +84 -0
  94. package/office/assets/wall-clock.js +84 -0
  95. package/office/assets/wall.js +53 -0
  96. package/office/assets/water-cooler.js +146 -0
  97. package/office/assets/whiteboard.js +115 -0
  98. package/office/assets.js +3 -431
  99. package/office/builder.js +791 -355
  100. package/office/campus-env.js +1012 -1119
  101. package/office/environment.js +2 -0
  102. package/office/gallery.js +997 -0
  103. package/office/index.js +165 -61
  104. package/office/navigation.js +173 -152
  105. package/office/player.js +178 -68
  106. package/office/robot-character.js +272 -0
  107. package/office/spectator-camera.js +33 -10
  108. package/office/state.js +2 -0
  109. package/office/world-save.js +35 -4
  110. package/package.json +57 -3
  111. package/providers/comfyui.js +383 -0
  112. package/providers/dalle.js +79 -0
  113. package/providers/gemini.js +181 -0
  114. package/providers/ollama.js +184 -0
  115. package/providers/replicate.js +115 -0
  116. package/providers/zai.js +183 -0
  117. package/runtime-descriptor.js +270 -0
  118. package/scripts/check-agent-contract-advisory.js +132 -0
  119. package/scripts/check-api-agent-parity.js +277 -0
  120. package/scripts/check-autonomy-v2-decision.js +207 -0
  121. package/scripts/check-autonomy-v2-execution.js +588 -0
  122. package/scripts/check-autonomy-v2-watchdog.js +224 -0
  123. package/scripts/check-branch-fork-snapshot.js +337 -0
  124. package/scripts/check-branch-isolation.js +787 -0
  125. package/scripts/check-branch-semantics.js +139 -0
  126. package/scripts/check-dashboard-control-plane.js +1304 -0
  127. package/scripts/check-docs-onboarding.js +490 -0
  128. package/scripts/check-event-schema.js +276 -0
  129. package/scripts/check-evidence-completion.js +239 -0
  130. package/scripts/check-invariants.js +992 -0
  131. package/scripts/check-lifecycle-hooks.js +525 -0
  132. package/scripts/check-managed-team-integration.js +166 -0
  133. package/scripts/check-markdown-workspace-export.js +548 -0
  134. package/scripts/check-markdown-workspace-safety.js +347 -0
  135. package/scripts/check-markdown-workspace.js +136 -0
  136. package/scripts/check-message-replay.js +429 -0
  137. package/scripts/check-migration-hardening.js +300 -0
  138. package/scripts/check-performance-indexing.js +272 -0
  139. package/scripts/check-provider-capabilities.js +316 -0
  140. package/scripts/check-runtime-contract.js +109 -0
  141. package/scripts/check-session-aware-context.js +172 -0
  142. package/scripts/check-session-lifecycle.js +210 -0
  143. package/scripts/export-markdown-workspace.js +84 -0
  144. package/scripts/fixtures/message-replay/clean.jsonl +2 -0
  145. package/scripts/fixtures/message-replay/corrupt-correction-payload.jsonl +1 -0
  146. package/scripts/fixtures/message-replay/corrupt-jsonl.jsonl +1 -0
  147. package/scripts/fixtures/message-replay/corrupt-payload.jsonl +1 -0
  148. package/scripts/fixtures/message-replay/out-of-order.jsonl +2 -0
  149. package/scripts/migrate-legacy-to-canonical.js +201 -0
  150. package/scripts/run-verification-suite.js +242 -0
  151. package/scripts/sync-packaged-docs.js +69 -0
  152. package/server.js +9546 -7216
  153. package/state/agents.js +161 -0
  154. package/state/canonical.js +3068 -0
  155. package/state/dashboard-queries.js +441 -0
  156. package/state/evidence.js +56 -0
  157. package/state/io.js +69 -0
  158. package/state/markdown-workspace.js +951 -0
  159. package/state/messages.js +669 -0
  160. package/state/sessions.js +683 -0
  161. package/state/tasks-workflows.js +92 -0
  162. package/templates/debate.json +2 -2
  163. package/templates/managed.json +4 -4
  164. package/templates/pair.json +2 -2
  165. package/templates/review.json +2 -2
  166. package/templates/team.json +3 -3
@@ -0,0 +1,67 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'pendant_light',
6
+ name: 'Pendant Light',
7
+ category: 'lighting',
8
+ icon: 'PL',
9
+ gridW: 1, gridD: 1, height: 3.0,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ var wireY = 3.0; // wire top anchor
14
+ var shadeY = 2.10; // bottom of shade
15
+ var R = 0.12; // globe radius
16
+
17
+ var chromeMat = PAL.chrome();
18
+ var cordMat = mat(0x1a1a1a, { roughness: 0.90 });
19
+
20
+ // Ceiling rose (mount disk)
21
+ var rose = new THREE.Mesh(new THREE.CylinderGeometry(0.055, 0.055, 0.018, 20), chromeMat);
22
+ rose.position.y = wireY;
23
+ g.add(rose);
24
+
25
+ // Pendant cord (thin cylinder)
26
+ var cordLen = wireY - shadeY - R - 0.01;
27
+ var cord = new THREE.Mesh(
28
+ new THREE.CylinderGeometry(0.006, 0.006, cordLen, 8),
29
+ cordMat
30
+ );
31
+ cord.position.y = shadeY + R + 0.01 + cordLen / 2;
32
+ g.add(cord);
33
+
34
+ // Globe shade (hollow sphere shell — DoubleSide)
35
+ var globeOuter = new THREE.Mesh(
36
+ new THREE.SphereGeometry(R, 28, 20),
37
+ mat(0xeae0d0, { transparent: true, opacity: 0.55, roughness: 0.10, side: THREE.DoubleSide })
38
+ );
39
+ globeOuter.position.y = shadeY + R;
40
+ g.add(globeOuter);
41
+
42
+ // Internal warm bulb glow
43
+ var bulb = new THREE.Mesh(
44
+ new THREE.SphereGeometry(0.028, 14, 10),
45
+ mat(0xfff0cc, { emissive: 0xfff0cc, emissiveIntensity: 1.4, transparent: true, opacity: 0.95 })
46
+ );
47
+ bulb.position.y = shadeY + R;
48
+ g.add(bulb);
49
+
50
+ // Chrome neck fitting (top of globe)
51
+ var neck = new THREE.Mesh(new THREE.CylinderGeometry(0.018, 0.018, 0.025, 14), chromeMat);
52
+ neck.position.y = shadeY + R * 2 - 0.005;
53
+ g.add(neck);
54
+
55
+ // Chrome bottom vent ring (open bottom)
56
+ var botRing = new THREE.Mesh(new THREE.TorusGeometry(R - 0.005, 0.005, 8, 28), chromeMat);
57
+ botRing.position.y = shadeY + 0.008;
58
+ g.add(botRing);
59
+
60
+ // Point light — hangs at bulb position
61
+ var light = new THREE.PointLight(0xfff0cc, 0.45, 5);
62
+ light.position.y = shadeY + R;
63
+ g.add(light);
64
+
65
+ return g;
66
+ }
67
+ };
@@ -0,0 +1,114 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'ping_pong',
6
+ name: 'Ping Pong Table',
7
+ category: 'recreation',
8
+ icon: 'PP',
9
+ gridW: 3, gridD: 2, height: 0.76,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Table top — green with dark edge
14
+ var top = new THREE.Mesh(
15
+ new THREE.BoxGeometry(2.74, 0.04, 1.52),
16
+ mat(0x115522, { roughness: 0.75 })
17
+ );
18
+ top.position.y = 0.74;
19
+ top.castShadow = true;
20
+ top.receiveShadow = true;
21
+ g.add(top);
22
+
23
+ // White edge border (thin frame around top surface)
24
+ var edgeMat = mat(0xffffff, { roughness: 0.70 });
25
+ var edgeLong = new THREE.BoxGeometry(2.74, 0.042, 0.018);
26
+ var edgeShort = new THREE.BoxGeometry(0.018, 0.042, 1.52);
27
+ [0.761, -0.761].forEach(function(z) {
28
+ var e = new THREE.Mesh(edgeLong, edgeMat);
29
+ e.position.set(0, 0.74, z);
30
+ g.add(e);
31
+ });
32
+ [-1.371, 1.371].forEach(function(x) {
33
+ var e = new THREE.Mesh(edgeShort, edgeMat);
34
+ e.position.set(x, 0.74, 0);
35
+ g.add(e);
36
+ });
37
+
38
+ // White center line (lengthwise)
39
+ var centerLine = new THREE.Mesh(
40
+ new THREE.BoxGeometry(2.74, 0.045, 0.022),
41
+ edgeMat
42
+ );
43
+ centerLine.position.set(0, 0.74, 0);
44
+ g.add(centerLine);
45
+
46
+ // White half-court line (across middle)
47
+ var halfLine = new THREE.Mesh(
48
+ new THREE.BoxGeometry(0.022, 0.045, 1.52),
49
+ edgeMat
50
+ );
51
+ halfLine.position.set(0, 0.74, 0);
52
+ g.add(halfLine);
53
+
54
+ // Net post — left
55
+ var postMat = PAL.chrome();
56
+ var postGeo = new THREE.CylinderGeometry(0.014, 0.014, 0.20, 8);
57
+ var leftPost = new THREE.Mesh(postGeo, postMat);
58
+ leftPost.position.set(0, 0.85, 0.78);
59
+ leftPost.castShadow = true;
60
+ g.add(leftPost);
61
+
62
+ // Net post — right
63
+ var rightPost = leftPost.clone();
64
+ rightPost.position.z = -0.78;
65
+ g.add(rightPost);
66
+
67
+ // Net top bar
68
+ var netBar = new THREE.Mesh(
69
+ new THREE.CylinderGeometry(0.008, 0.008, 1.56, 8),
70
+ postMat
71
+ );
72
+ netBar.rotation.x = Math.PI / 2;
73
+ netBar.position.set(0, 0.945, 0);
74
+ g.add(netBar);
75
+
76
+ // Net mesh (semi-transparent white)
77
+ var net = new THREE.Mesh(
78
+ new THREE.BoxGeometry(0.008, 0.16, 1.52),
79
+ mat(0xffffff, { transparent: true, opacity: 0.55, roughness: 0.70, side: THREE.DoubleSide })
80
+ );
81
+ net.position.set(0, 0.865, 0);
82
+ g.add(net);
83
+
84
+ // Chrome folding legs (angled X-frame style) — 4 corner assemblies
85
+ var legMat = PAL.chromeBrushed();
86
+ var legGeo = new THREE.CylinderGeometry(0.018, 0.018, 0.78, 8);
87
+ [[-1.18, 0.37, 0.58], [1.18, 0.37, 0.58], [-1.18, 0.37, -0.58], [1.18, 0.37, -0.58]].forEach(function(p) {
88
+ var leg = new THREE.Mesh(legGeo, legMat);
89
+ leg.position.set(p[0], p[1], p[2]);
90
+ leg.castShadow = true;
91
+ g.add(leg);
92
+ });
93
+
94
+ // Horizontal leg brace (side spreader bars)
95
+ var braceGeo = new THREE.CylinderGeometry(0.012, 0.012, 1.16, 8);
96
+ [[0.58], [-0.58]].forEach(function(arr) {
97
+ var brace = new THREE.Mesh(braceGeo, legMat);
98
+ brace.rotation.z = Math.PI / 2;
99
+ brace.position.set(0, 0.16, arr[0]);
100
+ g.add(brace);
101
+ });
102
+
103
+ // Small rubber feet
104
+ var feetMat = mat(0x0a0a0a, { roughness: 0.92 });
105
+ var feetGeo = new THREE.CylinderGeometry(0.022, 0.022, 0.016, 8);
106
+ [[-1.18, 0.008, 0.58], [1.18, 0.008, 0.58], [-1.18, 0.008, -0.58], [1.18, 0.008, -0.58]].forEach(function(p) {
107
+ var foot = new THREE.Mesh(feetGeo, feetMat);
108
+ foot.position.set(p[0], p[1], p[2]);
109
+ g.add(foot);
110
+ });
111
+
112
+ return g;
113
+ }
114
+ };
@@ -0,0 +1,72 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'plant',
6
+ name: 'Plant',
7
+ category: 'nature',
8
+ icon: 'Pl',
9
+ gridW: 1, gridD: 1, height: 0.8,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Concrete planter — cylindrical
14
+ var planter = new THREE.Mesh(
15
+ new THREE.CylinderGeometry(0.22, 0.17, 0.30, 20),
16
+ mat(0x3a3d44, { roughness: 0.90 })
17
+ );
18
+ planter.position.y = 0.15;
19
+ planter.castShadow = true;
20
+ planter.receiveShadow = true;
21
+ g.add(planter);
22
+
23
+ // Planter rim
24
+ var rim = new THREE.Mesh(
25
+ new THREE.TorusGeometry(0.22, 0.018, 8, 28),
26
+ mat(0x2a2d33, { roughness: 0.85 })
27
+ );
28
+ rim.position.y = 0.298;
29
+ rim.rotation.x = Math.PI / 2;
30
+ g.add(rim);
31
+
32
+ // Soil surface
33
+ var soil = new THREE.Mesh(
34
+ new THREE.CylinderGeometry(0.20, 0.20, 0.02, 20),
35
+ mat(0x1a1510, { roughness: 0.99 })
36
+ );
37
+ soil.position.y = 0.31;
38
+ g.add(soil);
39
+
40
+ // Leaf cluster 1 (center, taller)
41
+ var leaf1 = new THREE.Mesh(
42
+ new THREE.SphereGeometry(0.19, 12, 10),
43
+ PAL.leaf()
44
+ );
45
+ leaf1.scale.set(1, 1.35, 1);
46
+ leaf1.position.set(0, 0.62, 0);
47
+ leaf1.castShadow = true;
48
+ g.add(leaf1);
49
+
50
+ // Leaf cluster 2 (left, lower)
51
+ var leaf2 = new THREE.Mesh(
52
+ new THREE.SphereGeometry(0.14, 10, 8),
53
+ mat(0x226638, { roughness: 0.82 })
54
+ );
55
+ leaf2.scale.set(1, 1.1, 1);
56
+ leaf2.position.set(-0.16, 0.52, 0.05);
57
+ leaf2.castShadow = true;
58
+ g.add(leaf2);
59
+
60
+ // Leaf cluster 3 (right)
61
+ var leaf3 = new THREE.Mesh(
62
+ new THREE.SphereGeometry(0.13, 10, 8),
63
+ mat(0x1e5c30, { roughness: 0.84 })
64
+ );
65
+ leaf3.scale.set(1, 1.2, 1);
66
+ leaf3.position.set(0.15, 0.55, -0.08);
67
+ leaf3.castShadow = true;
68
+ g.add(leaf3);
69
+
70
+ return g;
71
+ }
72
+ };
@@ -0,0 +1,95 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'planter_box',
6
+ name: 'Planter Box',
7
+ category: 'exterior',
8
+ icon: 'PB',
9
+ gridW: 1, gridD: 1, height: 0.6,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ var concrete = PAL.concrete();
14
+
15
+ // Main concrete box body
16
+ var body = new THREE.Mesh(
17
+ new THREE.BoxGeometry(1.00, 0.50, 0.50),
18
+ concrete
19
+ );
20
+ body.position.y = 0.25;
21
+ body.castShadow = true;
22
+ body.receiveShadow = true;
23
+ g.add(body);
24
+
25
+ // Inner soil recess (dark soil color, sits inside top)
26
+ var soil = new THREE.Mesh(
27
+ new THREE.BoxGeometry(0.88, 0.12, 0.38),
28
+ mat(0x1a1206, { roughness: 0.95 })
29
+ );
30
+ soil.position.y = 0.50;
31
+ g.add(soil);
32
+
33
+ // Chamfered top edge detail (thin cap strip)
34
+ var cap = new THREE.Mesh(
35
+ new THREE.BoxGeometry(1.00, 0.030, 0.50),
36
+ mat(0x343740, { roughness: 0.80 })
37
+ );
38
+ cap.position.y = 0.515;
39
+ g.add(cap);
40
+
41
+ // Horizontal groove lines on body (2 decorative channels)
42
+ var grooveMat = mat(0x1e2028, { roughness: 0.90 });
43
+ var grooveGeo = new THREE.BoxGeometry(1.002, 0.018, 0.502);
44
+ [0.18, 0.32].forEach(function(y) {
45
+ var groove = new THREE.Mesh(grooveGeo, grooveMat);
46
+ groove.position.y = y;
47
+ g.add(groove);
48
+ });
49
+
50
+ // Greenery — cluster of rounded bush shapes
51
+ var leafMat = PAL.leaf();
52
+ var leafMat2 = mat(0x1d6e3a, { roughness: 0.82 });
53
+
54
+ var bushPositions = [
55
+ { x: -0.26, s: 0.18, h: 0.14 },
56
+ { x: 0, s: 0.22, h: 0.18 },
57
+ { x: 0.26, s: 0.17, h: 0.13 }
58
+ ];
59
+
60
+ bushPositions.forEach(function(b, idx) {
61
+ var bush = new THREE.Mesh(
62
+ new THREE.SphereGeometry(b.s, 10, 8),
63
+ idx % 2 === 0 ? leafMat : leafMat2
64
+ );
65
+ bush.scale.set(1.0, b.h / b.s, 1.0);
66
+ bush.position.set(b.x, 0.54 + b.h, 0);
67
+ bush.castShadow = true;
68
+ g.add(bush);
69
+ });
70
+
71
+ // Small accent sprigs between bushes
72
+ var sprigMat = mat(0x3aaa5e, { roughness: 0.78 });
73
+ [-0.13, 0.13].forEach(function(x) {
74
+ var sprig = new THREE.Mesh(
75
+ new THREE.SphereGeometry(0.09, 8, 6),
76
+ sprigMat
77
+ );
78
+ sprig.scale.set(0.7, 0.9, 0.7);
79
+ sprig.position.set(x, 0.66, 0.04);
80
+ sprig.castShadow = true;
81
+ g.add(sprig);
82
+ });
83
+
84
+ // Drainage detail (small holes on bottom face)
85
+ var drainMat = mat(0x111111, { roughness: 0.80 });
86
+ var drainGeo = new THREE.CylinderGeometry(0.018, 0.018, 0.012, 8);
87
+ [-0.22, 0, 0.22].forEach(function(x) {
88
+ var drain = new THREE.Mesh(drainGeo, drainMat);
89
+ drain.position.set(x, 0.004, 0);
90
+ g.add(drain);
91
+ });
92
+
93
+ return g;
94
+ }
95
+ };
@@ -0,0 +1,94 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'pool_table',
6
+ name: 'Pool Table',
7
+ category: 'recreation',
8
+ icon: 'PT',
9
+ gridW: 3, gridD: 2, height: 0.85,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Walnut outer frame
14
+ var frame = new THREE.Mesh(
15
+ new THREE.BoxGeometry(2.50, 0.16, 1.40),
16
+ PAL.walnutDark()
17
+ );
18
+ frame.position.y = 0.82;
19
+ frame.castShadow = true;
20
+ frame.receiveShadow = true;
21
+ g.add(frame);
22
+
23
+ // Green felt playing surface
24
+ var felt = new THREE.Mesh(
25
+ new THREE.BoxGeometry(2.28, 0.012, 1.18),
26
+ PAL.greenFelt()
27
+ );
28
+ felt.position.y = 0.914;
29
+ felt.receiveShadow = true;
30
+ g.add(felt);
31
+
32
+ // White center line
33
+ var centerLine = new THREE.Mesh(
34
+ new THREE.BoxGeometry(0.012, 0.015, 1.18),
35
+ mat(0xffffff, { roughness: 0.80 })
36
+ );
37
+ centerLine.position.y = 0.922;
38
+ g.add(centerLine);
39
+
40
+ // Baulk line (1/4 from end)
41
+ var baulkLine = new THREE.Mesh(
42
+ new THREE.BoxGeometry(0.012, 0.015, 1.18),
43
+ mat(0xffffff, { roughness: 0.80 })
44
+ );
45
+ baulkLine.position.set(-0.57, 0.922, 0);
46
+ g.add(baulkLine);
47
+
48
+ // 4 thick legs
49
+ var legMat = PAL.walnutDark();
50
+ var legGeo = new THREE.BoxGeometry(0.12, 0.78, 0.12);
51
+ [[-1.10, 0.39, 0.55], [1.10, 0.39, 0.55], [-1.10, 0.39, -0.55], [1.10, 0.39, -0.55]].forEach(function(p) {
52
+ var leg = new THREE.Mesh(legGeo, legMat);
53
+ leg.position.set(p[0], p[1], p[2]);
54
+ leg.castShadow = true;
55
+ g.add(leg);
56
+ });
57
+
58
+ // 6 pockets — small dark spheres
59
+ var pocketMat = mat(0x0a0a0a, { roughness: 0.70 });
60
+ var pocketGeo = new THREE.SphereGeometry(0.055, 10, 8);
61
+ var pocketPositions = [
62
+ [-1.11, 0.920, 0.56], // corners
63
+ [1.11, 0.920, 0.56],
64
+ [-1.11, 0.920, -0.56],
65
+ [1.11, 0.920, -0.56],
66
+ [0, 0.920, 0.60], // side midpoints
67
+ [0, 0.920, -0.60]
68
+ ];
69
+ pocketPositions.forEach(function(p) {
70
+ var pocket = new THREE.Mesh(pocketGeo, pocketMat);
71
+ pocket.position.set(p[0], p[1], p[2]);
72
+ g.add(pocket);
73
+ });
74
+
75
+ // Rail cushions (dark rubber strips around the inside edge)
76
+ var railMat = mat(0x1a1a0a, { roughness: 0.85 });
77
+ // Long rails
78
+ var longRailGeo = new THREE.BoxGeometry(2.26, 0.08, 0.04);
79
+ [0.565, -0.565].forEach(function(z) {
80
+ var rail = new THREE.Mesh(longRailGeo, railMat);
81
+ rail.position.set(0, 0.89, z);
82
+ g.add(rail);
83
+ });
84
+ // Short rails
85
+ var shortRailGeo = new THREE.BoxGeometry(0.04, 0.08, 1.10);
86
+ [-1.11, 1.11].forEach(function(x) {
87
+ var rail = new THREE.Mesh(shortRailGeo, railMat);
88
+ rail.position.set(x, 0.89, 0);
89
+ g.add(rail);
90
+ });
91
+
92
+ return g;
93
+ }
94
+ };
@@ -0,0 +1,113 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'printer',
6
+ name: 'Printer',
7
+ category: 'office',
8
+ icon: 'Pr',
9
+ gridW: 1, gridD: 1, height: 0.4,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ var bodyMat = mat(0x1e2128, { roughness: 0.50, metalness: 0.12 });
14
+ var accentMat = mat(0x252830, { roughness: 0.45, metalness: 0.10 });
15
+
16
+ // Main body block
17
+ var body = new THREE.Mesh(
18
+ new THREE.BoxGeometry(0.6, 0.32, 0.5),
19
+ bodyMat
20
+ );
21
+ body.position.y = 0.2;
22
+ body.castShadow = true;
23
+ body.receiveShadow = true;
24
+ g.add(body);
25
+
26
+ // Top scanner lid (slightly lighter)
27
+ var lid = new THREE.Mesh(
28
+ new THREE.BoxGeometry(0.6, 0.055, 0.5),
29
+ accentMat
30
+ );
31
+ lid.position.y = 0.388;
32
+ lid.castShadow = true;
33
+ g.add(lid);
34
+
35
+ // Scanner glass on top
36
+ var scanGlass = new THREE.Mesh(
37
+ new THREE.BoxGeometry(0.54, 0.01, 0.44),
38
+ mat(0x445566, { transparent: true, opacity: 0.55, roughness: 0.10 })
39
+ );
40
+ scanGlass.position.y = 0.418;
41
+ g.add(scanGlass);
42
+
43
+ // Paper output tray (front, slanted slot)
44
+ var outTray = new THREE.Mesh(
45
+ new THREE.BoxGeometry(0.44, 0.012, 0.22),
46
+ accentMat
47
+ );
48
+ outTray.rotation.x = -0.22;
49
+ outTray.position.set(0, 0.305, 0.255);
50
+ g.add(outTray);
51
+
52
+ // Paper input tray (slightly open slot at back)
53
+ var inTray = new THREE.Mesh(
54
+ new THREE.BoxGeometry(0.40, 0.012, 0.18),
55
+ accentMat
56
+ );
57
+ inTray.rotation.x = 0.2;
58
+ inTray.position.set(0, 0.355, -0.23);
59
+ g.add(inTray);
60
+
61
+ // Front control panel strip
62
+ var panel = new THREE.Mesh(
63
+ new THREE.BoxGeometry(0.58, 0.055, 0.012),
64
+ mat(0x111318, { roughness: 0.4 })
65
+ );
66
+ panel.position.set(0, 0.32, 0.256);
67
+ g.add(panel);
68
+
69
+ // Small LED indicator light (green)
70
+ var led = new THREE.Mesh(
71
+ new THREE.SphereGeometry(0.008, 8, 8),
72
+ mat(0x22c55e, { emissive: 0x22c55e, emissiveIntensity: 0.8 })
73
+ );
74
+ led.position.set(0.22, 0.325, 0.258);
75
+ g.add(led);
76
+
77
+ // Power button
78
+ var btn = new THREE.Mesh(
79
+ new THREE.CylinderGeometry(0.01, 0.01, 0.008, 10),
80
+ mat(0x444444, { roughness: 0.6 })
81
+ );
82
+ btn.rotation.x = Math.PI / 2;
83
+ btn.position.set(0.18, 0.322, 0.258);
84
+ g.add(btn);
85
+
86
+ // Brand label strip
87
+ var label = new THREE.Mesh(
88
+ new THREE.BoxGeometry(0.18, 0.025, 0.008),
89
+ mat(0x111111, { roughness: 0.7 })
90
+ );
91
+ label.position.set(-0.12, 0.322, 0.257);
92
+ g.add(label);
93
+
94
+ // USB/port strip on right side
95
+ var portStrip = new THREE.Mesh(
96
+ new THREE.BoxGeometry(0.012, 0.025, 0.05),
97
+ mat(0x111111, { roughness: 0.7 })
98
+ );
99
+ portStrip.position.set(0.307, 0.20, 0.1);
100
+ g.add(portStrip);
101
+
102
+ // 4 rubber feet
103
+ var footMat = mat(0x111111, { roughness: 0.95 });
104
+ var footGeo = new THREE.CylinderGeometry(0.018, 0.018, 0.012, 8);
105
+ [[-0.24, -0.21], [0.24, -0.21], [-0.24, 0.21], [0.24, 0.21]].forEach(function(p) {
106
+ var foot = new THREE.Mesh(footGeo, footMat);
107
+ foot.position.set(p[0], 0.006, p[1]);
108
+ g.add(foot);
109
+ });
110
+
111
+ return g;
112
+ }
113
+ };
@@ -0,0 +1,133 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'reception-desk',
6
+ name: 'Reception Desk',
7
+ category: 'office',
8
+ icon: 'RD',
9
+ gridW: 4, gridD: 2, height: 1.1,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ var bodyMat = mat(0x17191f, { roughness: 0.55, metalness: 0.10 });
14
+ var marbleMat = PAL.marbleBlack();
15
+ var ledMat = mat(0x58a6ff, { emissive: 0x58a6ff, emissiveIntensity: 0.7, roughness: 0.2 });
16
+
17
+ // === MAIN COUNTER (long front section) ===
18
+ // Counter body
19
+ var mainBody = new THREE.Mesh(
20
+ new THREE.BoxGeometry(4.0, 1.0, 0.65),
21
+ bodyMat
22
+ );
23
+ mainBody.position.set(0, 0.5, 0);
24
+ mainBody.castShadow = true;
25
+ mainBody.receiveShadow = true;
26
+ g.add(mainBody);
27
+
28
+ // Marble top — main
29
+ var mainTop = new THREE.Mesh(
30
+ new THREE.BoxGeometry(4.04, 0.06, 0.69),
31
+ marbleMat
32
+ );
33
+ mainTop.position.set(0, 1.03, 0);
34
+ mainTop.castShadow = true;
35
+ g.add(mainTop);
36
+
37
+ // Front panel fascia (slightly raised detail strip)
38
+ var fascia = new THREE.Mesh(
39
+ new THREE.BoxGeometry(3.96, 0.55, 0.02),
40
+ mat(0x1e2128, { roughness: 0.4, metalness: 0.18 })
41
+ );
42
+ fascia.position.set(0, 0.52, 0.335);
43
+ g.add(fascia);
44
+
45
+ // LED underglow strip — main front
46
+ var ledStrip = new THREE.Mesh(
47
+ new THREE.BoxGeometry(3.96, 0.018, 0.025),
48
+ ledMat
49
+ );
50
+ ledStrip.position.set(0, 0.065, 0.332);
51
+ g.add(ledStrip);
52
+
53
+ // Vertical divider panels on fascia (3 evenly spaced)
54
+ var divMat = mat(0x252830, { roughness: 0.5, metalness: 0.15 });
55
+ [-1.32, 0, 1.32].forEach(function(x) {
56
+ var div = new THREE.Mesh(new THREE.BoxGeometry(0.015, 0.52, 0.022), divMat);
57
+ div.position.set(x, 0.52, 0.336);
58
+ g.add(div);
59
+ });
60
+
61
+ // === WING SECTION (shorter right-angle return) ===
62
+ var wingBody = new THREE.Mesh(
63
+ new THREE.BoxGeometry(1.2, 1.0, 0.65),
64
+ bodyMat
65
+ );
66
+ wingBody.position.set(2.0, 0.5, 0.925);
67
+ wingBody.rotation.y = Math.PI / 2;
68
+ wingBody.castShadow = true;
69
+ wingBody.receiveShadow = true;
70
+ g.add(wingBody);
71
+
72
+ // Wing marble top
73
+ var wingTop = new THREE.Mesh(
74
+ new THREE.BoxGeometry(1.24, 0.06, 0.69),
75
+ marbleMat
76
+ );
77
+ wingTop.position.set(2.0, 1.03, 0.925);
78
+ wingTop.rotation.y = Math.PI / 2;
79
+ wingTop.castShadow = true;
80
+ g.add(wingTop);
81
+
82
+ // Wing front fascia
83
+ var wingFascia = new THREE.Mesh(
84
+ new THREE.BoxGeometry(0.02, 0.55, 1.16),
85
+ mat(0x1e2128, { roughness: 0.4, metalness: 0.18 })
86
+ );
87
+ wingFascia.position.set(2.335, 0.52, 0.925);
88
+ g.add(wingFascia);
89
+
90
+ // Wing LED underglow
91
+ var wingLed = new THREE.Mesh(
92
+ new THREE.BoxGeometry(0.025, 0.018, 1.16),
93
+ ledMat
94
+ );
95
+ wingLed.position.set(2.333, 0.065, 0.925);
96
+ g.add(wingLed);
97
+
98
+ // Corner connector piece (fills the gap)
99
+ var corner = new THREE.Mesh(
100
+ new THREE.BoxGeometry(0.65, 1.0, 0.65),
101
+ bodyMat
102
+ );
103
+ corner.position.set(2.0, 0.5, 0.325);
104
+ corner.castShadow = true;
105
+ g.add(corner);
106
+
107
+ // Corner marble top
108
+ var cornerTop = new THREE.Mesh(
109
+ new THREE.BoxGeometry(0.69, 0.06, 0.69),
110
+ marbleMat
111
+ );
112
+ cornerTop.position.set(2.0, 1.03, 0.325);
113
+ g.add(cornerTop);
114
+
115
+ // Inside rear shelf (staff side) — main
116
+ var innerShelf = new THREE.Mesh(
117
+ new THREE.BoxGeometry(3.8, 0.03, 0.42),
118
+ mat(0x1e2128, { roughness: 0.5 })
119
+ );
120
+ innerShelf.position.set(0, 0.72, -0.1);
121
+ g.add(innerShelf);
122
+
123
+ // Monitor mount riser on inner shelf
124
+ var riser = new THREE.Mesh(
125
+ new THREE.BoxGeometry(0.55, 0.06, 0.30),
126
+ mat(0x252830, { roughness: 0.45 })
127
+ );
128
+ riser.position.set(-0.8, 0.755, -0.1);
129
+ g.add(riser);
130
+
131
+ return g;
132
+ }
133
+ };