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,54 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'floor-tile',
6
+ name: 'Floor Tile',
7
+ category: 'structural',
8
+ icon: 'FT',
9
+ gridW: 2, gridD: 2, height: 0.02,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Main tile slab
14
+ var tile = new THREE.Mesh(
15
+ new THREE.BoxGeometry(2, 0.02, 2),
16
+ PAL.marbleBlack()
17
+ );
18
+ tile.receiveShadow = true;
19
+ g.add(tile);
20
+
21
+ // Thin grout lines — cross pattern
22
+ var groutMat = mat(0x0e1015, { roughness: 1.0 });
23
+
24
+ var groutH = new THREE.Mesh(
25
+ new THREE.BoxGeometry(2, 0.021, 0.025),
26
+ groutMat
27
+ );
28
+ g.add(groutH);
29
+
30
+ var groutV = new THREE.Mesh(
31
+ new THREE.BoxGeometry(0.025, 0.021, 2),
32
+ groutMat
33
+ );
34
+ g.add(groutV);
35
+
36
+ // Edge bevel strip (front)
37
+ var edgeF = new THREE.Mesh(
38
+ new THREE.BoxGeometry(2, 0.018, 0.015),
39
+ mat(0x0e1015, { roughness: 0.8 })
40
+ );
41
+ edgeF.position.z = 0.993;
42
+ g.add(edgeF);
43
+
44
+ // Edge bevel strip (right)
45
+ var edgeR = new THREE.Mesh(
46
+ new THREE.BoxGeometry(0.015, 0.018, 2),
47
+ mat(0x0e1015, { roughness: 0.8 })
48
+ );
49
+ edgeR.position.x = 0.993;
50
+ g.add(edgeR);
51
+
52
+ return g;
53
+ }
54
+ };
@@ -0,0 +1,76 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'flower_pot',
6
+ name: 'Flower Pot',
7
+ category: 'nature',
8
+ icon: 'Fp',
9
+ gridW: 1, gridD: 1, height: 0.35,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Terracotta-style pot body (dark glaze version for premium look)
14
+ var pot = new THREE.Mesh(
15
+ new THREE.CylinderGeometry(0.085, 0.060, 0.16, 16),
16
+ mat(0x2a1a14, { roughness: 0.80 })
17
+ );
18
+ pot.position.y = 0.08;
19
+ pot.castShadow = true;
20
+ pot.receiveShadow = true;
21
+ g.add(pot);
22
+
23
+ // Pot rim
24
+ var rim = new THREE.Mesh(
25
+ new THREE.TorusGeometry(0.088, 0.012, 8, 20),
26
+ mat(0x1a0f0a, { roughness: 0.75 })
27
+ );
28
+ rim.rotation.x = Math.PI / 2;
29
+ rim.position.y = 0.158;
30
+ g.add(rim);
31
+
32
+ // Soil disc
33
+ var soil = new THREE.Mesh(
34
+ new THREE.CylinderGeometry(0.076, 0.076, 0.012, 16),
35
+ mat(0x1a1510, { roughness: 0.99 })
36
+ );
37
+ soil.position.y = 0.168;
38
+ g.add(soil);
39
+
40
+ // Flower stems (3 stems)
41
+ var stemMat = mat(0x1a4020, { roughness: 0.85 });
42
+ var flowerColors = [0xd44080, 0xe8c030, 0x5888e0];
43
+
44
+ for (var i = 0; i < 3; i++) {
45
+ var spread = (i - 1) * 0.045;
46
+ var stemHeight = 0.10 + i * 0.015;
47
+
48
+ var stem = new THREE.Mesh(
49
+ new THREE.CylinderGeometry(0.005, 0.007, stemHeight, 6),
50
+ stemMat
51
+ );
52
+ stem.position.set(spread, 0.175 + stemHeight / 2, (i % 2) * 0.03 - 0.015);
53
+ stem.castShadow = true;
54
+ g.add(stem);
55
+
56
+ // Flower head (small sphere)
57
+ var flower = new THREE.Mesh(
58
+ new THREE.SphereGeometry(0.028, 10, 8),
59
+ mat(flowerColors[i], { roughness: 0.75 })
60
+ );
61
+ flower.position.set(spread, 0.175 + stemHeight + 0.028, (i % 2) * 0.03 - 0.015);
62
+ flower.castShadow = true;
63
+ g.add(flower);
64
+
65
+ // Flower center (yellow dot)
66
+ var center = new THREE.Mesh(
67
+ new THREE.SphereGeometry(0.011, 8, 8),
68
+ mat(0xf0d040, { roughness: 0.70 })
69
+ );
70
+ center.position.set(spread, 0.175 + stemHeight + 0.048, (i % 2) * 0.03 - 0.015);
71
+ g.add(center);
72
+ }
73
+
74
+ return g;
75
+ }
76
+ };
@@ -0,0 +1,95 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'foosball',
6
+ name: 'Foosball Table',
7
+ category: 'recreation',
8
+ icon: 'FB',
9
+ gridW: 2, gridD: 1, height: 0.85,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Main body box
14
+ var body = new THREE.Mesh(
15
+ new THREE.BoxGeometry(1.40, 0.22, 0.75),
16
+ mat(0x111318, { roughness: 0.50, metalness: 0.10 })
17
+ );
18
+ body.position.y = 0.74;
19
+ body.castShadow = true;
20
+ body.receiveShadow = true;
21
+ g.add(body);
22
+
23
+ // Playing field (green felt base)
24
+ var field = new THREE.Mesh(
25
+ new THREE.BoxGeometry(1.24, 0.010, 0.60),
26
+ PAL.greenFelt()
27
+ );
28
+ field.position.y = 0.856;
29
+ field.receiveShadow = true;
30
+ g.add(field);
31
+
32
+ // White center circle
33
+ var circle = new THREE.Mesh(
34
+ new THREE.CylinderGeometry(0.055, 0.055, 0.012, 18),
35
+ mat(0xffffff, { roughness: 0.80 })
36
+ );
37
+ circle.position.y = 0.864;
38
+ g.add(circle);
39
+
40
+ // Goals at each end (small cut-out feel — dark recess boxes)
41
+ var goalMat = mat(0x0a0a0a, { roughness: 0.70 });
42
+ [-0.62, 0.62].forEach(function(x) {
43
+ var goal = new THREE.Mesh(
44
+ new THREE.BoxGeometry(0.02, 0.10, 0.22),
45
+ goalMat
46
+ );
47
+ goal.position.set(x, 0.83, 0);
48
+ g.add(goal);
49
+ });
50
+
51
+ // Top rails (sides of the table)
52
+ var topRailMat = mat(0x1c1f26, { roughness: 0.45 });
53
+ var topRailGeo = new THREE.BoxGeometry(1.40, 0.06, 0.04);
54
+ [0.375, -0.375].forEach(function(z) {
55
+ var rail = new THREE.Mesh(topRailGeo, topRailMat);
56
+ rail.position.set(0, 0.875, z);
57
+ g.add(rail);
58
+ });
59
+
60
+ // 3 chrome rods through the table (horizontal, side to side)
61
+ var rodMat = PAL.chrome();
62
+ var rodGeo = new THREE.CylinderGeometry(0.018, 0.018, 0.92, 12);
63
+ [-0.45, 0, 0.45].forEach(function(x) {
64
+ var rod = new THREE.Mesh(rodGeo, rodMat);
65
+ rod.rotation.z = Math.PI / 2;
66
+ rod.position.set(x, 0.855, 0);
67
+ rod.castShadow = true;
68
+ g.add(rod);
69
+ });
70
+
71
+ // Player figures on rods (small dark cylinders)
72
+ var playerMat = mat(0x222222, { roughness: 0.60 });
73
+ var playerGeo = new THREE.CylinderGeometry(0.028, 0.028, 0.09, 8);
74
+ [-0.45, 0, 0.45].forEach(function(x) {
75
+ [-0.18, 0, 0.18].forEach(function(z) {
76
+ var player = new THREE.Mesh(playerGeo, playerMat);
77
+ player.rotation.z = Math.PI / 2;
78
+ player.position.set(x, 0.855, z);
79
+ g.add(player);
80
+ });
81
+ });
82
+
83
+ // 4 legs
84
+ var legGeo = new THREE.BoxGeometry(0.08, 0.68, 0.08);
85
+ var legMat = mat(0x16181d, { roughness: 0.55 });
86
+ [[-0.62, 0.34, 0.31], [0.62, 0.34, 0.31], [-0.62, 0.34, -0.31], [0.62, 0.34, -0.31]].forEach(function(p) {
87
+ var leg = new THREE.Mesh(legGeo, legMat);
88
+ leg.position.set(p[0], p[1], p[2]);
89
+ leg.castShadow = true;
90
+ g.add(leg);
91
+ });
92
+
93
+ return g;
94
+ }
95
+ };
@@ -0,0 +1,99 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'fridge',
6
+ name: 'Fridge',
7
+ category: 'kitchen',
8
+ icon: 'Fr',
9
+ gridW: 1, gridD: 1, height: 1.8,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Main body — dark brushed metallic
14
+ var body = new THREE.Mesh(
15
+ new THREE.BoxGeometry(0.70, 1.80, 0.60),
16
+ mat(0x16181d, { roughness: 0.30, metalness: 0.45 })
17
+ );
18
+ body.position.y = 0.90;
19
+ body.castShadow = true;
20
+ body.receiveShadow = true;
21
+ g.add(body);
22
+
23
+ // Door seam line (horizontal divide — top fridge / bottom freezer)
24
+ var seam = new THREE.Mesh(
25
+ new THREE.BoxGeometry(0.68, 0.008, 0.012),
26
+ mat(0x333333, { roughness: 0.60 })
27
+ );
28
+ seam.position.set(0, 1.10, 0.305);
29
+ g.add(seam);
30
+
31
+ // Top section door face (slightly lighter)
32
+ var topDoor = new THREE.Mesh(
33
+ new THREE.BoxGeometry(0.68, 0.68, 0.01),
34
+ mat(0x1c1f26, { roughness: 0.28, metalness: 0.40 })
35
+ );
36
+ topDoor.position.set(0, 1.45, 0.305);
37
+ g.add(topDoor);
38
+
39
+ // Bottom freezer door face
40
+ var botDoor = new THREE.Mesh(
41
+ new THREE.BoxGeometry(0.68, 0.40, 0.01),
42
+ mat(0x1c1f26, { roughness: 0.28, metalness: 0.40 })
43
+ );
44
+ botDoor.position.set(0, 0.62, 0.305);
45
+ g.add(botDoor);
46
+
47
+ // Chrome handle — top door
48
+ var handleMat = PAL.chrome();
49
+ var handleGeo = new THREE.CylinderGeometry(0.014, 0.014, 0.42, 10);
50
+ var handleTop = new THREE.Mesh(handleGeo, handleMat);
51
+ handleTop.position.set(0.28, 1.50, 0.322);
52
+ handleTop.castShadow = true;
53
+ g.add(handleTop);
54
+
55
+ // Handle mounts (top)
56
+ var mountGeo = new THREE.BoxGeometry(0.022, 0.022, 0.025);
57
+ [1.29, 1.71].forEach(function(y) {
58
+ var mount = new THREE.Mesh(mountGeo, PAL.chromeBrushed());
59
+ mount.position.set(0.28, y, 0.32);
60
+ g.add(mount);
61
+ });
62
+
63
+ // Chrome handle — bottom freezer door
64
+ var handleBot = new THREE.Mesh(
65
+ new THREE.CylinderGeometry(0.014, 0.014, 0.28, 10),
66
+ handleMat
67
+ );
68
+ handleBot.position.set(0.28, 0.62, 0.322);
69
+ handleBot.castShadow = true;
70
+ g.add(handleBot);
71
+
72
+ // Small LED status dot (green)
73
+ var led = new THREE.Mesh(
74
+ new THREE.SphereGeometry(0.012, 8, 8),
75
+ mat(0x22c55e, { emissive: 0x22c55e, emissiveIntensity: 1.0 })
76
+ );
77
+ led.position.set(-0.24, 1.74, 0.308);
78
+ g.add(led);
79
+
80
+ // Thin ventilation grill at top back
81
+ var vent = new THREE.Mesh(
82
+ new THREE.BoxGeometry(0.60, 0.06, 0.50),
83
+ mat(0x0e1014, { roughness: 0.70 })
84
+ );
85
+ vent.position.set(0, 1.78, -0.02);
86
+ g.add(vent);
87
+
88
+ // Small black feet
89
+ var feetMat = mat(0x0a0a0a, { roughness: 0.90 });
90
+ var feetGeo = new THREE.BoxGeometry(0.06, 0.03, 0.06);
91
+ [[-0.28, 0.015, 0.24], [0.28, 0.015, 0.24], [-0.28, 0.015, -0.24], [0.28, 0.015, -0.24]].forEach(function(p) {
92
+ var foot = new THREE.Mesh(feetGeo, feetMat);
93
+ foot.position.set(p[0], p[1], p[2]);
94
+ g.add(foot);
95
+ });
96
+
97
+ return g;
98
+ }
99
+ };
@@ -0,0 +1,154 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'gaming-chair',
6
+ name: 'Gaming Chair',
7
+ category: 'furniture',
8
+ icon: 'GC',
9
+ gridW: 1, gridD: 1, height: 1.35,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // 5-star base
14
+ var baseDisc = new THREE.Mesh(
15
+ new THREE.CylinderGeometry(0.30, 0.30, 0.025, 5),
16
+ PAL.chrome()
17
+ );
18
+ baseDisc.position.y = 0.02;
19
+ baseDisc.castShadow = true;
20
+ g.add(baseDisc);
21
+
22
+ for (var i = 0; i < 5; i++) {
23
+ var arm = new THREE.Mesh(
24
+ new THREE.BoxGeometry(0.28, 0.025, 0.05),
25
+ PAL.chrome()
26
+ );
27
+ arm.position.y = 0.02;
28
+ arm.rotation.y = (i / 5) * Math.PI * 2;
29
+ arm.castShadow = true;
30
+ g.add(arm);
31
+
32
+ var wheel = new THREE.Mesh(
33
+ new THREE.CylinderGeometry(0.035, 0.035, 0.045, 10),
34
+ mat(0x1a1a1a, { roughness: 0.9 })
35
+ );
36
+ var angle = (i / 5) * Math.PI * 2;
37
+ wheel.position.set(Math.sin(angle) * 0.28, 0.018, Math.cos(angle) * 0.28);
38
+ wheel.rotation.z = Math.PI / 2;
39
+ g.add(wheel);
40
+ }
41
+
42
+ // Pneumatic post
43
+ var post = new THREE.Mesh(
44
+ new THREE.CylinderGeometry(0.045, 0.055, 0.44, 12),
45
+ PAL.chromeBrushed()
46
+ );
47
+ post.position.y = 0.255;
48
+ post.castShadow = true;
49
+ g.add(post);
50
+
51
+ // Seat shell (bucket-style, wider than office chair)
52
+ var seatShell = new THREE.Mesh(
53
+ new THREE.BoxGeometry(0.56, 0.06, 0.54),
54
+ mat(0x111318, { roughness: 0.5, metalness: 0.15 })
55
+ );
56
+ seatShell.position.y = 0.5;
57
+ seatShell.castShadow = true;
58
+ g.add(seatShell);
59
+
60
+ // Seat cushion — black main with red stripe
61
+ var seatCushion = new THREE.Mesh(
62
+ new THREE.BoxGeometry(0.50, 0.08, 0.50),
63
+ PAL.leatherBlack()
64
+ );
65
+ seatCushion.position.y = 0.545;
66
+ seatCushion.castShadow = true;
67
+ seatCushion.receiveShadow = true;
68
+ g.add(seatCushion);
69
+
70
+ // Center stripe on seat
71
+ var seatStripe = new THREE.Mesh(
72
+ new THREE.BoxGeometry(0.12, 0.09, 0.50),
73
+ mat(0xcc1111, { roughness: 0.65 })
74
+ );
75
+ seatStripe.position.y = 0.545;
76
+ g.add(seatStripe);
77
+
78
+ // Tall racing back (wider, taller than office chair)
79
+ var backBody = new THREE.Mesh(
80
+ new THREE.BoxGeometry(0.54, 0.76, 0.09),
81
+ mat(0x111318, { roughness: 0.5, metalness: 0.12 })
82
+ );
83
+ backBody.position.set(0, 0.95, -0.24);
84
+ backBody.castShadow = true;
85
+ g.add(backBody);
86
+
87
+ // Back cushion
88
+ var backCushion = new THREE.Mesh(
89
+ new THREE.BoxGeometry(0.46, 0.7, 0.07),
90
+ PAL.leatherBlack()
91
+ );
92
+ backCushion.position.set(0, 0.95, -0.20);
93
+ backCushion.castShadow = true;
94
+ g.add(backCushion);
95
+
96
+ // Back center accent stripe
97
+ var backStripe = new THREE.Mesh(
98
+ new THREE.BoxGeometry(0.12, 0.7, 0.08),
99
+ mat(0xcc1111, { roughness: 0.65 })
100
+ );
101
+ backStripe.position.set(0, 0.95, -0.19);
102
+ g.add(backStripe);
103
+
104
+ // Headrest (attached top)
105
+ var headrest = new THREE.Mesh(
106
+ new THREE.BoxGeometry(0.30, 0.20, 0.09),
107
+ PAL.leatherBlack()
108
+ );
109
+ headrest.position.set(0, 1.38, -0.24);
110
+ headrest.castShadow = true;
111
+ g.add(headrest);
112
+
113
+ // Headrest pad
114
+ var headPad = new THREE.Mesh(
115
+ new THREE.BoxGeometry(0.22, 0.15, 0.06),
116
+ mat(0xcc1111, { roughness: 0.65 })
117
+ );
118
+ headPad.position.set(0, 1.38, -0.20);
119
+ g.add(headPad);
120
+
121
+ // Lumbar support pillow
122
+ var lumbar = new THREE.Mesh(
123
+ new THREE.BoxGeometry(0.30, 0.16, 0.07),
124
+ mat(0xcc1111, { roughness: 0.65 })
125
+ );
126
+ lumbar.position.set(0, 0.66, -0.17);
127
+ lumbar.castShadow = true;
128
+ g.add(lumbar);
129
+
130
+ // Armrests (height-adjustable style)
131
+ var armMat = mat(0x1a1a1a, { roughness: 0.7 });
132
+ var leftArmRest = new THREE.Mesh(
133
+ new THREE.BoxGeometry(0.08, 0.04, 0.24),
134
+ armMat
135
+ );
136
+ leftArmRest.position.set(-0.30, 0.74, -0.06);
137
+ leftArmRest.castShadow = true;
138
+ g.add(leftArmRest);
139
+
140
+ var rightArmRest = leftArmRest.clone();
141
+ rightArmRest.position.x = 0.30;
142
+ g.add(rightArmRest);
143
+
144
+ // Armrest posts
145
+ var lPost = new THREE.Mesh(new THREE.BoxGeometry(0.03, 0.22, 0.03), PAL.chromeBrushed());
146
+ lPost.position.set(-0.30, 0.63, -0.06);
147
+ g.add(lPost);
148
+ var rPost = lPost.clone();
149
+ rPost.position.x = 0.30;
150
+ g.add(rPost);
151
+
152
+ return g;
153
+ }
154
+ };
@@ -0,0 +1,105 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'gaming-desk',
6
+ name: 'Gaming Desk',
7
+ category: 'furniture',
8
+ icon: 'GD',
9
+ gridW: 3, gridD: 1, height: 0.76,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Main top surface (wide section)
14
+ var mainTop = new THREE.Mesh(
15
+ new THREE.BoxGeometry(2.2, 0.04, 1.0),
16
+ mat(0x111318, { roughness: 0.28, metalness: 0.08 })
17
+ );
18
+ mainTop.position.y = 0.76;
19
+ mainTop.castShadow = true;
20
+ mainTop.receiveShadow = true;
21
+ g.add(mainTop);
22
+
23
+ // L-wing (right extension)
24
+ var wing = new THREE.Mesh(
25
+ new THREE.BoxGeometry(1.0, 0.04, 0.7),
26
+ mat(0x111318, { roughness: 0.28, metalness: 0.08 })
27
+ );
28
+ wing.position.set(1.1, 0.76, -0.15);
29
+ wing.castShadow = true;
30
+ wing.receiveShadow = true;
31
+ g.add(wing);
32
+
33
+ // Monitor riser platform
34
+ var riser = new THREE.Mesh(
35
+ new THREE.BoxGeometry(1.4, 0.08, 0.28),
36
+ mat(0x1a1c22, { roughness: 0.45, metalness: 0.12 })
37
+ );
38
+ riser.position.set(-0.2, 0.82, -0.28);
39
+ riser.castShadow = true;
40
+ g.add(riser);
41
+
42
+ // Riser legs (2 small supports)
43
+ var riserLegMat = mat(0x2a2d35, { roughness: 0.5 });
44
+ var riserLeg = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.08, 0.06), riserLegMat);
45
+ riserLeg.position.set(-0.85, 0.78, -0.28);
46
+ g.add(riserLeg);
47
+ var riserLeg2 = riserLeg.clone();
48
+ riserLeg2.position.x = 0.45;
49
+ g.add(riserLeg2);
50
+
51
+ // RGB LED strip along front edge (emissive cyan)
52
+ var led = new THREE.Mesh(
53
+ new THREE.BoxGeometry(2.2, 0.012, 0.015),
54
+ mat(0x06b6d4, { emissive: 0x06b6d4, emissiveIntensity: 0.9, roughness: 0.3 })
55
+ );
56
+ led.position.set(0, 0.742, 0.508);
57
+ g.add(led);
58
+
59
+ // LED strip on wing front edge
60
+ var ledWing = new THREE.Mesh(
61
+ new THREE.BoxGeometry(1.0, 0.012, 0.015),
62
+ mat(0x06b6d4, { emissive: 0x06b6d4, emissiveIntensity: 0.9, roughness: 0.3 })
63
+ );
64
+ ledWing.position.set(1.1, 0.742, 0.157);
65
+ g.add(ledWing);
66
+
67
+ // Cable channel tray (under desk, rear)
68
+ var cableTray = new THREE.Mesh(
69
+ new THREE.BoxGeometry(1.8, 0.04, 0.08),
70
+ mat(0x1a1c22, { roughness: 0.8 })
71
+ );
72
+ cableTray.position.set(0, 0.58, -0.44);
73
+ g.add(cableTray);
74
+
75
+ // 4 legs (angular, steel-look)
76
+ var legMat = mat(0x1e2128, { roughness: 0.3, metalness: 0.55 });
77
+ var legGeo = new THREE.BoxGeometry(0.07, 0.74, 0.07);
78
+ var mainLegs = [[-0.97, 0.37, 0.44], [0.77, 0.37, 0.44], [-0.97, 0.37, -0.44]];
79
+ mainLegs.forEach(function(p) {
80
+ var leg = new THREE.Mesh(legGeo, legMat);
81
+ leg.position.set(p[0], p[1], p[2]);
82
+ leg.castShadow = true;
83
+ g.add(leg);
84
+ });
85
+
86
+ // Wing leg
87
+ var wingLeg = new THREE.Mesh(
88
+ new THREE.BoxGeometry(0.07, 0.74, 0.07),
89
+ legMat
90
+ );
91
+ wingLeg.position.set(1.55, 0.37, -0.28);
92
+ wingLeg.castShadow = true;
93
+ g.add(wingLeg);
94
+
95
+ // Connecting crossbar
96
+ var crossbar = new THREE.Mesh(
97
+ new THREE.BoxGeometry(0.04, 0.04, 0.9),
98
+ legMat
99
+ );
100
+ crossbar.position.set(-0.97, 0.28, 0);
101
+ g.add(crossbar);
102
+
103
+ return g;
104
+ }
105
+ };
@@ -0,0 +1,72 @@
1
+ import * as THREE from 'three';
2
+ import { mat, PAL } from './materials.js';
3
+
4
+ export default {
5
+ id: 'glass-door',
6
+ name: 'Glass Door',
7
+ category: 'structural',
8
+ icon: 'GD',
9
+ gridW: 2, gridD: 1, height: 2.5,
10
+ factory: function() {
11
+ var g = new THREE.Group();
12
+
13
+ // Frameless glass panel
14
+ var glass = new THREE.Mesh(
15
+ new THREE.BoxGeometry(1.2, 2.5, 0.015),
16
+ PAL.glass()
17
+ );
18
+ glass.receiveShadow = true;
19
+ g.add(glass);
20
+
21
+ // Chrome header bar
22
+ var header = new THREE.Mesh(
23
+ new THREE.BoxGeometry(1.3, 0.07, 0.06),
24
+ PAL.chrome()
25
+ );
26
+ header.position.y = 1.285;
27
+ header.castShadow = true;
28
+ g.add(header);
29
+
30
+ // Chrome floor threshold
31
+ var threshold = new THREE.Mesh(
32
+ new THREE.BoxGeometry(1.3, 0.04, 0.06),
33
+ PAL.chromeBrushed()
34
+ );
35
+ threshold.position.y = -1.23;
36
+ g.add(threshold);
37
+
38
+ // Chrome pull handle (horizontal bar, both sides)
39
+ var handleMat = PAL.chrome();
40
+ var pullBar = new THREE.Mesh(
41
+ new THREE.CylinderGeometry(0.02, 0.02, 0.35, 10),
42
+ handleMat
43
+ );
44
+ pullBar.rotation.z = Math.PI / 2;
45
+ pullBar.position.set(0, 0.1, 0.05);
46
+ pullBar.castShadow = true;
47
+ g.add(pullBar);
48
+
49
+ var pullBarBack = pullBar.clone();
50
+ pullBarBack.position.z = -0.05;
51
+ g.add(pullBarBack);
52
+
53
+ // Handle end caps
54
+ var capGeo = new THREE.SphereGeometry(0.022, 8, 6);
55
+ var capL = new THREE.Mesh(capGeo, handleMat);
56
+ capL.position.set(-0.175, 0.1, 0.05);
57
+ g.add(capL);
58
+ var capR = new THREE.Mesh(capGeo, handleMat);
59
+ capR.position.set(0.175, 0.1, 0.05);
60
+ g.add(capR);
61
+
62
+ // Subtle tint line at mid height
63
+ var tintLine = new THREE.Mesh(
64
+ new THREE.BoxGeometry(1.2, 0.02, 0.016),
65
+ mat(0x88aacc, { transparent: true, opacity: 0.4 })
66
+ );
67
+ tintLine.position.y = -0.3;
68
+ g.add(tintLine);
69
+
70
+ return g;
71
+ }
72
+ };