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
@@ -0,0 +1,102 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'desk_lamp',
6
+ name: 'Desk Lamp',
7
+ category: 'lighting',
8
+ icon: 'DL',
9
+ gridW: 1, gridD: 1, height: 0.5,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ var chromeMat = PAL.chrome();
14
+ var darkMat = PAL.darkMetal();
15
+ var shadeMat = mat(0xe8dcc8, { transparent: true, opacity: 0.75, roughness: 0.45, side: THREE.DoubleSide });
16
+
17
+ // Clamp/weighted base block
18
+ var base = new THREE.Mesh(
19
+ new THREE.BoxGeometry(0.10, 0.035, 0.10),
20
+ darkMat
21
+ );
22
+ base.position.y = 0.018;
23
+ base.castShadow = true;
24
+ g.add(base);
25
+
26
+ // Base chrome collar
27
+ var baseCollar = new THREE.Mesh(
28
+ new THREE.CylinderGeometry(0.018, 0.018, 0.030, 12),
29
+ chromeMat
30
+ );
31
+ baseCollar.position.y = 0.050;
32
+ g.add(baseCollar);
33
+
34
+ // Lower arm (angled backward at ~70 deg)
35
+ var armLow = new THREE.Mesh(
36
+ new THREE.BoxGeometry(0.012, 0.22, 0.012),
37
+ chromeMat
38
+ );
39
+ armLow.position.set(0, 0.165, -0.06);
40
+ armLow.rotation.x = -0.35;
41
+ armLow.castShadow = true;
42
+ g.add(armLow);
43
+
44
+ // Elbow joint knuckle
45
+ var elbow = new THREE.Mesh(
46
+ new THREE.SphereGeometry(0.018, 14, 10),
47
+ chromeMat
48
+ );
49
+ elbow.position.set(0, 0.300, -0.11);
50
+ g.add(elbow);
51
+
52
+ // Upper arm (angled forward and up)
53
+ var armUp = new THREE.Mesh(
54
+ new THREE.BoxGeometry(0.010, 0.20, 0.010),
55
+ chromeMat
56
+ );
57
+ armUp.position.set(0, 0.415, -0.045);
58
+ armUp.rotation.x = 0.55;
59
+ armUp.castShadow = true;
60
+ g.add(armUp);
61
+
62
+ // Head joint
63
+ var headJoint = new THREE.Mesh(
64
+ new THREE.SphereGeometry(0.015, 12, 8),
65
+ chromeMat
66
+ );
67
+ headJoint.position.set(0, 0.500, 0.030);
68
+ g.add(headJoint);
69
+
70
+ // Shade (small cone, opening downward)
71
+ var shade = new THREE.Mesh(
72
+ new THREE.ConeGeometry(0.080, 0.095, 28, 1, true),
73
+ shadeMat
74
+ );
75
+ shade.position.set(0, 0.465, 0.030);
76
+ shade.rotation.x = Math.PI;
77
+ g.add(shade);
78
+
79
+ // Shade chrome rim
80
+ var shadeRim = new THREE.Mesh(
81
+ new THREE.TorusGeometry(0.078, 0.004, 8, 28),
82
+ chromeMat
83
+ );
84
+ shadeRim.position.set(0, 0.422, 0.030);
85
+ g.add(shadeRim);
86
+
87
+ // Warm bulb inside shade
88
+ var bulb = new THREE.Mesh(
89
+ new THREE.SphereGeometry(0.018, 12, 8),
90
+ mat(0xfff0cc, { emissive: 0xfff0cc, emissiveIntensity: 1.3, transparent: true, opacity: 0.90 })
91
+ );
92
+ bulb.position.set(0, 0.455, 0.030);
93
+ g.add(bulb);
94
+
95
+ // Point light
96
+ var light = new THREE.PointLight(0xffeedd, 0.2, 3);
97
+ light.position.set(0, 0.42, 0.030);
98
+ g.add(light);
99
+
100
+ return g;
101
+ }
102
+ };
@@ -0,0 +1,76 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'desk',
6
+ name: 'Desk',
7
+ category: 'furniture',
8
+ icon: 'Dk',
9
+ gridW: 2, gridD: 1, height: 0.76,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Tabletop
14
+ var top = new THREE.Mesh(
15
+ new THREE.BoxGeometry(2, 0.05, 0.9),
16
+ mat(0x1e2128, { roughness: 0.35, metalness: 0.05 })
17
+ );
18
+ top.position.y = 0.755;
19
+ top.castShadow = true;
20
+ top.receiveShadow = true;
21
+ g.add(top);
22
+
23
+ // Thin edge trim on front
24
+ var edgeTrim = new THREE.Mesh(
25
+ new THREE.BoxGeometry(2, 0.03, 0.02),
26
+ mat(0xd4af37, { roughness: 0.25, metalness: 0.75 })
27
+ );
28
+ edgeTrim.position.y = 0.73;
29
+ edgeTrim.position.z = 0.46;
30
+ g.add(edgeTrim);
31
+
32
+ // Left side panel (apron)
33
+ var leftApron = new THREE.Mesh(
34
+ new THREE.BoxGeometry(0.03, 0.12, 0.86),
35
+ mat(0x17191f, { roughness: 0.5 })
36
+ );
37
+ leftApron.position.set(-0.975, 0.665, 0);
38
+ leftApron.castShadow = true;
39
+ g.add(leftApron);
40
+
41
+ // Right side panel
42
+ var rightApron = leftApron.clone();
43
+ rightApron.position.x = 0.975;
44
+ g.add(rightApron);
45
+
46
+ // Back apron
47
+ var backApron = new THREE.Mesh(
48
+ new THREE.BoxGeometry(1.94, 0.12, 0.03),
49
+ mat(0x17191f, { roughness: 0.5 })
50
+ );
51
+ backApron.position.set(0, 0.665, -0.44);
52
+ g.add(backApron);
53
+
54
+ // 4 legs — dark metal, tapered feel via scale
55
+ var legMat = mat(0x2a2d35, { roughness: 0.4, metalness: 0.15 });
56
+ var legGeo = new THREE.BoxGeometry(0.07, 0.73, 0.07);
57
+ var positions = [[-0.92, 0.365, 0.38], [0.92, 0.365, 0.38], [-0.92, 0.365, -0.38], [0.92, 0.365, -0.38]];
58
+ positions.forEach(function(p) {
59
+ var leg = new THREE.Mesh(legGeo, legMat);
60
+ leg.position.set(p[0], p[1], p[2]);
61
+ leg.castShadow = true;
62
+ g.add(leg);
63
+ });
64
+
65
+ // Floor glides (small feet pads)
66
+ var glideMat = mat(0x111111, { roughness: 0.9 });
67
+ var glideGeo = new THREE.CylinderGeometry(0.04, 0.04, 0.015, 8);
68
+ positions.forEach(function(p) {
69
+ var glide = new THREE.Mesh(glideGeo, glideMat);
70
+ glide.position.set(p[0], 0.008, p[2]);
71
+ g.add(glide);
72
+ });
73
+
74
+ return g;
75
+ }
76
+ };
@@ -0,0 +1,105 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'dining-table',
6
+ name: 'Dining Table',
7
+ category: 'furniture',
8
+ icon: 'DT',
9
+ gridW: 3, gridD: 2, height: 0.78,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Walnut tabletop
14
+ var top = new THREE.Mesh(
15
+ new THREE.BoxGeometry(3.0, 0.06, 1.2),
16
+ PAL.walnutDark()
17
+ );
18
+ top.position.y = 0.78;
19
+ top.castShadow = true;
20
+ top.receiveShadow = true;
21
+ g.add(top);
22
+
23
+ // Thin edge band (darker walnut contrast)
24
+ var edgeFront = new THREE.Mesh(
25
+ new THREE.BoxGeometry(3.0, 0.055, 0.018),
26
+ mat(0x2a1808, { roughness: 0.6 })
27
+ );
28
+ edgeFront.position.set(0, 0.778, 0.609);
29
+ g.add(edgeFront);
30
+
31
+ var edgeBack = edgeFront.clone();
32
+ edgeBack.position.z = -0.609;
33
+ g.add(edgeBack);
34
+
35
+ // Apron/torsion box under top
36
+ var apronFront = new THREE.Mesh(
37
+ new THREE.BoxGeometry(2.86, 0.09, 0.04),
38
+ PAL.walnutDark()
39
+ );
40
+ apronFront.position.set(0, 0.705, 0.54);
41
+ apronFront.castShadow = true;
42
+ g.add(apronFront);
43
+
44
+ var apronBack = apronFront.clone();
45
+ apronBack.position.z = -0.54;
46
+ g.add(apronBack);
47
+
48
+ var apronLeft = new THREE.Mesh(
49
+ new THREE.BoxGeometry(0.04, 0.09, 1.08),
50
+ PAL.walnutDark()
51
+ );
52
+ apronLeft.position.set(-1.43, 0.705, 0);
53
+ g.add(apronLeft);
54
+
55
+ var apronRight = apronLeft.clone();
56
+ apronRight.position.x = 1.43;
57
+ g.add(apronRight);
58
+
59
+ // 6 chrome legs in 3 pairs
60
+ var legMat = PAL.chrome();
61
+ var legGeo = new THREE.CylinderGeometry(0.038, 0.032, 0.75, 12);
62
+ var legPositions = [
63
+ [-1.32, -0.46], [-1.32, 0.46],
64
+ [0, -0.46], [0, 0.46],
65
+ [1.32, -0.46], [1.32, 0.46]
66
+ ];
67
+ legPositions.forEach(function(p) {
68
+ var leg = new THREE.Mesh(legGeo, legMat);
69
+ leg.position.set(p[0], 0.375, p[1]);
70
+ leg.castShadow = true;
71
+ g.add(leg);
72
+ });
73
+
74
+ // Horizontal stretcher bars connecting each pair
75
+ var stretchMat = PAL.chromeBrushed();
76
+ [[-1.32], [0], [1.32]].forEach(function(px) {
77
+ var bar = new THREE.Mesh(new THREE.BoxGeometry(0.03, 0.03, 0.9), stretchMat);
78
+ bar.position.set(px[0], 0.22, 0);
79
+ g.add(bar);
80
+ });
81
+
82
+ // Longitudinal bars connecting front legs
83
+ var longBarFront = new THREE.Mesh(
84
+ new THREE.BoxGeometry(2.64, 0.025, 0.025),
85
+ stretchMat
86
+ );
87
+ longBarFront.position.set(0, 0.22, -0.46);
88
+ g.add(longBarFront);
89
+
90
+ var longBarBack = longBarFront.clone();
91
+ longBarBack.position.z = 0.46;
92
+ g.add(longBarBack);
93
+
94
+ // Floor glides
95
+ var glideMat = mat(0x111111, { roughness: 0.9 });
96
+ var glideGeo = new THREE.CylinderGeometry(0.04, 0.04, 0.012, 8);
97
+ legPositions.forEach(function(p) {
98
+ var glide = new THREE.Mesh(glideGeo, glideMat);
99
+ glide.position.set(p[0], 0.006, p[1]);
100
+ g.add(glide);
101
+ });
102
+
103
+ return g;
104
+ }
105
+ };
@@ -0,0 +1,70 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'door',
6
+ name: 'Door',
7
+ category: 'structural',
8
+ icon: 'Dr',
9
+ gridW: 1, gridD: 1, height: 2.5,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Door slab — walnut
14
+ var slab = new THREE.Mesh(
15
+ new THREE.BoxGeometry(1, 2.5, 0.08),
16
+ PAL.walnutDark()
17
+ );
18
+ slab.castShadow = true;
19
+ slab.receiveShadow = true;
20
+ g.add(slab);
21
+
22
+ // Recessed panel (upper)
23
+ var panelU = new THREE.Mesh(
24
+ new THREE.BoxGeometry(0.75, 1.0, 0.01),
25
+ PAL.walnutLight()
26
+ );
27
+ panelU.position.y = 0.6;
28
+ panelU.position.z = 0.045;
29
+ g.add(panelU);
30
+
31
+ // Recessed panel (lower)
32
+ var panelL = new THREE.Mesh(
33
+ new THREE.BoxGeometry(0.75, 0.75, 0.01),
34
+ PAL.walnutLight()
35
+ );
36
+ panelL.position.y = -0.6;
37
+ panelL.position.z = 0.045;
38
+ g.add(panelL);
39
+
40
+ // Chrome handle bar
41
+ var handle = new THREE.Mesh(
42
+ new THREE.CylinderGeometry(0.018, 0.018, 0.12, 8),
43
+ PAL.chrome()
44
+ );
45
+ handle.rotation.z = Math.PI / 2;
46
+ handle.position.set(0.38, 0, 0.07);
47
+ handle.castShadow = true;
48
+ g.add(handle);
49
+
50
+ // Handle back plate
51
+ var plate = new THREE.Mesh(
52
+ new THREE.BoxGeometry(0.04, 0.18, 0.015),
53
+ PAL.chromeBrushed()
54
+ );
55
+ plate.position.set(0.38, 0, 0.048);
56
+ g.add(plate);
57
+
58
+ // Door frame surround
59
+ var frameMat = mat(0x1a1c22, { roughness: 0.6 });
60
+ var frameTop = new THREE.Mesh(new THREE.BoxGeometry(1.1, 0.07, 0.1), frameMat);
61
+ frameTop.position.y = 1.285;
62
+ g.add(frameTop);
63
+ var frameL = new THREE.Mesh(new THREE.BoxGeometry(0.07, 2.5, 0.1), frameMat);
64
+ frameL.position.x = -0.535;
65
+ g.add(frameL);
66
+ var frameR = frameL.clone(); frameR.position.x = 0.535; g.add(frameR);
67
+
68
+ return g;
69
+ }
70
+ };
@@ -0,0 +1,72 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'dual_monitor',
6
+ name: 'Dual Monitor',
7
+ category: 'tech',
8
+ icon: 'DM',
9
+ gridW: 1, gridD: 1, height: 0.5,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ var chromeMat = PAL.chrome();
14
+ var bezelMat = mat(0x111214, { roughness: 0.45, metalness: 0.10 });
15
+ var screenMat = mat(0x0a1628, { emissive: 0x1a4a8a, emissiveIntensity: 0.55, roughness: 0.05 });
16
+
17
+ // Build one monitor panel (reused twice)
18
+ function makePanel(xOffset) {
19
+ var grp = new THREE.Group();
20
+
21
+ var bezel = new THREE.Mesh(new THREE.BoxGeometry(0.44, 0.30, 0.025), bezelMat);
22
+ bezel.position.y = 0.38;
23
+ bezel.castShadow = true;
24
+ grp.add(bezel);
25
+
26
+ var screen = new THREE.Mesh(new THREE.BoxGeometry(0.38, 0.25, 0.010), screenMat);
27
+ screen.position.set(0, 0.38, 0.013);
28
+ grp.add(screen);
29
+
30
+ // thin side border accent
31
+ var accent = new THREE.Mesh(new THREE.BoxGeometry(0.005, 0.30, 0.026), chromeMat);
32
+ accent.position.set(0.22, 0.38, 0);
33
+ grp.add(accent);
34
+
35
+ grp.position.x = xOffset;
36
+ return grp;
37
+ }
38
+
39
+ g.add(makePanel(-0.235));
40
+ g.add(makePanel( 0.235));
41
+
42
+ // Shared horizontal arm bar
43
+ var arm = new THREE.Mesh(new THREE.BoxGeometry(0.52, 0.022, 0.022), chromeMat);
44
+ arm.position.y = 0.22;
45
+ arm.castShadow = true;
46
+ g.add(arm);
47
+
48
+ // Vertical pole from arm to base
49
+ var pole = new THREE.Mesh(new THREE.BoxGeometry(0.025, 0.22, 0.025), chromeMat);
50
+ pole.position.y = 0.11;
51
+ pole.castShadow = true;
52
+ g.add(pole);
53
+
54
+ // Heavy weighted base
55
+ var base = new THREE.Mesh(new THREE.BoxGeometry(0.28, 0.028, 0.18), chromeMat);
56
+ base.position.y = 0.014;
57
+ base.castShadow = true;
58
+ g.add(base);
59
+
60
+ // Two LED dots (one per screen)
61
+ [-0.235, 0.235].forEach(function(x) {
62
+ var led = new THREE.Mesh(
63
+ new THREE.SphereGeometry(0.007, 8, 8),
64
+ mat(0x00e5ff, { emissive: 0x00e5ff, emissiveIntensity: 1.0 })
65
+ );
66
+ led.position.set(x + 0.16, 0.215, 0.014);
67
+ g.add(led);
68
+ });
69
+
70
+ return g;
71
+ }
72
+ };
@@ -0,0 +1,76 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'fence',
6
+ name: 'Metal Fence',
7
+ category: 'exterior',
8
+ icon: 'Fn',
9
+ gridW: 2, gridD: 1, height: 1.2,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ var chrome = PAL.chrome();
14
+ var darkMetal = PAL.darkMetal();
15
+
16
+ // Left post
17
+ var postGeo = new THREE.BoxGeometry(0.048, 1.20, 0.048);
18
+ var leftPost = new THREE.Mesh(postGeo, chrome);
19
+ leftPost.position.set(-0.97, 0.60, 0);
20
+ leftPost.castShadow = true;
21
+ g.add(leftPost);
22
+
23
+ // Right post
24
+ var rightPost = leftPost.clone();
25
+ rightPost.position.x = 0.97;
26
+ g.add(rightPost);
27
+
28
+ // Post caps (small pyramidal tops)
29
+ var capGeo = new THREE.CylinderGeometry(0.0, 0.034, 0.055, 4);
30
+ var capMat = PAL.chromeBrushed();
31
+ [-0.97, 0.97].forEach(function(x) {
32
+ var cap = new THREE.Mesh(capGeo, capMat);
33
+ cap.position.set(x, 1.228, 0);
34
+ g.add(cap);
35
+ });
36
+
37
+ // 4 horizontal bars at varying heights
38
+ var barGeo = new THREE.BoxGeometry(1.94, 0.030, 0.030);
39
+ [0.16, 0.52, 0.88, 1.14].forEach(function(y) {
40
+ var bar = new THREE.Mesh(barGeo, darkMetal);
41
+ bar.position.y = y;
42
+ g.add(bar);
43
+ });
44
+
45
+ // Vertical pickets (7 between posts)
46
+ var picketMat = darkMetal;
47
+ var picketGeo = new THREE.BoxGeometry(0.022, 0.98, 0.022);
48
+ var picketCount = 7;
49
+ for (var i = 0; i < picketCount; i++) {
50
+ var x = -0.84 + (i / (picketCount - 1)) * 1.68;
51
+ var picket = new THREE.Mesh(picketGeo, picketMat);
52
+ picket.position.set(x, 0.60, 0);
53
+ picket.castShadow = true;
54
+ g.add(picket);
55
+ }
56
+
57
+ // Picket spear tips (sharp top points)
58
+ var spearGeo = new THREE.CylinderGeometry(0.0, 0.016, 0.055, 4);
59
+ for (var j = 0; j < picketCount; j++) {
60
+ var sx = -0.84 + (j / (picketCount - 1)) * 1.68;
61
+ var spear = new THREE.Mesh(spearGeo, PAL.chrome());
62
+ spear.position.set(sx, 1.12, 0);
63
+ g.add(spear);
64
+ }
65
+
66
+ // Base footer (ground anchor bar)
67
+ var footer = new THREE.Mesh(
68
+ new THREE.BoxGeometry(1.98, 0.040, 0.06),
69
+ mat(0x0e1014, { roughness: 0.60 })
70
+ );
71
+ footer.position.y = 0.020;
72
+ g.add(footer);
73
+
74
+ return g;
75
+ }
76
+ };
@@ -0,0 +1,111 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'filing-cabinet',
6
+ name: 'Filing Cabinet',
7
+ category: 'office',
8
+ icon: 'FC',
9
+ gridW: 1, gridD: 1, height: 1.2,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Main body
14
+ var body = new THREE.Mesh(
15
+ new THREE.BoxGeometry(0.5, 1.2, 0.5),
16
+ mat(0x1e2128, { roughness: 0.55, metalness: 0.20 })
17
+ );
18
+ body.position.y = 0.6;
19
+ body.castShadow = true;
20
+ body.receiveShadow = true;
21
+ g.add(body);
22
+
23
+ // Top cap
24
+ var topCap = new THREE.Mesh(
25
+ new THREE.BoxGeometry(0.52, 0.02, 0.52),
26
+ mat(0x252830, { roughness: 0.4, metalness: 0.25 })
27
+ );
28
+ topCap.position.y = 1.21;
29
+ topCap.castShadow = true;
30
+ g.add(topCap);
31
+
32
+ // 3 drawer faces
33
+ var drawerMat = mat(0x252830, { roughness: 0.45, metalness: 0.22 });
34
+ var handleMat = PAL.chrome();
35
+ var drawerHeights = [0.22, 0.62, 1.02];
36
+
37
+ drawerHeights.forEach(function(y) {
38
+ // Drawer face panel
39
+ var drawer = new THREE.Mesh(
40
+ new THREE.BoxGeometry(0.46, 0.34, 0.01),
41
+ drawerMat
42
+ );
43
+ drawer.position.set(0, y, 0.256);
44
+ g.add(drawer);
45
+
46
+ // Drawer inset shadow line (top)
47
+ var topLine = new THREE.Mesh(
48
+ new THREE.BoxGeometry(0.46, 0.008, 0.012),
49
+ mat(0x111318, { roughness: 0.8 })
50
+ );
51
+ topLine.position.set(0, y + 0.171, 0.257);
52
+ g.add(topLine);
53
+
54
+ // Drawer inset shadow line (bottom)
55
+ var botLine = topLine.clone();
56
+ botLine.position.y = y - 0.171;
57
+ g.add(botLine);
58
+
59
+ // Handle bar
60
+ var handle = new THREE.Mesh(
61
+ new THREE.BoxGeometry(0.22, 0.022, 0.022),
62
+ handleMat
63
+ );
64
+ handle.position.set(0, y, 0.268);
65
+ handle.castShadow = true;
66
+ g.add(handle);
67
+
68
+ // Handle end brackets
69
+ var bracket = new THREE.Mesh(
70
+ new THREE.BoxGeometry(0.022, 0.04, 0.025),
71
+ handleMat
72
+ );
73
+ bracket.position.set(-0.11, y - 0.01, 0.266);
74
+ g.add(bracket);
75
+ var bracketR = bracket.clone();
76
+ bracketR.position.x = 0.11;
77
+ g.add(bracketR);
78
+ });
79
+
80
+ // Lock cylinder on top drawer
81
+ var lock = new THREE.Mesh(
82
+ new THREE.CylinderGeometry(0.015, 0.015, 0.015, 12),
83
+ mat(0xb8860b, { roughness: 0.3, metalness: 0.8 })
84
+ );
85
+ lock.rotation.x = Math.PI / 2;
86
+ lock.position.set(0.14, 1.02, 0.258);
87
+ g.add(lock);
88
+
89
+ // Side ventilation slots (decorative lines)
90
+ var slotMat = mat(0x17191f, { roughness: 0.8 });
91
+ [0.2, 0.6, 1.0].forEach(function(y) {
92
+ var slot = new THREE.Mesh(new THREE.BoxGeometry(0.008, 0.04, 0.42), slotMat);
93
+ slot.position.set(-0.252, y, 0);
94
+ g.add(slot);
95
+ var slotR = slot.clone();
96
+ slotR.position.x = 0.252;
97
+ g.add(slotR);
98
+ });
99
+
100
+ // Small base feet
101
+ var footMat = mat(0x111111, { roughness: 0.9 });
102
+ var footGeo = new THREE.BoxGeometry(0.08, 0.04, 0.08);
103
+ [[-0.18, -0.18], [0.18, -0.18], [-0.18, 0.18], [0.18, 0.18]].forEach(function(p) {
104
+ var foot = new THREE.Mesh(footGeo, footMat);
105
+ foot.position.set(p[0], 0.02, p[1]);
106
+ g.add(foot);
107
+ });
108
+
109
+ return g;
110
+ }
111
+ };
@@ -0,0 +1,69 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'floor_lamp',
6
+ name: 'Floor Lamp',
7
+ category: 'lighting',
8
+ icon: 'FL',
9
+ gridW: 1, gridD: 1, height: 1.8,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ var poleMat = PAL.chromeBrushed();
14
+ var shadeMat = mat(0xf5e8d0, { transparent: true, opacity: 0.70, roughness: 0.50, side: THREE.DoubleSide });
15
+ var baseMat = mat(0x1a1c22, { roughness: 0.40, metalness: 0.55 });
16
+
17
+ // Round weighted base disk
18
+ var base = new THREE.Mesh(new THREE.CylinderGeometry(0.20, 0.22, 0.04, 32), baseMat);
19
+ base.position.y = 0.020;
20
+ base.castShadow = true;
21
+ g.add(base);
22
+
23
+ // Base rim chrome ring
24
+ var rim = new THREE.Mesh(new THREE.TorusGeometry(0.21, 0.010, 8, 32), PAL.chrome());
25
+ rim.position.y = 0.040;
26
+ g.add(rim);
27
+
28
+ // Tall pole (two sections for realism)
29
+ var poleBot = new THREE.Mesh(new THREE.CylinderGeometry(0.016, 0.018, 1.05, 16), poleMat);
30
+ poleBot.position.y = 0.065 + 0.525;
31
+ poleBot.castShadow = true;
32
+ g.add(poleBot);
33
+
34
+ var poleTop = new THREE.Mesh(new THREE.CylinderGeometry(0.012, 0.016, 0.65, 16), poleMat);
35
+ poleTop.position.y = 0.065 + 1.05 + 0.325;
36
+ poleTop.castShadow = true;
37
+ g.add(poleTop);
38
+
39
+ // Pole join collar
40
+ var collar = new THREE.Mesh(new THREE.CylinderGeometry(0.022, 0.022, 0.030, 16), PAL.chrome());
41
+ collar.position.y = 0.065 + 1.05;
42
+ g.add(collar);
43
+
44
+ // Cone shade
45
+ var shade = new THREE.Mesh(new THREE.ConeGeometry(0.22, 0.28, 32, 1, true), shadeMat);
46
+ shade.position.y = 1.65;
47
+ g.add(shade);
48
+
49
+ // Shade top cap
50
+ var topCap = new THREE.Mesh(new THREE.CylinderGeometry(0.022, 0.022, 0.025, 16), poleMat);
51
+ topCap.position.y = 1.80;
52
+ g.add(topCap);
53
+
54
+ // Warm bulb glow sphere
55
+ var bulb = new THREE.Mesh(
56
+ new THREE.SphereGeometry(0.030, 16, 12),
57
+ mat(0xffeedd, { emissive: 0xffeedd, emissiveIntensity: 1.2, transparent: true, opacity: 0.90 })
58
+ );
59
+ bulb.position.y = 1.68;
60
+ g.add(bulb);
61
+
62
+ // Point light
63
+ var light = new THREE.PointLight(0xffeedd, 0.3, 4);
64
+ light.position.y = 1.65;
65
+ g.add(light);
66
+
67
+ return g;
68
+ }
69
+ };