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,97 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'street_lamp',
6
+ name: 'Street Lamp',
7
+ category: 'exterior',
8
+ icon: 'SL',
9
+ gridW: 1, gridD: 1, height: 3.5,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Main pole — dark metal, slight taper
14
+ var pole = new THREE.Mesh(
15
+ new THREE.CylinderGeometry(0.040, 0.060, 3.20, 12),
16
+ PAL.darkMetal()
17
+ );
18
+ pole.position.y = 1.60;
19
+ pole.castShadow = true;
20
+ g.add(pole);
21
+
22
+ // Pole base collar (decorative flared ring)
23
+ var collar = new THREE.Mesh(
24
+ new THREE.CylinderGeometry(0.110, 0.130, 0.10, 16),
25
+ mat(0x1a1c22, { roughness: 0.45, metalness: 0.25 })
26
+ );
27
+ collar.position.y = 0.05;
28
+ collar.castShadow = true;
29
+ g.add(collar);
30
+
31
+ // Base plate
32
+ var basePlate = new THREE.Mesh(
33
+ new THREE.CylinderGeometry(0.130, 0.130, 0.045, 16),
34
+ PAL.darkMetal()
35
+ );
36
+ basePlate.position.y = 0.022;
37
+ g.add(basePlate);
38
+
39
+ // Decorative ring mid-pole
40
+ var midRing = new THREE.Mesh(
41
+ new THREE.TorusGeometry(0.055, 0.014, 8, 18),
42
+ mat(0x2a2d35, { roughness: 0.40, metalness: 0.30 })
43
+ );
44
+ midRing.position.y = 1.80;
45
+ g.add(midRing);
46
+
47
+ // Curved arm / goose-neck extending from top
48
+ var arm = new THREE.Mesh(
49
+ new THREE.CylinderGeometry(0.022, 0.030, 0.50, 10),
50
+ PAL.darkMetal()
51
+ );
52
+ arm.rotation.z = Math.PI / 4;
53
+ arm.position.set(0.18, 3.28, 0);
54
+ arm.castShadow = true;
55
+ g.add(arm);
56
+
57
+ // Lantern head housing
58
+ var lanternBody = new THREE.Mesh(
59
+ new THREE.CylinderGeometry(0.090, 0.110, 0.20, 10),
60
+ mat(0x111318, { roughness: 0.40, metalness: 0.25 })
61
+ );
62
+ lanternBody.position.set(0.32, 3.44, 0);
63
+ lanternBody.castShadow = true;
64
+ g.add(lanternBody);
65
+
66
+ // Lantern top cap
67
+ var lanternCap = new THREE.Mesh(
68
+ new THREE.CylinderGeometry(0.025, 0.095, 0.06, 10),
69
+ PAL.darkMetal()
70
+ );
71
+ lanternCap.position.set(0.32, 3.57, 0);
72
+ g.add(lanternCap);
73
+
74
+ // Lantern glass (warm glowing panel)
75
+ var glass = new THREE.Mesh(
76
+ new THREE.CylinderGeometry(0.085, 0.085, 0.15, 10),
77
+ mat(0xfff0cc, { transparent: true, opacity: 0.65, emissive: 0xffcc66, emissiveIntensity: 0.70, roughness: 0.10 })
78
+ );
79
+ glass.position.set(0.32, 3.44, 0);
80
+ g.add(glass);
81
+
82
+ // Lantern bottom diffuser disc
83
+ var diffuser = new THREE.Mesh(
84
+ new THREE.CylinderGeometry(0.082, 0.082, 0.010, 10),
85
+ mat(0xffeedd, { transparent: true, opacity: 0.80, emissive: 0xffcc55, emissiveIntensity: 0.60 })
86
+ );
87
+ diffuser.position.set(0.32, 3.34, 0);
88
+ g.add(diffuser);
89
+
90
+ // PointLight — warm street glow
91
+ var light = new THREE.PointLight(0xffcc66, 1.2, 6.0);
92
+ light.position.set(0.32, 3.35, 0);
93
+ g.add(light);
94
+
95
+ return g;
96
+ }
97
+ };
@@ -0,0 +1,83 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'trash_can',
6
+ name: 'Trash Can',
7
+ category: 'exterior',
8
+ icon: 'TC',
9
+ gridW: 1, gridD: 1, height: 0.7,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Main cylindrical body — dark metal, slightly tapered (wider at top)
14
+ var body = new THREE.Mesh(
15
+ new THREE.CylinderGeometry(0.145, 0.120, 0.62, 18),
16
+ mat(0x1a1c22, { roughness: 0.50, metalness: 0.30 })
17
+ );
18
+ body.position.y = 0.34;
19
+ body.castShadow = true;
20
+ body.receiveShadow = true;
21
+ g.add(body);
22
+
23
+ // Chrome top rim ring
24
+ var rim = new THREE.Mesh(
25
+ new THREE.TorusGeometry(0.148, 0.016, 8, 22),
26
+ PAL.chrome()
27
+ );
28
+ rim.rotation.x = Math.PI / 2;
29
+ rim.position.y = 0.656;
30
+ g.add(rim);
31
+
32
+ // Chrome bottom rim ring
33
+ var bottomRim = new THREE.Mesh(
34
+ new THREE.TorusGeometry(0.124, 0.012, 8, 22),
35
+ PAL.chrome()
36
+ );
37
+ bottomRim.rotation.x = Math.PI / 2;
38
+ bottomRim.position.y = 0.032;
39
+ g.add(bottomRim);
40
+
41
+ // Lid (slightly domed cap)
42
+ var lid = new THREE.Mesh(
43
+ new THREE.CylinderGeometry(0.148, 0.148, 0.040, 18),
44
+ mat(0x111318, { roughness: 0.45, metalness: 0.25 })
45
+ );
46
+ lid.position.y = 0.680;
47
+ lid.castShadow = true;
48
+ g.add(lid);
49
+
50
+ // Lid handle (small chrome knob)
51
+ var knob = new THREE.Mesh(
52
+ new THREE.CylinderGeometry(0.022, 0.022, 0.032, 10),
53
+ PAL.chrome()
54
+ );
55
+ knob.position.y = 0.716;
56
+ g.add(knob);
57
+
58
+ // Vertical embossed ribs on body (8 ribs)
59
+ var ribMat = mat(0x0e1014, { roughness: 0.55 });
60
+ var ribGeo = new THREE.BoxGeometry(0.012, 0.58, 0.014);
61
+ for (var i = 0; i < 8; i++) {
62
+ var angle = (i / 8) * Math.PI * 2;
63
+ var rib = new THREE.Mesh(ribGeo, ribMat);
64
+ rib.position.set(Math.cos(angle) * 0.138, 0.34, Math.sin(angle) * 0.138);
65
+ rib.rotation.y = -angle;
66
+ g.add(rib);
67
+ }
68
+
69
+ // Perforated pattern dots (small recessed circles on front face — 3x4 grid)
70
+ var holeMat = mat(0x0a0c10, { roughness: 0.70 });
71
+ var holeGeo = new THREE.CylinderGeometry(0.010, 0.010, 0.008, 8);
72
+ [-1, 0, 1].forEach(function(col) {
73
+ [0.55, 0.42, 0.30, 0.18].forEach(function(y) {
74
+ var hole = new THREE.Mesh(holeGeo, holeMat);
75
+ hole.rotation.x = Math.PI / 2;
76
+ hole.position.set(col * 0.038, y, 0.142);
77
+ g.add(hole);
78
+ });
79
+ });
80
+
81
+ return g;
82
+ }
83
+ };
@@ -0,0 +1,126 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'treadmill',
6
+ name: 'Treadmill',
7
+ category: 'recreation',
8
+ icon: 'TM',
9
+ gridW: 1, gridD: 2, height: 1.2,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Base frame
14
+ var base = new THREE.Mesh(
15
+ new THREE.BoxGeometry(0.70, 0.20, 1.60),
16
+ mat(0x111318, { roughness: 0.45, metalness: 0.20 })
17
+ );
18
+ base.position.y = 0.10;
19
+ base.castShadow = true;
20
+ base.receiveShadow = true;
21
+ g.add(base);
22
+
23
+ // Running belt surface (dark rubber)
24
+ var belt = new THREE.Mesh(
25
+ new THREE.BoxGeometry(0.54, 0.018, 1.38),
26
+ mat(0x1a1a1a, { roughness: 0.92 })
27
+ );
28
+ belt.position.y = 0.212;
29
+ belt.receiveShadow = true;
30
+ g.add(belt);
31
+
32
+ // Belt edge stripes (yellow safety lines)
33
+ var stripeMat = mat(0xeecc00, { roughness: 0.70 });
34
+ var stripeGeo = new THREE.BoxGeometry(0.025, 0.020, 1.38);
35
+ [-0.255, 0.255].forEach(function(x) {
36
+ var stripe = new THREE.Mesh(stripeGeo, stripeMat);
37
+ stripe.position.set(x, 0.222, 0);
38
+ g.add(stripe);
39
+ });
40
+
41
+ // Front roller drum
42
+ var rollerGeo = new THREE.CylinderGeometry(0.055, 0.055, 0.54, 14);
43
+ var rollerMat = PAL.chromeBrushed();
44
+ var frontRoller = new THREE.Mesh(rollerGeo, rollerMat);
45
+ frontRoller.rotation.z = Math.PI / 2;
46
+ frontRoller.position.set(0, 0.21, -0.70);
47
+ frontRoller.castShadow = true;
48
+ g.add(frontRoller);
49
+
50
+ // Rear roller
51
+ var rearRoller = new THREE.Mesh(rollerGeo, rollerMat);
52
+ rearRoller.rotation.z = Math.PI / 2;
53
+ rearRoller.position.set(0, 0.21, 0.70);
54
+ rearRoller.castShadow = true;
55
+ g.add(rearRoller);
56
+
57
+ // Left handlebar upright
58
+ var upMat = mat(0x1e2128, { roughness: 0.40, metalness: 0.30 });
59
+ var upGeo = new THREE.CylinderGeometry(0.022, 0.022, 0.92, 10);
60
+ var leftUp = new THREE.Mesh(upGeo, upMat);
61
+ leftUp.position.set(-0.28, 0.66, -0.45);
62
+ leftUp.castShadow = true;
63
+ g.add(leftUp);
64
+
65
+ // Right handlebar upright
66
+ var rightUp = leftUp.clone();
67
+ rightUp.position.x = 0.28;
68
+ g.add(rightUp);
69
+
70
+ // Crossbar connecting uprights
71
+ var crossbar = new THREE.Mesh(
72
+ new THREE.CylinderGeometry(0.018, 0.018, 0.56, 10),
73
+ upMat
74
+ );
75
+ crossbar.rotation.z = Math.PI / 2;
76
+ crossbar.position.set(0, 1.08, -0.45);
77
+ crossbar.castShadow = true;
78
+ g.add(crossbar);
79
+
80
+ // Handlebar grips (foam-style, slightly wider)
81
+ var gripMat = mat(0x222222, { roughness: 0.90 });
82
+ var gripGeo = new THREE.CylinderGeometry(0.028, 0.028, 0.22, 10);
83
+ var leftGrip = new THREE.Mesh(gripGeo, gripMat);
84
+ leftGrip.rotation.z = Math.PI / 2;
85
+ leftGrip.position.set(-0.19, 1.10, -0.45);
86
+ g.add(leftGrip);
87
+ var rightGrip = leftGrip.clone();
88
+ rightGrip.position.x = 0.19;
89
+ g.add(rightGrip);
90
+
91
+ // Console display panel
92
+ var consoleStem = new THREE.Mesh(
93
+ new THREE.BoxGeometry(0.06, 0.12, 0.04),
94
+ upMat
95
+ );
96
+ consoleStem.position.set(0, 1.16, -0.45);
97
+ g.add(consoleStem);
98
+
99
+ var console_ = new THREE.Mesh(
100
+ new THREE.BoxGeometry(0.36, 0.18, 0.06),
101
+ mat(0x111318, { roughness: 0.45 })
102
+ );
103
+ console_.position.set(0, 1.26, -0.46);
104
+ console_.castShadow = true;
105
+ g.add(console_);
106
+
107
+ // Console screen (emissive blue)
108
+ var consoleScreen = new THREE.Mesh(
109
+ new THREE.BoxGeometry(0.22, 0.10, 0.008),
110
+ mat(0x001128, { emissive: 0x0066cc, emissiveIntensity: 0.90, roughness: 0.1 })
111
+ );
112
+ consoleScreen.position.set(0, 1.27, -0.432);
113
+ g.add(consoleScreen);
114
+
115
+ // Feet (rubber pads)
116
+ var feetMat = mat(0x0a0a0a, { roughness: 0.92 });
117
+ var feetGeo = new THREE.BoxGeometry(0.10, 0.025, 0.10);
118
+ [[-0.28, 0.012, 0.68], [0.28, 0.012, 0.68], [-0.28, 0.012, -0.68], [0.28, 0.012, -0.68]].forEach(function(p) {
119
+ var foot = new THREE.Mesh(feetGeo, feetMat);
120
+ foot.position.set(p[0], p[1], p[2]);
121
+ g.add(foot);
122
+ });
123
+
124
+ return g;
125
+ }
126
+ };
@@ -0,0 +1,89 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'trophy',
6
+ name: 'Trophy',
7
+ category: 'decor',
8
+ icon: 'Tr',
9
+ gridW: 1, gridD: 1, height: 0.35,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Walnut base slab
14
+ var base = new THREE.Mesh(
15
+ new THREE.BoxGeometry(0.18, 0.055, 0.12),
16
+ PAL.walnutDark()
17
+ );
18
+ base.position.y = 0.028;
19
+ base.castShadow = true;
20
+ base.receiveShadow = true;
21
+ g.add(base);
22
+
23
+ // Base nameplate strip
24
+ var plate = new THREE.Mesh(
25
+ new THREE.BoxGeometry(0.12, 0.018, 0.005),
26
+ PAL.gold()
27
+ );
28
+ plate.position.set(0, 0.032, 0.062);
29
+ g.add(plate);
30
+
31
+ // Trophy stem lower (narrow rod)
32
+ var stemLow = new THREE.Mesh(
33
+ new THREE.CylinderGeometry(0.015, 0.022, 0.07, 12),
34
+ PAL.gold()
35
+ );
36
+ stemLow.position.y = 0.09;
37
+ stemLow.castShadow = true;
38
+ g.add(stemLow);
39
+
40
+ // Trophy stem mid (wider knob)
41
+ var knob = new THREE.Mesh(
42
+ new THREE.SphereGeometry(0.028, 14, 14),
43
+ PAL.gold()
44
+ );
45
+ knob.position.y = 0.145;
46
+ knob.castShadow = true;
47
+ g.add(knob);
48
+
49
+ // Trophy stem upper (narrow rod)
50
+ var stemUp = new THREE.Mesh(
51
+ new THREE.CylinderGeometry(0.012, 0.015, 0.055, 12),
52
+ PAL.gold()
53
+ );
54
+ stemUp.position.y = 0.195;
55
+ stemUp.castShadow = true;
56
+ g.add(stemUp);
57
+
58
+ // Cup body (wide at top, narrow at base)
59
+ var cup = new THREE.Mesh(
60
+ new THREE.CylinderGeometry(0.065, 0.025, 0.10, 18),
61
+ PAL.gold()
62
+ );
63
+ cup.position.y = 0.27;
64
+ cup.castShadow = true;
65
+ g.add(cup);
66
+
67
+ // Left handle
68
+ var handleL = new THREE.Mesh(
69
+ new THREE.TorusGeometry(0.025, 0.007, 8, 14, Math.PI),
70
+ PAL.gold()
71
+ );
72
+ handleL.position.set(-0.078, 0.275, 0);
73
+ handleL.rotation.y = Math.PI / 2;
74
+ handleL.castShadow = true;
75
+ g.add(handleL);
76
+
77
+ // Right handle
78
+ var handleR = new THREE.Mesh(
79
+ new THREE.TorusGeometry(0.025, 0.007, 8, 14, Math.PI),
80
+ PAL.gold()
81
+ );
82
+ handleR.position.set(0.078, 0.275, 0);
83
+ handleR.rotation.y = -Math.PI / 2;
84
+ handleR.castShadow = true;
85
+ g.add(handleR);
86
+
87
+ return g;
88
+ }
89
+ };
@@ -0,0 +1,79 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'tv_screen',
6
+ name: 'TV Screen',
7
+ category: 'tech',
8
+ icon: 'TV',
9
+ gridW: 2, gridD: 1, height: 1.2,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ var W = 2.0;
14
+ var H = 1.2;
15
+ var bezelThick = 0.028;
16
+
17
+ // Outer bezel — ultra-thin glossy black
18
+ var bezel = new THREE.Mesh(
19
+ new THREE.BoxGeometry(W, H, bezelThick),
20
+ mat(0x080808, { roughness: 0.15, metalness: 0.25 })
21
+ );
22
+ bezel.position.y = H / 2 + 0.05;
23
+ bezel.castShadow = true;
24
+ g.add(bezel);
25
+
26
+ // Screen panel — large emissive dark blue
27
+ var screen = new THREE.Mesh(
28
+ new THREE.BoxGeometry(W - 0.04, H - 0.04, 0.008),
29
+ mat(0x040c1a, { emissive: 0x051530, emissiveIntensity: 0.65, roughness: 0.02 })
30
+ );
31
+ screen.position.set(0, H / 2 + 0.05, bezelThick / 2 + 0.001);
32
+ g.add(screen);
33
+
34
+ // Subtle scan-line overlay strip (lower third highlight)
35
+ var scanStrip = new THREE.Mesh(
36
+ new THREE.BoxGeometry(W - 0.06, H * 0.28, 0.002),
37
+ mat(0x0a1f3a, { transparent: true, opacity: 0.18, roughness: 0.0 })
38
+ );
39
+ scanStrip.position.set(0, 0.22, bezelThick / 2 + 0.009);
40
+ g.add(scanStrip);
41
+
42
+ // Logo dot center-bottom bezel
43
+ var logo = new THREE.Mesh(
44
+ new THREE.SphereGeometry(0.009, 10, 10),
45
+ mat(0xffffff, { emissive: 0xffffff, emissiveIntensity: 0.5, roughness: 0.2 })
46
+ );
47
+ logo.position.set(0, 0.022, bezelThick / 2 + 0.012);
48
+ g.add(logo);
49
+
50
+ // Wall-mount bracket plate (rear center)
51
+ var mountPlate = new THREE.Mesh(
52
+ new THREE.BoxGeometry(0.18, 0.22, 0.015),
53
+ PAL.chromeBrushed()
54
+ );
55
+ mountPlate.position.set(0, H / 2 + 0.05, -bezelThick / 2 - 0.006);
56
+ g.add(mountPlate);
57
+
58
+ // Mount screw knobs (4 corners of bracket)
59
+ [[-0.06, 0.07], [0.06, 0.07], [-0.06, -0.07], [0.06, -0.07]].forEach(function(pos) {
60
+ var screw = new THREE.Mesh(
61
+ new THREE.CylinderGeometry(0.008, 0.008, 0.014, 8),
62
+ PAL.chrome()
63
+ );
64
+ screw.rotation.x = Math.PI / 2;
65
+ screw.position.set(pos[0], H / 2 + 0.05 + pos[1], -bezelThick / 2 - 0.015);
66
+ g.add(screw);
67
+ });
68
+
69
+ // Thin chrome edge trim (bottom edge)
70
+ var trim = new THREE.Mesh(
71
+ new THREE.BoxGeometry(W + 0.01, 0.010, bezelThick + 0.005),
72
+ PAL.chrome()
73
+ );
74
+ trim.position.y = 0.042;
75
+ g.add(trim);
76
+
77
+ return g;
78
+ }
79
+ };
@@ -0,0 +1,84 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'vase',
6
+ name: 'Vase',
7
+ category: 'decor',
8
+ icon: 'Vs',
9
+ gridW: 1, gridD: 1, height: 1.0,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Small side table — dark walnut legs
14
+ var tableLeg = mat(0x2a1808, { roughness: 0.60 });
15
+
16
+ var legFL = new THREE.Mesh(new THREE.BoxGeometry(0.035, 0.58, 0.035), tableLeg);
17
+ legFL.position.set(-0.13, 0.29, -0.13);
18
+ legFL.castShadow = true;
19
+ g.add(legFL);
20
+
21
+ var legFR = new THREE.Mesh(new THREE.BoxGeometry(0.035, 0.58, 0.035), tableLeg);
22
+ legFR.position.set(0.13, 0.29, -0.13);
23
+ legFR.castShadow = true;
24
+ g.add(legFR);
25
+
26
+ var legBL = new THREE.Mesh(new THREE.BoxGeometry(0.035, 0.58, 0.035), tableLeg);
27
+ legBL.position.set(-0.13, 0.29, 0.13);
28
+ legBL.castShadow = true;
29
+ g.add(legBL);
30
+
31
+ var legBR = new THREE.Mesh(new THREE.BoxGeometry(0.035, 0.58, 0.035), tableLeg);
32
+ legBR.position.set(0.13, 0.29, 0.13);
33
+ legBR.castShadow = true;
34
+ g.add(legBR);
35
+
36
+ // Table top
37
+ var tabletop = new THREE.Mesh(
38
+ new THREE.BoxGeometry(0.32, 0.025, 0.32),
39
+ PAL.walnutDark()
40
+ );
41
+ tabletop.position.y = 0.6;
42
+ tabletop.castShadow = true;
43
+ tabletop.receiveShadow = true;
44
+ g.add(tabletop);
45
+
46
+ // Vase base (flared bottom)
47
+ var vaseBase = new THREE.Mesh(
48
+ new THREE.CylinderGeometry(0.10, 0.07, 0.08, 20),
49
+ mat(0x1a1a24, { roughness: 0.18, metalness: 0.08 })
50
+ );
51
+ vaseBase.position.y = 0.665;
52
+ vaseBase.castShadow = true;
53
+ g.add(vaseBase);
54
+
55
+ // Vase body (tapered cylinder — dark glaze)
56
+ var vaseBody = new THREE.Mesh(
57
+ new THREE.CylinderGeometry(0.085, 0.10, 0.28, 20),
58
+ mat(0x12121a, { roughness: 0.14, metalness: 0.10 })
59
+ );
60
+ vaseBody.position.y = 0.85;
61
+ vaseBody.castShadow = true;
62
+ g.add(vaseBody);
63
+
64
+ // Vase neck (narrow)
65
+ var vaseNeck = new THREE.Mesh(
66
+ new THREE.CylinderGeometry(0.042, 0.085, 0.10, 20),
67
+ mat(0x12121a, { roughness: 0.14, metalness: 0.10 })
68
+ );
69
+ vaseNeck.position.y = 1.04;
70
+ vaseNeck.castShadow = true;
71
+ g.add(vaseNeck);
72
+
73
+ // Vase lip (slight flare at top)
74
+ var vaseLip = new THREE.Mesh(
75
+ new THREE.CylinderGeometry(0.052, 0.042, 0.03, 20),
76
+ mat(0x1e1e2a, { roughness: 0.12, metalness: 0.12 })
77
+ );
78
+ vaseLip.position.y = 1.105;
79
+ vaseLip.castShadow = true;
80
+ g.add(vaseLip);
81
+
82
+ return g;
83
+ }
84
+ };
@@ -0,0 +1,84 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'wall_clock',
6
+ name: 'Wall Clock',
7
+ category: 'decor',
8
+ icon: 'Wc',
9
+ gridW: 1, gridD: 1, height: 2.5,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Clock face (dark dial)
14
+ var face = new THREE.Mesh(
15
+ new THREE.CylinderGeometry(0.195, 0.195, 0.018, 40),
16
+ mat(0x18191f, { roughness: 0.60 })
17
+ );
18
+ face.position.set(0, 2.5, 0);
19
+ face.rotation.x = Math.PI / 2;
20
+ face.receiveShadow = true;
21
+ g.add(face);
22
+
23
+ // Chrome rim ring
24
+ var rim = new THREE.Mesh(
25
+ new THREE.TorusGeometry(0.2, 0.018, 12, 48),
26
+ PAL.chrome()
27
+ );
28
+ rim.position.set(0, 2.5, 0.008);
29
+ rim.castShadow = true;
30
+ g.add(rim);
31
+
32
+ // Hour markers (12 small gold dots)
33
+ var markerMat = PAL.gold();
34
+ for (var i = 0; i < 12; i++) {
35
+ var angle = (i / 12) * Math.PI * 2;
36
+ var mx = Math.sin(angle) * 0.155;
37
+ var my = Math.cos(angle) * 0.155;
38
+ var marker = new THREE.Mesh(
39
+ new THREE.CylinderGeometry(0.008, 0.008, 0.012, 8),
40
+ markerMat
41
+ );
42
+ marker.position.set(mx, 2.5 + my, 0.016);
43
+ marker.rotation.x = Math.PI / 2;
44
+ g.add(marker);
45
+ }
46
+
47
+ // Hour hand (short, gold)
48
+ var hourHand = new THREE.Mesh(
49
+ new THREE.BoxGeometry(0.016, 0.09, 0.010),
50
+ PAL.gold()
51
+ );
52
+ hourHand.position.set(0.028, 2.535, 0.022);
53
+ hourHand.rotation.z = -0.9;
54
+ g.add(hourHand);
55
+
56
+ // Minute hand (long, chrome)
57
+ var minHand = new THREE.Mesh(
58
+ new THREE.BoxGeometry(0.011, 0.135, 0.010),
59
+ PAL.chrome()
60
+ );
61
+ minHand.position.set(-0.04, 2.562, 0.024);
62
+ minHand.rotation.z = 0.7;
63
+ g.add(minHand);
64
+
65
+ // Center cap
66
+ var cap = new THREE.Mesh(
67
+ new THREE.CylinderGeometry(0.012, 0.012, 0.014, 12),
68
+ PAL.gold()
69
+ );
70
+ cap.position.set(0, 2.5, 0.022);
71
+ cap.rotation.x = Math.PI / 2;
72
+ g.add(cap);
73
+
74
+ // Wall bracket (small block behind)
75
+ var bracket = new THREE.Mesh(
76
+ new THREE.BoxGeometry(0.06, 0.10, 0.04),
77
+ mat(0x111111, { roughness: 0.6, metalness: 0.4 })
78
+ );
79
+ bracket.position.set(0, 2.5, -0.03);
80
+ g.add(bracket);
81
+
82
+ return g;
83
+ }
84
+ };
@@ -0,0 +1,53 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'wall',
6
+ name: 'Wall',
7
+ category: 'structural',
8
+ icon: 'Wa',
9
+ gridW: 2, gridD: 1, height: 3,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Main body
14
+ var body = new THREE.Mesh(
15
+ new THREE.BoxGeometry(2, 3, 0.3),
16
+ PAL.concrete()
17
+ );
18
+ body.castShadow = true;
19
+ body.receiveShadow = true;
20
+ g.add(body);
21
+
22
+ // Top trim strip
23
+ var trim = new THREE.Mesh(
24
+ new THREE.BoxGeometry(2, 0.06, 0.32),
25
+ mat(0x1e2128, { roughness: 0.7 })
26
+ );
27
+ trim.position.y = 1.47;
28
+ trim.castShadow = true;
29
+ g.add(trim);
30
+
31
+ // Bottom base strip
32
+ var base = new THREE.Mesh(
33
+ new THREE.BoxGeometry(2, 0.1, 0.34),
34
+ mat(0x1e2128, { roughness: 0.7 })
35
+ );
36
+ base.position.y = -1.45;
37
+ g.add(base);
38
+
39
+ // Subtle vertical score lines (decoration)
40
+ var score1 = new THREE.Mesh(
41
+ new THREE.BoxGeometry(0.02, 2.8, 0.31),
42
+ mat(0x22252e, { roughness: 0.9 })
43
+ );
44
+ score1.position.x = -0.5;
45
+ g.add(score1);
46
+
47
+ var score2 = score1.clone();
48
+ score2.position.x = 0.5;
49
+ g.add(score2);
50
+
51
+ return g;
52
+ }
53
+ };