let-them-talk 5.2.5 → 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 -7214
  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
@@ -204,7 +204,7 @@ export function updateAgent(agent, dt, time) {
204
204
  var sittingTarget = agent.isSitting ? 1 : 0;
205
205
  agent.sittingLerp += (sittingTarget - agent.sittingLerp) * Math.min(1, dt * 5);
206
206
 
207
- agent.parts.group.position.y = agent.sittingLerp * 0.14;
207
+ agent.parts.group.position.y = agent.sittingLerp * 0.08;
208
208
  var sitHip = -1.5 * agent.sittingLerp;
209
209
  agent.parts.leftLeg.rotation.x = agent.parts.leftLeg.rotation.x * (1 - agent.sittingLerp) + sitHip * agent.sittingLerp;
210
210
  agent.parts.rightLeg.rotation.x = agent.parts.rightLeg.rotation.x * (1 - agent.sittingLerp) + sitHip * agent.sittingLerp;
@@ -0,0 +1,141 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'arcade_cabinet',
6
+ name: 'Arcade Cabinet',
7
+ category: 'recreation',
8
+ icon: 'AC',
9
+ gridW: 1, gridD: 1, height: 1.7,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Main cabinet body
14
+ var body = new THREE.Mesh(
15
+ new THREE.BoxGeometry(0.70, 1.50, 0.60),
16
+ mat(0x0e1014, { roughness: 0.50, metalness: 0.15 })
17
+ );
18
+ body.position.y = 0.75;
19
+ body.castShadow = true;
20
+ body.receiveShadow = true;
21
+ g.add(body);
22
+
23
+ // Top angled marquee section
24
+ var marquee = new THREE.Mesh(
25
+ new THREE.BoxGeometry(0.70, 0.26, 0.38),
26
+ mat(0x111318, { roughness: 0.45 })
27
+ );
28
+ marquee.position.set(0, 1.63, -0.11);
29
+ marquee.castShadow = true;
30
+ g.add(marquee);
31
+
32
+ // Marquee light (emissive panel)
33
+ var marqueeLit = new THREE.Mesh(
34
+ new THREE.BoxGeometry(0.58, 0.18, 0.01),
35
+ mat(0x220022, { emissive: 0xaa00ff, emissiveIntensity: 1.0, roughness: 0.1 })
36
+ );
37
+ marqueeLit.position.set(0, 1.64, 0.19);
38
+ g.add(marqueeLit);
39
+
40
+ // Marquee glow light
41
+ var marqueeLight = new THREE.PointLight(0xaa00ff, 0.5, 1.2);
42
+ marqueeLight.position.set(0, 1.72, 0.30);
43
+ g.add(marqueeLight);
44
+
45
+ // Angled monitor bezel
46
+ var bezel = new THREE.Mesh(
47
+ new THREE.BoxGeometry(0.54, 0.42, 0.025),
48
+ mat(0x080808, { roughness: 0.70 })
49
+ );
50
+ bezel.rotation.x = -0.30;
51
+ bezel.position.set(0, 1.22, 0.265);
52
+ g.add(bezel);
53
+
54
+ // Screen (emissive display)
55
+ var screen = new THREE.Mesh(
56
+ new THREE.BoxGeometry(0.46, 0.34, 0.010),
57
+ mat(0x000a22, { emissive: 0x0044ff, emissiveIntensity: 0.80, roughness: 0.1 })
58
+ );
59
+ screen.rotation.x = -0.30;
60
+ screen.position.set(0, 1.225, 0.273);
61
+ g.add(screen);
62
+
63
+ // Screen glow
64
+ var screenGlow = new THREE.PointLight(0x0044ff, 0.35, 0.9);
65
+ screenGlow.position.set(0, 1.22, 0.38);
66
+ g.add(screenGlow);
67
+
68
+ // Control panel (angled surface below screen)
69
+ var controlPanel = new THREE.Mesh(
70
+ new THREE.BoxGeometry(0.66, 0.06, 0.32),
71
+ mat(0x111318, { roughness: 0.45 })
72
+ );
73
+ controlPanel.rotation.x = -0.45;
74
+ controlPanel.position.set(0, 0.90, 0.24);
75
+ g.add(controlPanel);
76
+
77
+ // Joystick base
78
+ var joystickBase = new THREE.Mesh(
79
+ new THREE.CylinderGeometry(0.038, 0.038, 0.018, 10),
80
+ mat(0x222222, { roughness: 0.60 })
81
+ );
82
+ joystickBase.position.set(-0.14, 0.96, 0.25);
83
+ g.add(joystickBase);
84
+
85
+ // Joystick stick
86
+ var joystick = new THREE.Mesh(
87
+ new THREE.CylinderGeometry(0.012, 0.012, 0.065, 8),
88
+ mat(0x333333, { roughness: 0.50 })
89
+ );
90
+ joystick.position.set(-0.14, 1.01, 0.25);
91
+ g.add(joystick);
92
+
93
+ // Joystick ball top
94
+ var ball = new THREE.Mesh(
95
+ new THREE.SphereGeometry(0.022, 10, 8),
96
+ mat(0xcc0000, { roughness: 0.40 })
97
+ );
98
+ ball.position.set(-0.14, 1.04, 0.25);
99
+ g.add(ball);
100
+
101
+ // Action buttons (4 colored circles)
102
+ var btnColors = [0xee2211, 0x2288ee, 0x22cc44, 0xeecc00];
103
+ btnColors.forEach(function(color, i) {
104
+ var btn = new THREE.Mesh(
105
+ new THREE.CylinderGeometry(0.022, 0.022, 0.018, 10),
106
+ mat(color, { roughness: 0.45 })
107
+ );
108
+ var bx = 0.06 + (i % 2) * 0.055;
109
+ var bz = 0.22 + Math.floor(i / 2) * 0.055;
110
+ btn.position.set(bx, 0.962, bz);
111
+ g.add(btn);
112
+ });
113
+
114
+ // Side panel decorative stripe (neon line)
115
+ var stripe = new THREE.Mesh(
116
+ new THREE.BoxGeometry(0.008, 1.10, 0.012),
117
+ mat(0xff00cc, { emissive: 0xff00cc, emissiveIntensity: 0.80 })
118
+ );
119
+ stripe.position.set(0.352, 0.80, 0);
120
+ g.add(stripe);
121
+
122
+ // Coin slot
123
+ var coinSlot = new THREE.Mesh(
124
+ new THREE.BoxGeometry(0.055, 0.010, 0.022),
125
+ mat(0x333333, { roughness: 0.60, metalness: 0.40 })
126
+ );
127
+ coinSlot.position.set(0, 0.70, 0.305);
128
+ g.add(coinSlot);
129
+
130
+ // Base kick panel
131
+ var kick = new THREE.Mesh(
132
+ new THREE.BoxGeometry(0.70, 0.20, 0.60),
133
+ mat(0x0a0c10, { roughness: 0.60 })
134
+ );
135
+ kick.position.y = 0.10;
136
+ kick.castShadow = true;
137
+ g.add(kick);
138
+
139
+ return g;
140
+ }
141
+ };
@@ -0,0 +1,77 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'archway',
6
+ name: 'Archway',
7
+ category: 'structural',
8
+ icon: 'Ar',
9
+ gridW: 2, gridD: 1, height: 3,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ var walnut = PAL.walnutDark();
14
+ var chrome = PAL.chrome();
15
+
16
+ // Left pillar
17
+ var pillarL = new THREE.Mesh(
18
+ new THREE.BoxGeometry(0.28, 3, 0.3),
19
+ walnut
20
+ );
21
+ pillarL.position.x = -0.86;
22
+ pillarL.castShadow = true;
23
+ g.add(pillarL);
24
+
25
+ // Right pillar
26
+ var pillarR = pillarL.clone();
27
+ pillarR.position.x = 0.86;
28
+ g.add(pillarR);
29
+
30
+ // Top lintel beam (horizontal span)
31
+ var lintel = new THREE.Mesh(
32
+ new THREE.BoxGeometry(2, 0.35, 0.3),
33
+ walnut
34
+ );
35
+ lintel.position.y = 1.325;
36
+ lintel.castShadow = true;
37
+ g.add(lintel);
38
+
39
+ // Chrome accent strip on lintel front face
40
+ var lintelStrip = new THREE.Mesh(
41
+ new THREE.BoxGeometry(2, 0.05, 0.01),
42
+ chrome
43
+ );
44
+ lintelStrip.position.set(0, 1.325, 0.155);
45
+ g.add(lintelStrip);
46
+
47
+ // Chrome base caps on pillars
48
+ var capGeo = new THREE.BoxGeometry(0.32, 0.07, 0.34);
49
+ var capL = new THREE.Mesh(capGeo, chrome);
50
+ capL.position.set(-0.86, -1.465, 0);
51
+ g.add(capL);
52
+ var capR = new THREE.Mesh(capGeo, chrome);
53
+ capR.position.set(0.86, -1.465, 0);
54
+ g.add(capR);
55
+
56
+ // Chrome crown caps on pillars (top)
57
+ var crownL = new THREE.Mesh(capGeo, chrome);
58
+ crownL.position.set(-0.86, 1.465, 0);
59
+ g.add(crownL);
60
+ var crownR = new THREE.Mesh(capGeo, chrome);
61
+ crownR.position.set(0.86, 1.465, 0);
62
+ g.add(crownR);
63
+
64
+ // Arch soffit — curved underside suggestion (thin curved strip)
65
+ var archCurve = new THREE.Mesh(
66
+ new THREE.TorusGeometry(0.72, 0.04, 6, 18, Math.PI),
67
+ chrome
68
+ );
69
+ archCurve.rotation.y = Math.PI / 2;
70
+ archCurve.rotation.z = Math.PI;
71
+ archCurve.position.y = 0.65;
72
+ archCurve.castShadow = true;
73
+ g.add(archCurve);
74
+
75
+ return g;
76
+ }
77
+ };
@@ -0,0 +1,91 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'bar_counter',
6
+ name: 'Bar Counter',
7
+ category: 'kitchen',
8
+ icon: 'Ba',
9
+ gridW: 3, gridD: 1, height: 1.1,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Walnut countertop
14
+ var top = new THREE.Mesh(
15
+ new THREE.BoxGeometry(3.0, 0.06, 1.2),
16
+ PAL.walnutDark()
17
+ );
18
+ top.position.y = 1.1;
19
+ top.castShadow = true;
20
+ top.receiveShadow = true;
21
+ g.add(top);
22
+
23
+ // Countertop front overhang edge strip (gold trim)
24
+ var edgeTrim = new THREE.Mesh(
25
+ new THREE.BoxGeometry(3.0, 0.025, 0.02),
26
+ PAL.gold()
27
+ );
28
+ edgeTrim.position.set(0, 1.075, 0.61);
29
+ g.add(edgeTrim);
30
+
31
+ // Main front panel (dark)
32
+ var frontPanel = new THREE.Mesh(
33
+ new THREE.BoxGeometry(3.0, 0.92, 0.06),
34
+ mat(0x111318, { roughness: 0.45, metalness: 0.10 })
35
+ );
36
+ frontPanel.position.set(0, 0.59, 0.57);
37
+ frontPanel.castShadow = true;
38
+ g.add(frontPanel);
39
+
40
+ // Cabinet body
41
+ var body = new THREE.Mesh(
42
+ new THREE.BoxGeometry(2.96, 0.90, 1.08),
43
+ mat(0x16181e, { roughness: 0.50 })
44
+ );
45
+ body.position.set(0, 0.59, 0);
46
+ body.castShadow = true;
47
+ g.add(body);
48
+
49
+ // LED underglow strip — neon blue emissive bar under front panel
50
+ var led = new THREE.Mesh(
51
+ new THREE.BoxGeometry(2.9, 0.015, 0.025),
52
+ mat(0x58a6ff, { emissive: 0x58a6ff, emissiveIntensity: 1.2, roughness: 0.3 })
53
+ );
54
+ led.position.set(0, 0.085, 0.575);
55
+ g.add(led);
56
+
57
+ // PointLight for LED underglow
58
+ var glow = new THREE.PointLight(0x58a6ff, 0.6, 1.8);
59
+ glow.position.set(0, 0.06, 0.5);
60
+ g.add(glow);
61
+
62
+ // Base plinth (dark metal strip at floor)
63
+ var plinth = new THREE.Mesh(
64
+ new THREE.BoxGeometry(3.0, 0.08, 1.2),
65
+ PAL.darkMetal()
66
+ );
67
+ plinth.position.y = 0.04;
68
+ plinth.castShadow = true;
69
+ g.add(plinth);
70
+
71
+ // Vertical divider panels inside (3 sections)
72
+ var dividerMat = mat(0x1e2128, { roughness: 0.55 });
73
+ var dividerGeo = new THREE.BoxGeometry(0.04, 0.86, 1.04);
74
+ [-1.0, 0, 1.0].forEach(function(x) {
75
+ var d = new THREE.Mesh(dividerGeo, dividerMat);
76
+ d.position.set(x, 0.59, 0);
77
+ g.add(d);
78
+ });
79
+
80
+ // Chrome rail along bar top front edge
81
+ var rail = new THREE.Mesh(
82
+ new THREE.CylinderGeometry(0.018, 0.018, 3.0, 10),
83
+ PAL.chrome()
84
+ );
85
+ rail.rotation.z = Math.PI / 2;
86
+ rail.position.set(0, 1.14, 0.58);
87
+ g.add(rail);
88
+
89
+ return g;
90
+ }
91
+ };
@@ -0,0 +1,71 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'bar_stool',
6
+ name: 'Bar Stool',
7
+ category: 'kitchen',
8
+ icon: 'BS',
9
+ gridW: 1, gridD: 1, height: 0.75,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Round seat (dark leather pad)
14
+ var seat = new THREE.Mesh(
15
+ new THREE.CylinderGeometry(0.18, 0.17, 0.06, 20),
16
+ PAL.leatherBlack()
17
+ );
18
+ seat.position.y = 0.75;
19
+ seat.castShadow = true;
20
+ seat.receiveShadow = true;
21
+ g.add(seat);
22
+
23
+ // Seat chrome rim
24
+ var seatRim = new THREE.Mesh(
25
+ new THREE.TorusGeometry(0.18, 0.012, 8, 24),
26
+ PAL.chrome()
27
+ );
28
+ seatRim.rotation.x = Math.PI / 2;
29
+ seatRim.position.y = 0.722;
30
+ g.add(seatRim);
31
+
32
+ // Main chrome post
33
+ var post = new THREE.Mesh(
34
+ new THREE.CylinderGeometry(0.025, 0.025, 0.60, 12),
35
+ PAL.chrome()
36
+ );
37
+ post.position.y = 0.42;
38
+ post.castShadow = true;
39
+ g.add(post);
40
+
41
+ // Pneumatic sleeve (mid-post, slightly wider)
42
+ var sleeve = new THREE.Mesh(
43
+ new THREE.CylinderGeometry(0.036, 0.036, 0.18, 12),
44
+ PAL.chromeBrushed()
45
+ );
46
+ sleeve.position.y = 0.54;
47
+ g.add(sleeve);
48
+
49
+ // Circular base (flat disc)
50
+ var base = new THREE.Mesh(
51
+ new THREE.CylinderGeometry(0.28, 0.30, 0.04, 24),
52
+ PAL.chromeBrushed()
53
+ );
54
+ base.position.y = 0.02;
55
+ base.castShadow = true;
56
+ base.receiveShadow = true;
57
+ g.add(base);
58
+
59
+ // 4 floor glide feet around base rim
60
+ var glideMat = mat(0x111111, { roughness: 0.9 });
61
+ var glideGeo = new THREE.CylinderGeometry(0.022, 0.022, 0.018, 8);
62
+ [0, 1, 2, 3].forEach(function(i) {
63
+ var angle = (i / 4) * Math.PI * 2;
64
+ var glide = new THREE.Mesh(glideGeo, glideMat);
65
+ glide.position.set(Math.cos(angle) * 0.26, 0.009, Math.sin(angle) * 0.26);
66
+ g.add(glide);
67
+ });
68
+
69
+ return g;
70
+ }
71
+ };
@@ -0,0 +1,64 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'beanbag',
6
+ name: 'Beanbag Chair',
7
+ category: 'recreation',
8
+ icon: 'BB',
9
+ gridW: 1, gridD: 1, height: 0.4,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Pick a random color from 4 options
14
+ var colorOptions = [0x2a1f5e, 0x5e1f2a, 0x1f4a2a, 0x4a3a1f];
15
+ var color = colorOptions[Math.floor(Math.random() * colorOptions.length)];
16
+ var trimColors = [0x4433aa, 0xaa3344, 0x338855, 0x887744];
17
+ var trimIdx = colorOptions.indexOf(color);
18
+ var trimColor = trimColors[trimIdx >= 0 ? trimIdx : 0];
19
+
20
+ // Main squashed sphere body
21
+ var body = new THREE.Mesh(
22
+ new THREE.SphereGeometry(0.40, 22, 16),
23
+ mat(color, { roughness: 0.88 })
24
+ );
25
+ body.scale.set(1.0, 0.55, 1.0);
26
+ body.position.y = 0.22;
27
+ body.castShadow = true;
28
+ body.receiveShadow = true;
29
+ g.add(body);
30
+
31
+ // Slightly lighter top section (highlight)
32
+ var top = new THREE.Mesh(
33
+ new THREE.SphereGeometry(0.26, 16, 12),
34
+ mat(trimColor, { roughness: 0.85 })
35
+ );
36
+ top.scale.set(1.0, 0.45, 1.0);
37
+ top.position.y = 0.32;
38
+ g.add(top);
39
+
40
+ // Seam lines (thin dark strips around middle)
41
+ var seamMat = mat(0x111111, { roughness: 0.90 });
42
+ var seamGeo = new THREE.TorusGeometry(0.38, 0.012, 6, 28);
43
+ var seamH = new THREE.Mesh(seamGeo, seamMat);
44
+ seamH.position.y = 0.20;
45
+ g.add(seamH);
46
+
47
+ // Cross seam (vertical ring)
48
+ var seamV = new THREE.Mesh(seamGeo, seamMat);
49
+ seamV.rotation.y = Math.PI / 2;
50
+ seamV.scale.set(0.7, 0.5, 0.7);
51
+ seamV.position.y = 0.22;
52
+ g.add(seamV);
53
+
54
+ // Small logo tag (tiny contrast strip on front)
55
+ var tag = new THREE.Mesh(
56
+ new THREE.BoxGeometry(0.055, 0.025, 0.008),
57
+ mat(0xffffff, { roughness: 0.80 })
58
+ );
59
+ tag.position.set(0, 0.22, 0.40);
60
+ g.add(tag);
61
+
62
+ return g;
63
+ }
64
+ };
@@ -0,0 +1,99 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'bench',
6
+ name: 'Park Bench',
7
+ category: 'exterior',
8
+ icon: 'Bn',
9
+ gridW: 2, gridD: 1, height: 0.8,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ var walnut = PAL.walnutLight();
14
+ var chrome = PAL.chrome();
15
+
16
+ // 3 seat slats
17
+ var slatGeo = new THREE.BoxGeometry(1.50, 0.038, 0.10);
18
+ [-0.14, 0, 0.14].forEach(function(z) {
19
+ var slat = new THREE.Mesh(slatGeo, walnut);
20
+ slat.position.set(0, 0.48, z);
21
+ slat.castShadow = true;
22
+ slat.receiveShadow = true;
23
+ g.add(slat);
24
+ });
25
+
26
+ // 3 back slats (angled slightly)
27
+ var backSlatGeo = new THREE.BoxGeometry(1.50, 0.038, 0.10);
28
+ [0, 0.12, 0.24].forEach(function(i, idx) {
29
+ var slat = new THREE.Mesh(backSlatGeo, walnut);
30
+ slat.rotation.x = -0.18;
31
+ slat.position.set(0, 0.57 + idx * 0.12, -0.24 + idx * 0.03);
32
+ slat.castShadow = true;
33
+ g.add(slat);
34
+ });
35
+
36
+ // Left chrome leg frame (L-shape: vertical + diagonal brace)
37
+ var legGeo = new THREE.BoxGeometry(0.025, 0.48, 0.025);
38
+ var leftLegF = new THREE.Mesh(legGeo, chrome);
39
+ leftLegF.position.set(-0.64, 0.24, 0.16);
40
+ leftLegF.castShadow = true;
41
+ g.add(leftLegF);
42
+
43
+ var leftLegB = new THREE.Mesh(legGeo, chrome);
44
+ leftLegB.position.set(-0.64, 0.24, -0.16);
45
+ leftLegB.castShadow = true;
46
+ g.add(leftLegB);
47
+
48
+ // Right legs
49
+ var rightLegF = leftLegF.clone();
50
+ rightLegF.position.x = 0.64;
51
+ g.add(rightLegF);
52
+
53
+ var rightLegB = leftLegB.clone();
54
+ rightLegB.position.x = 0.64;
55
+ g.add(rightLegB);
56
+
57
+ // Horizontal chrome stretcher bars
58
+ var stretcherGeo = new THREE.BoxGeometry(1.50, 0.020, 0.020);
59
+ var frontStr = new THREE.Mesh(stretcherGeo, chrome);
60
+ frontStr.position.set(0, 0.22, 0.16);
61
+ g.add(frontStr);
62
+
63
+ var backStr = new THREE.Mesh(stretcherGeo, chrome);
64
+ backStr.position.set(0, 0.22, -0.16);
65
+ g.add(backStr);
66
+
67
+ // Back support verticals (angled)
68
+ var backVertGeo = new THREE.BoxGeometry(0.025, 0.44, 0.025);
69
+ var leftBV = new THREE.Mesh(backVertGeo, chrome);
70
+ leftBV.rotation.x = -0.18;
71
+ leftBV.position.set(-0.64, 0.68, -0.22);
72
+ leftBV.castShadow = true;
73
+ g.add(leftBV);
74
+
75
+ var rightBV = leftBV.clone();
76
+ rightBV.position.x = 0.64;
77
+ g.add(rightBV);
78
+
79
+ // Chrome armrests
80
+ var armGeo = new THREE.BoxGeometry(0.025, 0.025, 0.38);
81
+ var leftArm = new THREE.Mesh(armGeo, chrome);
82
+ leftArm.position.set(-0.64, 0.52, -0.01);
83
+ g.add(leftArm);
84
+ var rightArm = leftArm.clone();
85
+ rightArm.position.x = 0.64;
86
+ g.add(rightArm);
87
+
88
+ // Rubber floor pads
89
+ var padMat = mat(0x0a0a0a, { roughness: 0.92 });
90
+ var padGeo = new THREE.CylinderGeometry(0.020, 0.020, 0.012, 8);
91
+ [[-0.64, 0.006, 0.16], [0.64, 0.006, 0.16], [-0.64, 0.006, -0.16], [0.64, 0.006, -0.16]].forEach(function(p) {
92
+ var pad = new THREE.Mesh(padGeo, padMat);
93
+ pad.position.set(p[0], p[1], p[2]);
94
+ g.add(pad);
95
+ });
96
+
97
+ return g;
98
+ }
99
+ };
@@ -0,0 +1,87 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'bollard',
6
+ name: 'Bollard',
7
+ category: 'exterior',
8
+ icon: 'Bo',
9
+ gridW: 1, gridD: 1, height: 0.8,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Main concrete cylindrical body (slightly tapered at bottom)
14
+ var body = new THREE.Mesh(
15
+ new THREE.CylinderGeometry(0.095, 0.110, 0.72, 16),
16
+ PAL.concrete()
17
+ );
18
+ body.position.y = 0.38;
19
+ body.castShadow = true;
20
+ body.receiveShadow = true;
21
+ g.add(body);
22
+
23
+ // Domed cap (rounded top)
24
+ var cap = new THREE.Mesh(
25
+ new THREE.SphereGeometry(0.098, 14, 10, 0, Math.PI * 2, 0, Math.PI * 0.5),
26
+ mat(0x222530, { roughness: 0.75 })
27
+ );
28
+ cap.position.y = 0.740;
29
+ cap.castShadow = true;
30
+ g.add(cap);
31
+
32
+ // Reflective yellow safety band (wide stripe near top)
33
+ var yellowBand = new THREE.Mesh(
34
+ new THREE.CylinderGeometry(0.097, 0.097, 0.065, 16),
35
+ mat(0xffcc00, { roughness: 0.35, metalness: 0.10, emissive: 0xffcc00, emissiveIntensity: 0.15 })
36
+ );
37
+ yellowBand.position.y = 0.62;
38
+ g.add(yellowBand);
39
+
40
+ // Narrow black divider bands above and below yellow
41
+ var divMat = mat(0x0a0a0a, { roughness: 0.75 });
42
+ var divGeo = new THREE.CylinderGeometry(0.098, 0.098, 0.014, 16);
43
+ [0.587, 0.657].forEach(function(y) {
44
+ var div = new THREE.Mesh(divGeo, divMat);
45
+ div.position.y = y;
46
+ g.add(div);
47
+ });
48
+
49
+ // Secondary narrow grey band mid-body
50
+ var greyBand = new THREE.Mesh(
51
+ new THREE.CylinderGeometry(0.1015, 0.1015, 0.022, 16),
52
+ mat(0x3a3d45, { roughness: 0.55 })
53
+ );
54
+ greyBand.position.y = 0.30;
55
+ g.add(greyBand);
56
+
57
+ // Embedded anchor ring at base (chrome)
58
+ var anchor = new THREE.Mesh(
59
+ new THREE.TorusGeometry(0.055, 0.010, 6, 14),
60
+ PAL.chromeBrushed()
61
+ );
62
+ anchor.rotation.x = Math.PI / 2;
63
+ anchor.position.y = 0.055;
64
+ g.add(anchor);
65
+
66
+ // Ground base plate (flush with floor, dark concrete)
67
+ var plate = new THREE.Mesh(
68
+ new THREE.CylinderGeometry(0.130, 0.130, 0.028, 16),
69
+ mat(0x1a1d24, { roughness: 0.90 })
70
+ );
71
+ plate.position.y = 0.014;
72
+ plate.receiveShadow = true;
73
+ g.add(plate);
74
+
75
+ // 4 small anchor bolts around base plate
76
+ var boltMat = PAL.chromeBrushed();
77
+ var boltGeo = new THREE.CylinderGeometry(0.008, 0.008, 0.030, 6);
78
+ [0, 1, 2, 3].forEach(function(i) {
79
+ var angle = (i / 4) * Math.PI * 2 + Math.PI / 4;
80
+ var bolt = new THREE.Mesh(boltGeo, boltMat);
81
+ bolt.position.set(Math.cos(angle) * 0.108, 0.015, Math.sin(angle) * 0.108);
82
+ g.add(bolt);
83
+ });
84
+
85
+ return g;
86
+ }
87
+ };