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.
- package/CHANGELOG.md +3 -1
- package/README.md +158 -592
- package/SECURITY.md +3 -3
- package/USAGE.md +151 -0
- package/agent-contracts.js +447 -0
- package/api-agents.js +760 -0
- package/autonomy/decision-v2.js +380 -0
- package/autonomy/watchdog-policy.js +572 -0
- package/cli.js +454 -298
- package/conversation-templates/autonomous-feature.json +83 -22
- package/conversation-templates/code-review.json +69 -21
- package/conversation-templates/debug-squad.json +69 -21
- package/conversation-templates/feature-build.json +69 -21
- package/conversation-templates/research-write.json +69 -21
- package/dashboard.html +3148 -174
- package/dashboard.js +823 -786
- package/data-dir.js +58 -0
- package/docs/architecture/branch-semantics.md +157 -0
- package/docs/architecture/canonical-event-schema.md +88 -0
- package/docs/architecture/markdown-workspace.md +183 -0
- package/docs/architecture/runtime-contract.md +459 -0
- package/docs/architecture/runtime-migration-hardening.md +64 -0
- package/events/hooks.js +154 -0
- package/events/log.js +457 -0
- package/events/replay.js +33 -0
- package/events/schema.js +432 -0
- package/managed-team-integration.js +261 -0
- package/office/agents.js +704 -597
- package/office/animation.js +1 -1
- package/office/assets/arcade-cabinet.js +141 -0
- package/office/assets/archway.js +77 -0
- package/office/assets/bar-counter.js +91 -0
- package/office/assets/bar-stool.js +71 -0
- package/office/assets/beanbag.js +64 -0
- package/office/assets/bench.js +99 -0
- package/office/assets/bollard.js +87 -0
- package/office/assets/cactus.js +100 -0
- package/office/assets/carpet-tile.js +46 -0
- package/office/assets/chair.js +123 -0
- package/office/assets/chandelier.js +107 -0
- package/office/assets/coffee-machine.js +95 -0
- package/office/assets/coffee-table.js +81 -0
- package/office/assets/column.js +95 -0
- package/office/assets/desk-lamp.js +102 -0
- package/office/assets/desk.js +76 -0
- package/office/assets/dining-table.js +105 -0
- package/office/assets/door.js +70 -0
- package/office/assets/dual-monitor.js +72 -0
- package/office/assets/fence.js +76 -0
- package/office/assets/filing-cabinet.js +111 -0
- package/office/assets/floor-lamp.js +69 -0
- package/office/assets/floor-tile.js +54 -0
- package/office/assets/flower-pot.js +76 -0
- package/office/assets/foosball.js +95 -0
- package/office/assets/fridge.js +99 -0
- package/office/assets/gaming-chair.js +154 -0
- package/office/assets/gaming-desk.js +105 -0
- package/office/assets/glass-door.js +72 -0
- package/office/assets/glass-wall.js +64 -0
- package/office/assets/half-wall.js +49 -0
- package/office/assets/hanging-plant.js +112 -0
- package/office/assets/index.js +151 -0
- package/office/assets/indoor-tree.js +90 -0
- package/office/assets/l-sofa.js +153 -0
- package/office/assets/marble-floor.js +64 -0
- package/office/assets/materials.js +40 -0
- package/office/assets/meeting-table.js +88 -0
- package/office/assets/microwave.js +94 -0
- package/office/assets/monitor.js +67 -0
- package/office/assets/neon-strip.js +73 -0
- package/office/assets/painting.js +84 -0
- package/office/assets/palm-tree.js +108 -0
- package/office/assets/pc-tower.js +91 -0
- package/office/assets/pendant-light.js +67 -0
- package/office/assets/ping-pong.js +114 -0
- package/office/assets/plant.js +72 -0
- package/office/assets/planter-box.js +95 -0
- package/office/assets/pool-table.js +94 -0
- package/office/assets/printer.js +113 -0
- package/office/assets/reception-desk.js +133 -0
- package/office/assets/rug.js +78 -0
- package/office/assets/sculpture.js +85 -0
- package/office/assets/server-rack.js +98 -0
- package/office/assets/sink.js +109 -0
- package/office/assets/sofa.js +106 -0
- package/office/assets/speaker.js +83 -0
- package/office/assets/spotlight.js +83 -0
- package/office/assets/street-lamp.js +97 -0
- package/office/assets/trash-can.js +83 -0
- package/office/assets/treadmill.js +126 -0
- package/office/assets/trophy.js +89 -0
- package/office/assets/tv-screen.js +79 -0
- package/office/assets/vase.js +84 -0
- package/office/assets/wall-clock.js +84 -0
- package/office/assets/wall.js +53 -0
- package/office/assets/water-cooler.js +146 -0
- package/office/assets/whiteboard.js +115 -0
- package/office/assets.js +3 -431
- package/office/builder.js +791 -355
- package/office/campus-env.js +1012 -1119
- package/office/environment.js +2 -0
- package/office/gallery.js +997 -0
- package/office/index.js +165 -61
- package/office/navigation.js +173 -152
- package/office/player.js +178 -68
- package/office/robot-character.js +272 -0
- package/office/spectator-camera.js +33 -10
- package/office/state.js +2 -0
- package/office/world-save.js +35 -4
- package/package.json +57 -3
- package/providers/comfyui.js +383 -0
- package/providers/dalle.js +79 -0
- package/providers/gemini.js +181 -0
- package/providers/ollama.js +184 -0
- package/providers/replicate.js +115 -0
- package/providers/zai.js +183 -0
- package/runtime-descriptor.js +270 -0
- package/scripts/check-agent-contract-advisory.js +132 -0
- package/scripts/check-api-agent-parity.js +277 -0
- package/scripts/check-autonomy-v2-decision.js +207 -0
- package/scripts/check-autonomy-v2-execution.js +588 -0
- package/scripts/check-autonomy-v2-watchdog.js +224 -0
- package/scripts/check-branch-fork-snapshot.js +337 -0
- package/scripts/check-branch-isolation.js +787 -0
- package/scripts/check-branch-semantics.js +139 -0
- package/scripts/check-dashboard-control-plane.js +1304 -0
- package/scripts/check-docs-onboarding.js +490 -0
- package/scripts/check-event-schema.js +276 -0
- package/scripts/check-evidence-completion.js +239 -0
- package/scripts/check-invariants.js +992 -0
- package/scripts/check-lifecycle-hooks.js +525 -0
- package/scripts/check-managed-team-integration.js +166 -0
- package/scripts/check-markdown-workspace-export.js +548 -0
- package/scripts/check-markdown-workspace-safety.js +347 -0
- package/scripts/check-markdown-workspace.js +136 -0
- package/scripts/check-message-replay.js +429 -0
- package/scripts/check-migration-hardening.js +300 -0
- package/scripts/check-performance-indexing.js +272 -0
- package/scripts/check-provider-capabilities.js +316 -0
- package/scripts/check-runtime-contract.js +109 -0
- package/scripts/check-session-aware-context.js +172 -0
- package/scripts/check-session-lifecycle.js +210 -0
- package/scripts/export-markdown-workspace.js +84 -0
- package/scripts/fixtures/message-replay/clean.jsonl +2 -0
- package/scripts/fixtures/message-replay/corrupt-correction-payload.jsonl +1 -0
- package/scripts/fixtures/message-replay/corrupt-jsonl.jsonl +1 -0
- package/scripts/fixtures/message-replay/corrupt-payload.jsonl +1 -0
- package/scripts/fixtures/message-replay/out-of-order.jsonl +2 -0
- package/scripts/migrate-legacy-to-canonical.js +201 -0
- package/scripts/run-verification-suite.js +242 -0
- package/scripts/sync-packaged-docs.js +69 -0
- package/server.js +9546 -7214
- package/state/agents.js +161 -0
- package/state/canonical.js +3068 -0
- package/state/dashboard-queries.js +441 -0
- package/state/evidence.js +56 -0
- package/state/io.js +69 -0
- package/state/markdown-workspace.js +951 -0
- package/state/messages.js +669 -0
- package/state/sessions.js +683 -0
- package/state/tasks-workflows.js +92 -0
- package/templates/debate.json +2 -2
- package/templates/managed.json +4 -4
- package/templates/pair.json +2 -2
- package/templates/review.json +2 -2
- package/templates/team.json +3 -3
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Shared material cache for all assets — prevents duplicate materials
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
|
|
4
|
+
var _cache = {};
|
|
5
|
+
|
|
6
|
+
export function mat(color, opts) {
|
|
7
|
+
var key = color + JSON.stringify(opts || {});
|
|
8
|
+
if (!_cache[key]) {
|
|
9
|
+
_cache[key] = new THREE.MeshStandardMaterial(Object.assign({ color: color }, opts || {}));
|
|
10
|
+
}
|
|
11
|
+
return _cache[key];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Pre-defined palette shortcuts
|
|
15
|
+
export var PAL = {
|
|
16
|
+
marbleBlack: function() { return mat(0x1a1c22, { roughness: 0.12, metalness: 0.05 }); },
|
|
17
|
+
marbleWhite: function() { return mat(0xf0ece4, { roughness: 0.15, metalness: 0.05 }); },
|
|
18
|
+
walnutDark: function() { return mat(0x3a2210, { roughness: 0.55 }); },
|
|
19
|
+
walnutLight: function() { return mat(0x8B5E3C, { roughness: 0.50 }); },
|
|
20
|
+
chrome: function() { return mat(0xd0d0d0, { roughness: 0.08, metalness: 0.85 }); },
|
|
21
|
+
chromeBrushed: function() { return mat(0x999999, { roughness: 0.25, metalness: 0.70 }); },
|
|
22
|
+
glass: function() { return mat(0xaaccee, { transparent: true, opacity: 0.25, roughness: 0.05, side: THREE.DoubleSide }); },
|
|
23
|
+
glassFrosted: function() { return mat(0xd0d8e8, { transparent: true, opacity: 0.50, roughness: 0.40, side: THREE.DoubleSide }); },
|
|
24
|
+
concrete: function() { return mat(0x2a2d35, { roughness: 0.85 }); },
|
|
25
|
+
leatherBlack: function() { return mat(0x1a1a1a, { roughness: 0.70 }); },
|
|
26
|
+
leatherCognac: function() { return mat(0x8B4513, { roughness: 0.65 }); },
|
|
27
|
+
gold: function() { return mat(0xd4af37, { roughness: 0.30, metalness: 0.70 }); },
|
|
28
|
+
darkMetal: function() { return mat(0x111111, { roughness: 0.40, metalness: 0.20 }); },
|
|
29
|
+
fabric: function() { return mat(0x2a2d3a, { roughness: 0.95 }); },
|
|
30
|
+
rubber: function() { return mat(0x2a2a2a, { roughness: 0.95 }); },
|
|
31
|
+
greenFelt: function() { return mat(0x006633, { roughness: 0.90 }); },
|
|
32
|
+
leaf: function() { return mat(0x2d8a4e, { roughness: 0.80 }); },
|
|
33
|
+
neonBlue: function() { return mat(0x58a6ff, { emissive: 0x58a6ff, emissiveIntensity: 0.6 }); },
|
|
34
|
+
neonPurple: function() { return mat(0xa855f7, { emissive: 0xa855f7, emissiveIntensity: 0.5 }); },
|
|
35
|
+
neonGreen: function() { return mat(0x22c55e, { emissive: 0x22c55e, emissiveIntensity: 0.5 }); },
|
|
36
|
+
neonRed: function() { return mat(0xef4444, { emissive: 0xef4444, emissiveIntensity: 0.5 }); },
|
|
37
|
+
neonCyan: function() { return mat(0x06b6d4, { emissive: 0x06b6d4, emissiveIntensity: 0.6 }); },
|
|
38
|
+
warmLight: function() { return mat(0xffeedd, { emissive: 0xffeedd, emissiveIntensity: 0.4, transparent: true, opacity: 0.8 }); },
|
|
39
|
+
screen: function() { return mat(0x333333, { emissive: 0x111122, emissiveIntensity: 0.3 }); },
|
|
40
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { mat, PAL } from './materials.js';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
id: 'meeting-table',
|
|
6
|
+
name: 'Meeting Table',
|
|
7
|
+
category: 'office',
|
|
8
|
+
icon: 'MT',
|
|
9
|
+
gridW: 3, gridD: 2, height: 0.78,
|
|
10
|
+
factory: function() {
|
|
11
|
+
var g = new THREE.Group();
|
|
12
|
+
|
|
13
|
+
// Oval tabletop approximated with scaled cylinder
|
|
14
|
+
var topGeo = new THREE.CylinderGeometry(1.0, 1.0, 0.055, 32);
|
|
15
|
+
var top = new THREE.Mesh(topGeo, mat(0x1a1c22, { roughness: 0.25, metalness: 0.08 }));
|
|
16
|
+
top.scale.set(2.5, 1, 1.5);
|
|
17
|
+
top.position.y = 0.78;
|
|
18
|
+
top.castShadow = true;
|
|
19
|
+
top.receiveShadow = true;
|
|
20
|
+
g.add(top);
|
|
21
|
+
|
|
22
|
+
// Top surface inlay (lighter tone center oval)
|
|
23
|
+
var inlayGeo = new THREE.CylinderGeometry(0.88, 0.88, 0.008, 32);
|
|
24
|
+
var inlay = new THREE.Mesh(inlayGeo, mat(0x252830, { roughness: 0.30 }));
|
|
25
|
+
inlay.scale.set(2.3, 1, 1.3);
|
|
26
|
+
inlay.position.y = 0.81;
|
|
27
|
+
g.add(inlay);
|
|
28
|
+
|
|
29
|
+
// Gold edge ring band
|
|
30
|
+
var ringGeo = new THREE.TorusGeometry(1.0, 0.012, 8, 32);
|
|
31
|
+
var ring = new THREE.Mesh(ringGeo, PAL.gold());
|
|
32
|
+
ring.scale.set(2.5, 1.5, 1);
|
|
33
|
+
ring.rotation.x = Math.PI / 2;
|
|
34
|
+
ring.position.y = 0.808;
|
|
35
|
+
g.add(ring);
|
|
36
|
+
|
|
37
|
+
// Central pedestal column
|
|
38
|
+
var column = new THREE.Mesh(
|
|
39
|
+
new THREE.CylinderGeometry(0.08, 0.10, 0.52, 14),
|
|
40
|
+
PAL.chromeBrushed()
|
|
41
|
+
);
|
|
42
|
+
column.position.y = 0.52;
|
|
43
|
+
column.castShadow = true;
|
|
44
|
+
g.add(column);
|
|
45
|
+
|
|
46
|
+
// Upper column flare
|
|
47
|
+
var flareTop = new THREE.Mesh(
|
|
48
|
+
new THREE.CylinderGeometry(0.22, 0.08, 0.08, 14),
|
|
49
|
+
PAL.chrome()
|
|
50
|
+
);
|
|
51
|
+
flareTop.position.y = 0.80;
|
|
52
|
+
flareTop.castShadow = true;
|
|
53
|
+
g.add(flareTop);
|
|
54
|
+
|
|
55
|
+
// Lower column base flare
|
|
56
|
+
var flareBot = new THREE.Mesh(
|
|
57
|
+
new THREE.CylinderGeometry(0.10, 0.22, 0.08, 14),
|
|
58
|
+
PAL.chrome()
|
|
59
|
+
);
|
|
60
|
+
flareBot.position.y = 0.26;
|
|
61
|
+
flareBot.castShadow = true;
|
|
62
|
+
g.add(flareBot);
|
|
63
|
+
|
|
64
|
+
// Cross base (4-arm spider base)
|
|
65
|
+
var baseMat = PAL.chrome();
|
|
66
|
+
var armGeo = new THREE.BoxGeometry(1.4, 0.04, 0.08);
|
|
67
|
+
var armH = new THREE.Mesh(armGeo, baseMat);
|
|
68
|
+
armH.position.y = 0.05;
|
|
69
|
+
armH.castShadow = true;
|
|
70
|
+
g.add(armH);
|
|
71
|
+
|
|
72
|
+
var armV = new THREE.Mesh(new THREE.BoxGeometry(0.08, 0.04, 1.4), baseMat);
|
|
73
|
+
armV.position.y = 0.05;
|
|
74
|
+
armV.castShadow = true;
|
|
75
|
+
g.add(armV);
|
|
76
|
+
|
|
77
|
+
// 4 glide feet at base arm ends
|
|
78
|
+
var glideMat = mat(0x111111, { roughness: 0.9 });
|
|
79
|
+
var glideGeo = new THREE.CylinderGeometry(0.04, 0.04, 0.018, 8);
|
|
80
|
+
[[0.68, 0], [-0.68, 0], [0, 0.68], [0, -0.68]].forEach(function(p) {
|
|
81
|
+
var glide = new THREE.Mesh(glideGeo, glideMat);
|
|
82
|
+
glide.position.set(p[0], 0.009, p[1]);
|
|
83
|
+
g.add(glide);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return g;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { mat, PAL } from './materials.js';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
id: 'microwave',
|
|
6
|
+
name: 'Microwave',
|
|
7
|
+
category: 'kitchen',
|
|
8
|
+
icon: 'MW',
|
|
9
|
+
gridW: 1, gridD: 1, height: 0.3,
|
|
10
|
+
factory: function() {
|
|
11
|
+
var g = new THREE.Group();
|
|
12
|
+
|
|
13
|
+
// Main body
|
|
14
|
+
var body = new THREE.Mesh(
|
|
15
|
+
new THREE.BoxGeometry(0.50, 0.30, 0.35),
|
|
16
|
+
mat(0x111318, { roughness: 0.42, metalness: 0.18 })
|
|
17
|
+
);
|
|
18
|
+
body.position.y = 0.15;
|
|
19
|
+
body.castShadow = true;
|
|
20
|
+
body.receiveShadow = true;
|
|
21
|
+
g.add(body);
|
|
22
|
+
|
|
23
|
+
// Glass door panel (left portion of front)
|
|
24
|
+
var glassDoor = new THREE.Mesh(
|
|
25
|
+
new THREE.BoxGeometry(0.30, 0.24, 0.012),
|
|
26
|
+
mat(0x1a2530, { transparent: true, opacity: 0.75, roughness: 0.12, metalness: 0.05 })
|
|
27
|
+
);
|
|
28
|
+
glassDoor.position.set(-0.07, 0.15, 0.182);
|
|
29
|
+
g.add(glassDoor);
|
|
30
|
+
|
|
31
|
+
// Glass door inner grill pattern (emissive dark mesh feel)
|
|
32
|
+
var grill = new THREE.Mesh(
|
|
33
|
+
new THREE.BoxGeometry(0.26, 0.20, 0.004),
|
|
34
|
+
mat(0x0d1015, { roughness: 0.80 })
|
|
35
|
+
);
|
|
36
|
+
grill.position.set(-0.07, 0.15, 0.188);
|
|
37
|
+
g.add(grill);
|
|
38
|
+
|
|
39
|
+
// Door frame around glass
|
|
40
|
+
var doorFrame = new THREE.Mesh(
|
|
41
|
+
new THREE.BoxGeometry(0.32, 0.26, 0.016),
|
|
42
|
+
mat(0x1c1f26, { roughness: 0.50, metalness: 0.12 })
|
|
43
|
+
);
|
|
44
|
+
doorFrame.position.set(-0.07, 0.15, 0.179);
|
|
45
|
+
g.add(doorFrame);
|
|
46
|
+
|
|
47
|
+
// Control panel section (right side)
|
|
48
|
+
var panel = new THREE.Mesh(
|
|
49
|
+
new THREE.BoxGeometry(0.12, 0.26, 0.01),
|
|
50
|
+
mat(0x16181e, { roughness: 0.50 })
|
|
51
|
+
);
|
|
52
|
+
panel.position.set(0.18, 0.15, 0.181);
|
|
53
|
+
g.add(panel);
|
|
54
|
+
|
|
55
|
+
// Small display screen
|
|
56
|
+
var display = new THREE.Mesh(
|
|
57
|
+
new THREE.BoxGeometry(0.09, 0.055, 0.008),
|
|
58
|
+
mat(0x001a0a, { emissive: 0x00cc44, emissiveIntensity: 0.85, roughness: 0.1 })
|
|
59
|
+
);
|
|
60
|
+
display.position.set(0.175, 0.22, 0.189);
|
|
61
|
+
g.add(display);
|
|
62
|
+
|
|
63
|
+
// Control buttons (small grid — 3 rows x 2 cols)
|
|
64
|
+
var btnMat = mat(0x2a2d35, { roughness: 0.60 });
|
|
65
|
+
var btnGeo = new THREE.BoxGeometry(0.030, 0.024, 0.010);
|
|
66
|
+
[0, 1, 2].forEach(function(row) {
|
|
67
|
+
[0, 1].forEach(function(col) {
|
|
68
|
+
var btn = new THREE.Mesh(btnGeo, btnMat);
|
|
69
|
+
btn.position.set(0.155 + col * 0.038, 0.16 - row * 0.032, 0.189);
|
|
70
|
+
g.add(btn);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Chrome side vent strips
|
|
75
|
+
var ventMat = PAL.chromeBrushed();
|
|
76
|
+
var ventGeo = new THREE.BoxGeometry(0.006, 0.22, 0.025);
|
|
77
|
+
[-0.248, 0.248].forEach(function(x) {
|
|
78
|
+
var vent = new THREE.Mesh(ventGeo, ventMat);
|
|
79
|
+
vent.position.set(x, 0.15, 0);
|
|
80
|
+
g.add(vent);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Bottom feet
|
|
84
|
+
var feetMat = mat(0x0a0a0a, { roughness: 0.90 });
|
|
85
|
+
var feetGeo = new THREE.BoxGeometry(0.04, 0.014, 0.04);
|
|
86
|
+
[[-0.20, 0.007, 0.14], [0.20, 0.007, 0.14], [-0.20, 0.007, -0.14], [0.20, 0.007, -0.14]].forEach(function(p) {
|
|
87
|
+
var foot = new THREE.Mesh(feetGeo, feetMat);
|
|
88
|
+
foot.position.set(p[0], p[1], p[2]);
|
|
89
|
+
g.add(foot);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return g;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { mat, PAL } from './materials.js';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
id: 'monitor',
|
|
6
|
+
name: 'Monitor',
|
|
7
|
+
category: 'tech',
|
|
8
|
+
icon: 'Mo',
|
|
9
|
+
gridW: 1, gridD: 1, height: 0.5,
|
|
10
|
+
factory: function() {
|
|
11
|
+
var g = new THREE.Group();
|
|
12
|
+
|
|
13
|
+
// Bezel outer frame
|
|
14
|
+
var bezel = new THREE.Mesh(
|
|
15
|
+
new THREE.BoxGeometry(0.50, 0.35, 0.03),
|
|
16
|
+
mat(0x111214, { roughness: 0.45, metalness: 0.10 })
|
|
17
|
+
);
|
|
18
|
+
bezel.position.y = 0.38;
|
|
19
|
+
bezel.castShadow = true;
|
|
20
|
+
g.add(bezel);
|
|
21
|
+
|
|
22
|
+
// Screen inner panel (emissive glow)
|
|
23
|
+
var screen = new THREE.Mesh(
|
|
24
|
+
new THREE.BoxGeometry(0.44, 0.29, 0.012),
|
|
25
|
+
mat(0x0a1628, { emissive: 0x1a4a8a, emissiveIntensity: 0.55, roughness: 0.05 })
|
|
26
|
+
);
|
|
27
|
+
screen.position.y = 0.38;
|
|
28
|
+
screen.position.z = 0.012;
|
|
29
|
+
g.add(screen);
|
|
30
|
+
|
|
31
|
+
// Screen glare strip (top highlight)
|
|
32
|
+
var glare = new THREE.Mesh(
|
|
33
|
+
new THREE.BoxGeometry(0.42, 0.04, 0.001),
|
|
34
|
+
mat(0xaaccff, { transparent: true, opacity: 0.08, roughness: 0.0 })
|
|
35
|
+
);
|
|
36
|
+
glare.position.set(0, 0.505, 0.019);
|
|
37
|
+
g.add(glare);
|
|
38
|
+
|
|
39
|
+
// Chrome neck / stem
|
|
40
|
+
var neck = new THREE.Mesh(
|
|
41
|
+
new THREE.BoxGeometry(0.035, 0.12, 0.035),
|
|
42
|
+
PAL.chrome()
|
|
43
|
+
);
|
|
44
|
+
neck.position.y = 0.175;
|
|
45
|
+
neck.castShadow = true;
|
|
46
|
+
g.add(neck);
|
|
47
|
+
|
|
48
|
+
// Chrome base disk
|
|
49
|
+
var base = new THREE.Mesh(
|
|
50
|
+
new THREE.CylinderGeometry(0.12, 0.14, 0.025, 24),
|
|
51
|
+
PAL.chrome()
|
|
52
|
+
);
|
|
53
|
+
base.position.y = 0.013;
|
|
54
|
+
base.castShadow = true;
|
|
55
|
+
g.add(base);
|
|
56
|
+
|
|
57
|
+
// Power LED dot
|
|
58
|
+
var led = new THREE.Mesh(
|
|
59
|
+
new THREE.SphereGeometry(0.008, 8, 8),
|
|
60
|
+
mat(0x00e5ff, { emissive: 0x00e5ff, emissiveIntensity: 1.0 })
|
|
61
|
+
);
|
|
62
|
+
led.position.set(0.18, 0.215, 0.016);
|
|
63
|
+
g.add(led);
|
|
64
|
+
|
|
65
|
+
return g;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { mat, PAL } from './materials.js';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
id: 'neon_strip',
|
|
6
|
+
name: 'LED Neon Strip',
|
|
7
|
+
category: 'lighting',
|
|
8
|
+
icon: 'NS',
|
|
9
|
+
gridW: 2, gridD: 1, height: 0.03,
|
|
10
|
+
factory: function() {
|
|
11
|
+
var g = new THREE.Group();
|
|
12
|
+
|
|
13
|
+
var W = 2.0;
|
|
14
|
+
var barH = 0.030;
|
|
15
|
+
var barD = 0.030;
|
|
16
|
+
|
|
17
|
+
// Aluminium channel body (housing for strip)
|
|
18
|
+
var channel = new THREE.Mesh(
|
|
19
|
+
new THREE.BoxGeometry(W, barH, barD),
|
|
20
|
+
mat(0x888888, { roughness: 0.20, metalness: 0.75 })
|
|
21
|
+
);
|
|
22
|
+
channel.position.y = barH / 2;
|
|
23
|
+
channel.castShadow = true;
|
|
24
|
+
g.add(channel);
|
|
25
|
+
|
|
26
|
+
// Neon diffuser bar (glowing blue, slightly protruding)
|
|
27
|
+
var strip = new THREE.Mesh(
|
|
28
|
+
new THREE.BoxGeometry(W - 0.02, barH * 0.45, barD * 0.55),
|
|
29
|
+
mat(0x58a6ff, { emissive: 0x58a6ff, emissiveIntensity: 1.4, transparent: true, opacity: 0.92, roughness: 0.0 })
|
|
30
|
+
);
|
|
31
|
+
strip.position.set(0, barH * 0.7, barD * 0.25);
|
|
32
|
+
g.add(strip);
|
|
33
|
+
|
|
34
|
+
// End caps (chrome)
|
|
35
|
+
[-W / 2, W / 2].forEach(function(x) {
|
|
36
|
+
var cap = new THREE.Mesh(
|
|
37
|
+
new THREE.BoxGeometry(0.012, barH + 0.002, barD + 0.002),
|
|
38
|
+
PAL.chrome()
|
|
39
|
+
);
|
|
40
|
+
cap.position.set(x, barH / 2, 0);
|
|
41
|
+
g.add(cap);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Mounting clip pair (evenly spaced, mid-strip)
|
|
45
|
+
[-W * 0.30, W * 0.30].forEach(function(x) {
|
|
46
|
+
var clipBody = new THREE.Mesh(
|
|
47
|
+
new THREE.BoxGeometry(0.020, 0.025, 0.012),
|
|
48
|
+
mat(0x555555, { roughness: 0.45, metalness: 0.50 })
|
|
49
|
+
);
|
|
50
|
+
clipBody.position.set(x, -0.008, -barD / 2 - 0.005);
|
|
51
|
+
g.add(clipBody);
|
|
52
|
+
|
|
53
|
+
// Screw hole indicator
|
|
54
|
+
var screw = new THREE.Mesh(
|
|
55
|
+
new THREE.CylinderGeometry(0.004, 0.004, 0.013, 8),
|
|
56
|
+
mat(0x222222, { roughness: 0.50 })
|
|
57
|
+
);
|
|
58
|
+
screw.rotation.x = Math.PI / 2;
|
|
59
|
+
screw.position.set(x, -0.008, -barD / 2 - 0.012);
|
|
60
|
+
g.add(screw);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Soft ambient glow blob (behind strip for wall bleed effect)
|
|
64
|
+
var glow = new THREE.Mesh(
|
|
65
|
+
new THREE.BoxGeometry(W + 0.10, barH * 2.5, 0.004),
|
|
66
|
+
mat(0x3388ff, { transparent: true, opacity: 0.12, emissive: 0x3388ff, emissiveIntensity: 0.30, roughness: 0.0 })
|
|
67
|
+
);
|
|
68
|
+
glow.position.set(0, barH / 2, -barD / 2 - 0.002);
|
|
69
|
+
g.add(glow);
|
|
70
|
+
|
|
71
|
+
return g;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { mat, PAL } from './materials.js';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
id: 'painting',
|
|
6
|
+
name: 'Painting',
|
|
7
|
+
category: 'decor',
|
|
8
|
+
icon: 'Pa',
|
|
9
|
+
gridW: 1, gridD: 1, height: 1.5,
|
|
10
|
+
factory: function() {
|
|
11
|
+
var g = new THREE.Group();
|
|
12
|
+
|
|
13
|
+
// Canvas (dark abstract)
|
|
14
|
+
var canvas = new THREE.Mesh(
|
|
15
|
+
new THREE.BoxGeometry(1.2, 0.8, 0.04),
|
|
16
|
+
mat(0x1a1522, { roughness: 0.95 })
|
|
17
|
+
);
|
|
18
|
+
canvas.position.set(0, 2, 0);
|
|
19
|
+
canvas.castShadow = true;
|
|
20
|
+
g.add(canvas);
|
|
21
|
+
|
|
22
|
+
// Abstract color block 1 (deep teal)
|
|
23
|
+
var block1 = new THREE.Mesh(
|
|
24
|
+
new THREE.BoxGeometry(0.45, 0.6, 0.045),
|
|
25
|
+
mat(0x0d3d3a, { roughness: 0.90 })
|
|
26
|
+
);
|
|
27
|
+
block1.position.set(-0.22, 2, 0.001);
|
|
28
|
+
g.add(block1);
|
|
29
|
+
|
|
30
|
+
// Abstract color block 2 (dark burgundy)
|
|
31
|
+
var block2 = new THREE.Mesh(
|
|
32
|
+
new THREE.BoxGeometry(0.35, 0.4, 0.045),
|
|
33
|
+
mat(0x3a0d1a, { roughness: 0.90 })
|
|
34
|
+
);
|
|
35
|
+
block2.position.set(0.22, 2.1, 0.001);
|
|
36
|
+
g.add(block2);
|
|
37
|
+
|
|
38
|
+
// Gold frame — top bar
|
|
39
|
+
var frameTop = new THREE.Mesh(
|
|
40
|
+
new THREE.BoxGeometry(1.3, 0.06, 0.07),
|
|
41
|
+
PAL.gold()
|
|
42
|
+
);
|
|
43
|
+
frameTop.position.set(0, 2.43, 0);
|
|
44
|
+
frameTop.castShadow = true;
|
|
45
|
+
g.add(frameTop);
|
|
46
|
+
|
|
47
|
+
// Gold frame — bottom bar
|
|
48
|
+
var frameBot = new THREE.Mesh(
|
|
49
|
+
new THREE.BoxGeometry(1.3, 0.06, 0.07),
|
|
50
|
+
PAL.gold()
|
|
51
|
+
);
|
|
52
|
+
frameBot.position.set(0, 1.57, 0);
|
|
53
|
+
frameBot.castShadow = true;
|
|
54
|
+
g.add(frameBot);
|
|
55
|
+
|
|
56
|
+
// Gold frame — left bar
|
|
57
|
+
var frameLeft = new THREE.Mesh(
|
|
58
|
+
new THREE.BoxGeometry(0.06, 0.92, 0.07),
|
|
59
|
+
PAL.gold()
|
|
60
|
+
);
|
|
61
|
+
frameLeft.position.set(-0.63, 2, 0);
|
|
62
|
+
frameLeft.castShadow = true;
|
|
63
|
+
g.add(frameLeft);
|
|
64
|
+
|
|
65
|
+
// Gold frame — right bar
|
|
66
|
+
var frameRight = new THREE.Mesh(
|
|
67
|
+
new THREE.BoxGeometry(0.06, 0.92, 0.07),
|
|
68
|
+
PAL.gold()
|
|
69
|
+
);
|
|
70
|
+
frameRight.position.set(0.63, 2, 0);
|
|
71
|
+
frameRight.castShadow = true;
|
|
72
|
+
g.add(frameRight);
|
|
73
|
+
|
|
74
|
+
// Wall-mount bracket (small dark rectangle behind)
|
|
75
|
+
var bracket = new THREE.Mesh(
|
|
76
|
+
new THREE.BoxGeometry(0.08, 0.12, 0.04),
|
|
77
|
+
mat(0x111111, { roughness: 0.6, metalness: 0.4 })
|
|
78
|
+
);
|
|
79
|
+
bracket.position.set(0, 2, -0.04);
|
|
80
|
+
g.add(bracket);
|
|
81
|
+
|
|
82
|
+
return g;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { mat, PAL } from './materials.js';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
id: 'palm_tree',
|
|
6
|
+
name: 'Palm Tree',
|
|
7
|
+
category: 'nature',
|
|
8
|
+
icon: 'PT',
|
|
9
|
+
gridW: 1, gridD: 1, height: 3.0,
|
|
10
|
+
factory: function() {
|
|
11
|
+
var g = new THREE.Group();
|
|
12
|
+
|
|
13
|
+
// Planter — dark round pot
|
|
14
|
+
var planter = new THREE.Mesh(
|
|
15
|
+
new THREE.CylinderGeometry(0.28, 0.22, 0.36, 18),
|
|
16
|
+
mat(0x1e2028, { roughness: 0.90 })
|
|
17
|
+
);
|
|
18
|
+
planter.position.y = 0.18;
|
|
19
|
+
planter.castShadow = true;
|
|
20
|
+
planter.receiveShadow = true;
|
|
21
|
+
g.add(planter);
|
|
22
|
+
|
|
23
|
+
// Rim
|
|
24
|
+
var rim = new THREE.Mesh(
|
|
25
|
+
new THREE.TorusGeometry(0.285, 0.022, 8, 28),
|
|
26
|
+
mat(0x13151a, { roughness: 0.85 })
|
|
27
|
+
);
|
|
28
|
+
rim.rotation.x = Math.PI / 2;
|
|
29
|
+
rim.position.y = 0.357;
|
|
30
|
+
g.add(rim);
|
|
31
|
+
|
|
32
|
+
// Soil
|
|
33
|
+
var soil = new THREE.Mesh(
|
|
34
|
+
new THREE.CylinderGeometry(0.26, 0.26, 0.018, 18),
|
|
35
|
+
mat(0x1a1510, { roughness: 0.99 })
|
|
36
|
+
);
|
|
37
|
+
soil.position.y = 0.370;
|
|
38
|
+
g.add(soil);
|
|
39
|
+
|
|
40
|
+
// Trunk — slim, slight taper
|
|
41
|
+
var trunkMat = mat(0x5a4020, { roughness: 0.85 });
|
|
42
|
+
var trunk = new THREE.Mesh(
|
|
43
|
+
new THREE.CylinderGeometry(0.038, 0.060, 2.55, 10),
|
|
44
|
+
trunkMat
|
|
45
|
+
);
|
|
46
|
+
trunk.position.y = 1.655;
|
|
47
|
+
trunk.rotation.z = 0.05; // slight lean
|
|
48
|
+
trunk.castShadow = true;
|
|
49
|
+
g.add(trunk);
|
|
50
|
+
|
|
51
|
+
// Trunk ring details (bark texture rings)
|
|
52
|
+
for (var i = 0; i < 5; i++) {
|
|
53
|
+
var ring = new THREE.Mesh(
|
|
54
|
+
new THREE.TorusGeometry(0.052 - i * 0.002, 0.008, 6, 16),
|
|
55
|
+
mat(0x3d2a10, { roughness: 0.90 })
|
|
56
|
+
);
|
|
57
|
+
ring.position.y = 0.55 + i * 0.45;
|
|
58
|
+
ring.rotation.x = Math.PI / 2;
|
|
59
|
+
g.add(ring);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Palm frond fan — 6 elongated leaves fanning out
|
|
63
|
+
var leafMat = mat(0x2a7a40, { roughness: 0.78, side: THREE.DoubleSide });
|
|
64
|
+
var frondCount = 6;
|
|
65
|
+
for (var j = 0; j < frondCount; j++) {
|
|
66
|
+
var angle = (j / frondCount) * Math.PI * 2;
|
|
67
|
+
var frond = new THREE.Mesh(
|
|
68
|
+
new THREE.BoxGeometry(0.06, 0.60, 0.018),
|
|
69
|
+
leafMat
|
|
70
|
+
);
|
|
71
|
+
frond.position.set(
|
|
72
|
+
Math.sin(angle) * 0.38,
|
|
73
|
+
2.88,
|
|
74
|
+
Math.cos(angle) * 0.38
|
|
75
|
+
);
|
|
76
|
+
frond.rotation.y = -angle;
|
|
77
|
+
frond.rotation.z = 0.55; // droop outward
|
|
78
|
+
frond.castShadow = true;
|
|
79
|
+
g.add(frond);
|
|
80
|
+
|
|
81
|
+
// Frond tip (narrower end piece)
|
|
82
|
+
var tip = new THREE.Mesh(
|
|
83
|
+
new THREE.ConeGeometry(0.025, 0.22, 6),
|
|
84
|
+
leafMat
|
|
85
|
+
);
|
|
86
|
+
tip.position.set(
|
|
87
|
+
Math.sin(angle) * 0.68,
|
|
88
|
+
2.72,
|
|
89
|
+
Math.cos(angle) * 0.68
|
|
90
|
+
);
|
|
91
|
+
tip.rotation.y = -angle;
|
|
92
|
+
tip.rotation.z = Math.PI / 2 - 0.2;
|
|
93
|
+
tip.castShadow = true;
|
|
94
|
+
g.add(tip);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Crown center sphere
|
|
98
|
+
var crown = new THREE.Mesh(
|
|
99
|
+
new THREE.SphereGeometry(0.09, 12, 10),
|
|
100
|
+
mat(0x1e5c30, { roughness: 0.80 })
|
|
101
|
+
);
|
|
102
|
+
crown.position.y = 2.93;
|
|
103
|
+
crown.castShadow = true;
|
|
104
|
+
g.add(crown);
|
|
105
|
+
|
|
106
|
+
return g;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { mat, PAL } from './materials.js';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
id: 'pc_tower',
|
|
6
|
+
name: 'PC Tower',
|
|
7
|
+
category: 'tech',
|
|
8
|
+
icon: 'PC',
|
|
9
|
+
gridW: 1, gridD: 1, height: 0.45,
|
|
10
|
+
factory: function() {
|
|
11
|
+
var g = new THREE.Group();
|
|
12
|
+
|
|
13
|
+
var H = 0.45;
|
|
14
|
+
var W = 0.22;
|
|
15
|
+
var D = 0.40;
|
|
16
|
+
|
|
17
|
+
// Main case body — matte black
|
|
18
|
+
var body = new THREE.Mesh(
|
|
19
|
+
new THREE.BoxGeometry(W, H, D),
|
|
20
|
+
mat(0x0d0d0f, { roughness: 0.50, metalness: 0.18 })
|
|
21
|
+
);
|
|
22
|
+
body.position.y = H / 2;
|
|
23
|
+
body.castShadow = true;
|
|
24
|
+
g.add(body);
|
|
25
|
+
|
|
26
|
+
// RGB tempered glass side panel (right side)
|
|
27
|
+
var glass = new THREE.Mesh(
|
|
28
|
+
new THREE.BoxGeometry(0.005, H - 0.02, D - 0.02),
|
|
29
|
+
mat(0x0a1a3a, { transparent: true, opacity: 0.55, roughness: 0.02, emissive: 0x1144cc, emissiveIntensity: 0.45 })
|
|
30
|
+
);
|
|
31
|
+
glass.position.set(W / 2 + 0.003, H / 2, 0);
|
|
32
|
+
g.add(glass);
|
|
33
|
+
|
|
34
|
+
// Internal RGB strip glow bar (seen through glass)
|
|
35
|
+
var rgbStrip = new THREE.Mesh(
|
|
36
|
+
new THREE.BoxGeometry(0.008, H * 0.80, 0.018),
|
|
37
|
+
mat(0x3355ff, { emissive: 0x2244ff, emissiveIntensity: 1.2 })
|
|
38
|
+
);
|
|
39
|
+
rgbStrip.position.set(W / 2 - 0.025, H / 2, 0);
|
|
40
|
+
g.add(rgbStrip);
|
|
41
|
+
|
|
42
|
+
// Front panel — slightly different texture
|
|
43
|
+
var front = new THREE.Mesh(
|
|
44
|
+
new THREE.BoxGeometry(W - 0.01, H - 0.01, 0.012),
|
|
45
|
+
mat(0x141418, { roughness: 0.38, metalness: 0.22 })
|
|
46
|
+
);
|
|
47
|
+
front.position.set(0, H / 2, D / 2 + 0.001);
|
|
48
|
+
front.castShadow = true;
|
|
49
|
+
g.add(front);
|
|
50
|
+
|
|
51
|
+
// Power button (front top)
|
|
52
|
+
var pwrBtn = new THREE.Mesh(
|
|
53
|
+
new THREE.CylinderGeometry(0.012, 0.012, 0.006, 16),
|
|
54
|
+
mat(0x555577, { roughness: 0.30, metalness: 0.60 })
|
|
55
|
+
);
|
|
56
|
+
pwrBtn.rotation.x = Math.PI / 2;
|
|
57
|
+
pwrBtn.position.set(-0.04, H - 0.055, D / 2 + 0.007);
|
|
58
|
+
g.add(pwrBtn);
|
|
59
|
+
|
|
60
|
+
// Power LED ring around button
|
|
61
|
+
var pwrLED = new THREE.Mesh(
|
|
62
|
+
new THREE.TorusGeometry(0.013, 0.003, 8, 16),
|
|
63
|
+
mat(0x00aaff, { emissive: 0x0088ff, emissiveIntensity: 0.9 })
|
|
64
|
+
);
|
|
65
|
+
pwrLED.rotation.x = Math.PI / 2;
|
|
66
|
+
pwrLED.position.set(-0.04, H - 0.055, D / 2 + 0.009);
|
|
67
|
+
g.add(pwrLED);
|
|
68
|
+
|
|
69
|
+
// USB ports strip (front)
|
|
70
|
+
var usbBar = new THREE.Mesh(
|
|
71
|
+
new THREE.BoxGeometry(0.06, 0.018, 0.005),
|
|
72
|
+
mat(0x222222, { roughness: 0.70 })
|
|
73
|
+
);
|
|
74
|
+
usbBar.position.set(0.03, H - 0.065, D / 2 + 0.008);
|
|
75
|
+
g.add(usbBar);
|
|
76
|
+
|
|
77
|
+
// Bottom feet
|
|
78
|
+
[-W * 0.35, W * 0.35].forEach(function(xOff) {
|
|
79
|
+
[-D * 0.38, D * 0.38].forEach(function(zOff) {
|
|
80
|
+
var foot = new THREE.Mesh(
|
|
81
|
+
new THREE.BoxGeometry(0.025, 0.012, 0.025),
|
|
82
|
+
PAL.rubber()
|
|
83
|
+
);
|
|
84
|
+
foot.position.set(xOff, 0.006, zOff);
|
|
85
|
+
g.add(foot);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return g;
|
|
90
|
+
}
|
|
91
|
+
};
|