senangwebs-tour 1.0.2 → 1.0.3
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/README.md +190 -217
- package/dist/swt-editor.css +759 -730
- package/dist/swt-editor.css.map +1 -1
- package/dist/swt-editor.js +550 -602
- package/dist/swt-editor.js.map +1 -1
- package/dist/swt-editor.min.css +1 -1
- package/dist/swt-editor.min.js +1 -1
- package/dist/swt.js +56531 -867
- package/dist/swt.js.map +1 -1
- package/dist/swt.min.js +12 -1
- package/package.json +6 -3
- package/src/editor/css/main.css +610 -581
- package/src/editor/js/export-manager.js +175 -262
- package/src/editor/js/preview-controller.js +36 -17
- package/src/editor/js/ui-controller.js +377 -362
- package/src/index.js +45 -33
|
@@ -1,387 +1,402 @@
|
|
|
1
1
|
// UI Controller - Handles DOM manipulation and rendering
|
|
2
2
|
|
|
3
3
|
class UIController {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
4
|
+
constructor(editor) {
|
|
5
|
+
this.editor = editor;
|
|
6
|
+
this.sceneList = document.getElementById("sceneList");
|
|
7
|
+
this.hotspotList = document.getElementById("hotspotList");
|
|
8
|
+
this.draggedElement = null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Render scene list
|
|
13
|
+
*/
|
|
14
|
+
renderSceneList() {
|
|
15
|
+
if (!this.sceneList) return;
|
|
16
|
+
|
|
17
|
+
this.sceneList.innerHTML = "";
|
|
18
|
+
const scenes = this.editor.sceneManager.getAllScenes();
|
|
19
|
+
const currentIndex = this.editor.sceneManager.currentSceneIndex;
|
|
20
|
+
|
|
21
|
+
if (scenes.length === 0) {
|
|
22
|
+
const empty = document.createElement("div");
|
|
23
|
+
empty.className = "empty-state";
|
|
24
|
+
empty.innerHTML = `
|
|
25
25
|
<p>No scenes yet</p>
|
|
26
26
|
<p class="hint">Click "Add Scene" to upload a 360° panorama</p>
|
|
27
27
|
`;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
scenes.forEach((scene, index) => {
|
|
33
|
-
const card = this.createSceneCard(scene, index, index === currentIndex);
|
|
34
|
-
this.sceneList.appendChild(card);
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Create scene card element
|
|
40
|
-
*/
|
|
41
|
-
createSceneCard(scene, index, isActive) {
|
|
42
|
-
const card = document.createElement('div');
|
|
43
|
-
card.className = 'scene-card' + (isActive ? ' active' : '');
|
|
44
|
-
card.draggable = true;
|
|
45
|
-
card.dataset.index = index;
|
|
46
|
-
|
|
47
|
-
// Drag handle
|
|
48
|
-
const dragHandle = document.createElement('div');
|
|
49
|
-
dragHandle.className = 'drag-handle';
|
|
50
|
-
dragHandle.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free v6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M0 96C0 78.3 14.3 64 32 64l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 288c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32L32 448c-17.7 0-32-14.3-32-32s14.3-32 32-32l384 0c17.7 0 32 14.3 32 32z"/></svg>';
|
|
51
|
-
|
|
52
|
-
// Thumbnail
|
|
53
|
-
const thumbnail = document.createElement('img');
|
|
54
|
-
thumbnail.src = scene.thumbnail || scene.imageUrl;
|
|
55
|
-
thumbnail.alt = scene.name;
|
|
56
|
-
|
|
57
|
-
// Info
|
|
58
|
-
const info = document.createElement('div');
|
|
59
|
-
info.className = 'scene-info';
|
|
60
|
-
|
|
61
|
-
const name = document.createElement('div');
|
|
62
|
-
name.className = 'scene-name';
|
|
63
|
-
name.textContent = scene.name;
|
|
64
|
-
|
|
65
|
-
const meta = document.createElement('div');
|
|
66
|
-
meta.className = 'scene-meta';
|
|
67
|
-
meta.textContent = `${scene.hotspots.length} hotspot${scene.hotspots.length !== 1 ? 's' : ''}`;
|
|
68
|
-
|
|
69
|
-
info.appendChild(name);
|
|
70
|
-
info.appendChild(meta);
|
|
71
|
-
|
|
72
|
-
// Actions
|
|
73
|
-
const actions = document.createElement('div');
|
|
74
|
-
actions.className = 'scene-actions';
|
|
75
|
-
|
|
76
|
-
const deleteBtn = document.createElement('button');
|
|
77
|
-
deleteBtn.className = 'btn-icon';
|
|
78
|
-
deleteBtn.innerHTML = '🗑️';
|
|
79
|
-
deleteBtn.title = 'Delete scene';
|
|
80
|
-
deleteBtn.onclick = (e) => {
|
|
81
|
-
e.stopPropagation();
|
|
82
|
-
this.editor.removeScene(index);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
actions.appendChild(deleteBtn);
|
|
86
|
-
|
|
87
|
-
card.appendChild(dragHandle);
|
|
88
|
-
card.appendChild(thumbnail);
|
|
89
|
-
card.appendChild(info);
|
|
90
|
-
card.appendChild(actions);
|
|
91
|
-
|
|
92
|
-
// Click handler
|
|
93
|
-
card.onclick = () => {
|
|
94
|
-
this.editor.selectScene(index);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// Drag and drop handlers
|
|
98
|
-
this.setupDragAndDrop(card);
|
|
99
|
-
|
|
100
|
-
return card;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Setup drag and drop for scene reordering
|
|
105
|
-
*/
|
|
106
|
-
setupDragAndDrop(card) {
|
|
107
|
-
card.addEventListener('dragstart', (e) => {
|
|
108
|
-
this.draggedElement = card;
|
|
109
|
-
card.classList.add('dragging');
|
|
110
|
-
e.dataTransfer.effectAllowed = 'move';
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
card.addEventListener('dragend', () => {
|
|
114
|
-
card.classList.remove('dragging');
|
|
115
|
-
this.draggedElement = null;
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
card.addEventListener('dragover', (e) => {
|
|
119
|
-
e.preventDefault();
|
|
120
|
-
e.dataTransfer.dropEffect = 'move';
|
|
121
|
-
|
|
122
|
-
if (this.draggedElement && this.draggedElement !== card) {
|
|
123
|
-
const bounding = card.getBoundingClientRect();
|
|
124
|
-
const offset = bounding.y + bounding.height / 2;
|
|
125
|
-
|
|
126
|
-
if (e.clientY - offset > 0) {
|
|
127
|
-
card.style.borderBottom = '2px solid var(--accent-color)';
|
|
128
|
-
card.style.borderTop = '';
|
|
129
|
-
} else {
|
|
130
|
-
card.style.borderTop = '2px solid var(--accent-color)';
|
|
131
|
-
card.style.borderBottom = '';
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
card.addEventListener('dragleave', () => {
|
|
137
|
-
card.style.borderTop = '';
|
|
138
|
-
card.style.borderBottom = '';
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
card.addEventListener('drop', (e) => {
|
|
142
|
-
e.preventDefault();
|
|
143
|
-
card.style.borderTop = '';
|
|
144
|
-
card.style.borderBottom = '';
|
|
145
|
-
|
|
146
|
-
if (this.draggedElement && this.draggedElement !== card) {
|
|
147
|
-
const fromIndex = parseInt(this.draggedElement.dataset.index);
|
|
148
|
-
const toIndex = parseInt(card.dataset.index);
|
|
149
|
-
this.editor.reorderScenes(fromIndex, toIndex);
|
|
150
|
-
}
|
|
151
|
-
});
|
|
28
|
+
this.sceneList.appendChild(empty);
|
|
29
|
+
return;
|
|
152
30
|
}
|
|
153
31
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
32
|
+
scenes.forEach((scene, index) => {
|
|
33
|
+
const card = this.createSceneCard(scene, index, index === currentIndex);
|
|
34
|
+
this.sceneList.appendChild(card);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Create scene card element
|
|
40
|
+
*/
|
|
41
|
+
createSceneCard(scene, index, isActive) {
|
|
42
|
+
const card = document.createElement("div");
|
|
43
|
+
card.className = "scene-card" + (isActive ? " active" : "");
|
|
44
|
+
card.draggable = true;
|
|
45
|
+
card.dataset.index = index;
|
|
46
|
+
|
|
47
|
+
// Drag handle
|
|
48
|
+
const dragHandle = document.createElement("div");
|
|
49
|
+
dragHandle.className = "drag-handle";
|
|
50
|
+
dragHandle.innerHTML =
|
|
51
|
+
'<ss-icon icon="arrow-up-down-left-right" thickness="2.2"></ss-icon>';
|
|
52
|
+
|
|
53
|
+
// Thumbnail
|
|
54
|
+
const thumbnail = document.createElement("img");
|
|
55
|
+
thumbnail.src = scene.thumbnail || scene.imageUrl;
|
|
56
|
+
thumbnail.alt = scene.name;
|
|
57
|
+
|
|
58
|
+
// Info
|
|
59
|
+
const info = document.createElement("div");
|
|
60
|
+
info.className = "scene-info";
|
|
61
|
+
|
|
62
|
+
const name = document.createElement("div");
|
|
63
|
+
name.className = "scene-name";
|
|
64
|
+
name.textContent = scene.name;
|
|
65
|
+
|
|
66
|
+
const meta = document.createElement("div");
|
|
67
|
+
meta.className = "scene-meta";
|
|
68
|
+
meta.textContent = `${scene.hotspots.length} hotspot${
|
|
69
|
+
scene.hotspots.length !== 1 ? "s" : ""
|
|
70
|
+
}`;
|
|
71
|
+
|
|
72
|
+
info.appendChild(name);
|
|
73
|
+
info.appendChild(meta);
|
|
74
|
+
|
|
75
|
+
// Actions
|
|
76
|
+
const actions = document.createElement("div");
|
|
77
|
+
actions.className = "scene-actions";
|
|
78
|
+
|
|
79
|
+
const deleteBtn = document.createElement("button");
|
|
80
|
+
deleteBtn.className = "btn-icon";
|
|
81
|
+
deleteBtn.innerHTML = '<ss-icon icon="trash" thickness="2.2"></ss-icon>';
|
|
82
|
+
deleteBtn.title = "Delete scene";
|
|
83
|
+
deleteBtn.onclick = (e) => {
|
|
84
|
+
e.stopPropagation();
|
|
85
|
+
this.editor.removeScene(index);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
actions.appendChild(deleteBtn);
|
|
89
|
+
|
|
90
|
+
card.appendChild(dragHandle);
|
|
91
|
+
card.appendChild(thumbnail);
|
|
92
|
+
card.appendChild(info);
|
|
93
|
+
card.appendChild(actions);
|
|
94
|
+
|
|
95
|
+
// Click handler
|
|
96
|
+
card.onclick = () => {
|
|
97
|
+
this.editor.selectScene(index);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Drag and drop handlers
|
|
101
|
+
this.setupDragAndDrop(card);
|
|
102
|
+
|
|
103
|
+
return card;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Setup drag and drop for scene reordering
|
|
108
|
+
*/
|
|
109
|
+
setupDragAndDrop(card) {
|
|
110
|
+
card.addEventListener("dragstart", (e) => {
|
|
111
|
+
this.draggedElement = card;
|
|
112
|
+
card.classList.add("dragging");
|
|
113
|
+
e.dataTransfer.effectAllowed = "move";
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
card.addEventListener("dragend", () => {
|
|
117
|
+
card.classList.remove("dragging");
|
|
118
|
+
this.draggedElement = null;
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
card.addEventListener("dragover", (e) => {
|
|
122
|
+
e.preventDefault();
|
|
123
|
+
e.dataTransfer.dropEffect = "move";
|
|
124
|
+
|
|
125
|
+
if (this.draggedElement && this.draggedElement !== card) {
|
|
126
|
+
const bounding = card.getBoundingClientRect();
|
|
127
|
+
const offset = bounding.y + bounding.height / 2;
|
|
128
|
+
|
|
129
|
+
if (e.clientY - offset > 0) {
|
|
130
|
+
card.style.borderBottom = "2px solid var(--accent-color)";
|
|
131
|
+
card.style.borderTop = "";
|
|
201
132
|
} else {
|
|
202
|
-
|
|
133
|
+
card.style.borderTop = "2px solid var(--accent-color)";
|
|
134
|
+
card.style.borderBottom = "";
|
|
203
135
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
card.addEventListener("dragleave", () => {
|
|
140
|
+
card.style.borderTop = "";
|
|
141
|
+
card.style.borderBottom = "";
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
card.addEventListener("drop", (e) => {
|
|
145
|
+
e.preventDefault();
|
|
146
|
+
card.style.borderTop = "";
|
|
147
|
+
card.style.borderBottom = "";
|
|
148
|
+
|
|
149
|
+
if (this.draggedElement && this.draggedElement !== card) {
|
|
150
|
+
const fromIndex = parseInt(this.draggedElement.dataset.index);
|
|
151
|
+
const toIndex = parseInt(card.dataset.index);
|
|
152
|
+
this.editor.reorderScenes(fromIndex, toIndex);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Render hotspot list
|
|
159
|
+
*/
|
|
160
|
+
renderHotspotList() {
|
|
161
|
+
if (!this.hotspotList) return;
|
|
162
|
+
|
|
163
|
+
this.hotspotList.innerHTML = "";
|
|
164
|
+
const hotspots = this.editor.hotspotEditor.getAllHotspots();
|
|
165
|
+
const currentIndex = this.editor.hotspotEditor.currentHotspotIndex;
|
|
166
|
+
|
|
167
|
+
if (hotspots.length === 0) {
|
|
168
|
+
const empty = document.createElement("div");
|
|
169
|
+
empty.className = "empty-state";
|
|
170
|
+
empty.textContent = 'No hotspots. Click "Add Hotspot" to create one.';
|
|
171
|
+
this.hotspotList.appendChild(empty);
|
|
172
|
+
return;
|
|
231
173
|
}
|
|
232
174
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
// Update position inputs
|
|
274
|
-
const pos = hotspot.position || { x: 0, y: 0, z: 0 };
|
|
275
|
-
document.getElementById('hotspotPosX').value = pos.x;
|
|
276
|
-
document.getElementById('hotspotPosY').value = pos.y;
|
|
277
|
-
document.getElementById('hotspotPosZ').value = pos.z;
|
|
278
|
-
|
|
279
|
-
// Update target dropdown
|
|
280
|
-
this.updateTargetSceneOptions();
|
|
175
|
+
hotspots.forEach((hotspot, index) => {
|
|
176
|
+
const item = this.createHotspotItem(
|
|
177
|
+
hotspot,
|
|
178
|
+
index,
|
|
179
|
+
index === currentIndex
|
|
180
|
+
);
|
|
181
|
+
this.hotspotList.appendChild(item);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Create hotspot list item
|
|
187
|
+
*/
|
|
188
|
+
createHotspotItem(hotspot, index, isActive) {
|
|
189
|
+
const item = document.createElement("div");
|
|
190
|
+
item.className = "hotspot-item" + (isActive ? " active" : "");
|
|
191
|
+
|
|
192
|
+
const color = document.createElement("div");
|
|
193
|
+
color.className = "hotspot-color";
|
|
194
|
+
color.style.backgroundColor = hotspot.color;
|
|
195
|
+
|
|
196
|
+
const info = document.createElement("div");
|
|
197
|
+
info.className = "hotspot-info";
|
|
198
|
+
|
|
199
|
+
const title = document.createElement("div");
|
|
200
|
+
title.className = "hotspot-title";
|
|
201
|
+
title.textContent = hotspot.title || "Untitled Hotspot";
|
|
202
|
+
|
|
203
|
+
const target = document.createElement("div");
|
|
204
|
+
target.className = "hotspot-target";
|
|
205
|
+
if (hotspot.targetSceneId) {
|
|
206
|
+
const targetScene = this.editor.sceneManager.getSceneById(
|
|
207
|
+
hotspot.targetSceneId
|
|
208
|
+
);
|
|
209
|
+
target.textContent = targetScene
|
|
210
|
+
? `→ ${targetScene.name}`
|
|
211
|
+
: `→ ${hotspot.targetSceneId}`;
|
|
212
|
+
} else {
|
|
213
|
+
target.textContent = "No target";
|
|
281
214
|
}
|
|
282
215
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
216
|
+
info.appendChild(title);
|
|
217
|
+
info.appendChild(target);
|
|
218
|
+
|
|
219
|
+
const actions = document.createElement("div");
|
|
220
|
+
actions.className = "hotspot-actions";
|
|
221
|
+
|
|
222
|
+
const deleteBtn = document.createElement("button");
|
|
223
|
+
deleteBtn.className = "btn-delete";
|
|
224
|
+
deleteBtn.innerHTML = '<ss-icon icon="trash" thickness="2.2"></ss-icon>';
|
|
225
|
+
deleteBtn.title = "Delete";
|
|
226
|
+
deleteBtn.onclick = (e) => {
|
|
227
|
+
e.stopPropagation();
|
|
228
|
+
this.editor.removeHotspot(index);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
actions.appendChild(deleteBtn);
|
|
232
|
+
|
|
233
|
+
item.appendChild(color);
|
|
234
|
+
item.appendChild(info);
|
|
235
|
+
item.appendChild(actions);
|
|
236
|
+
|
|
237
|
+
item.onclick = () => {
|
|
238
|
+
this.editor.selectHotspot(index);
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
return item;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Update properties panel for hotspot
|
|
246
|
+
*/
|
|
247
|
+
updateHotspotProperties(hotspot) {
|
|
248
|
+
const hotspotAll = document.getElementById("hotspotAll");
|
|
249
|
+
const hotspotProperties = document.getElementById("hotspotProperties");
|
|
250
|
+
|
|
251
|
+
if (!hotspot) {
|
|
252
|
+
// No hotspot selected - show list, hide properties
|
|
253
|
+
if (hotspotAll) hotspotAll.style.display = "block";
|
|
254
|
+
if (hotspotProperties) hotspotProperties.style.display = "none";
|
|
255
|
+
|
|
256
|
+
// Clear form
|
|
257
|
+
document.getElementById("hotspotTitle").value = "";
|
|
258
|
+
document.getElementById("hotspotDescription").value = "";
|
|
259
|
+
document.getElementById("hotspotTarget").value = "";
|
|
260
|
+
document.getElementById("hotspotColor").value = "#00ff00";
|
|
261
|
+
const colorText = document.getElementById("hotspotColorText");
|
|
262
|
+
if (colorText) colorText.value = "#00ff00";
|
|
263
|
+
document.getElementById("hotspotPosX").value = "";
|
|
264
|
+
document.getElementById("hotspotPosY").value = "";
|
|
265
|
+
document.getElementById("hotspotPosZ").value = "";
|
|
266
|
+
return;
|
|
297
267
|
}
|
|
298
268
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
269
|
+
// Hotspot selected - show both list and properties
|
|
270
|
+
if (hotspotAll) hotspotAll.style.display = "block";
|
|
271
|
+
if (hotspotProperties) hotspotProperties.style.display = "block";
|
|
272
|
+
|
|
273
|
+
document.getElementById("hotspotTitle").value = hotspot.title || "";
|
|
274
|
+
document.getElementById("hotspotDescription").value =
|
|
275
|
+
hotspot.description || "";
|
|
276
|
+
document.getElementById("hotspotTarget").value =
|
|
277
|
+
hotspot.targetSceneId || "";
|
|
278
|
+
document.getElementById("hotspotColor").value = hotspot.color || "#00ff00";
|
|
279
|
+
|
|
280
|
+
// Update color text input if it exists
|
|
281
|
+
const colorText = document.getElementById("hotspotColorText");
|
|
282
|
+
if (colorText) {
|
|
283
|
+
colorText.value = hotspot.color || "#00ff00";
|
|
314
284
|
}
|
|
315
285
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
select.value = currentValue;
|
|
286
|
+
// Update position inputs
|
|
287
|
+
const pos = hotspot.position || { x: 0, y: 0, z: 0 };
|
|
288
|
+
document.getElementById("hotspotPosX").value = pos.x;
|
|
289
|
+
document.getElementById("hotspotPosY").value = pos.y;
|
|
290
|
+
document.getElementById("hotspotPosZ").value = pos.z;
|
|
291
|
+
|
|
292
|
+
// Update target dropdown
|
|
293
|
+
this.updateTargetSceneOptions();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Update properties panel for scene
|
|
298
|
+
*/
|
|
299
|
+
updateSceneProperties(scene) {
|
|
300
|
+
if (!scene) {
|
|
301
|
+
document.getElementById("sceneId").value = "";
|
|
302
|
+
document.getElementById("sceneName").value = "";
|
|
303
|
+
document.getElementById("sceneImageUrl").value = "";
|
|
304
|
+
return;
|
|
336
305
|
}
|
|
337
306
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
307
|
+
document.getElementById("sceneId").value = scene.id || "";
|
|
308
|
+
document.getElementById("sceneName").value = scene.name || "";
|
|
309
|
+
document.getElementById("sceneImageUrl").value = scene.imageUrl || "";
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Update properties panel for tour
|
|
314
|
+
*/
|
|
315
|
+
updateTourProperties(config) {
|
|
316
|
+
document.getElementById("tourTitle").value = config.title || "";
|
|
317
|
+
document.getElementById("tourDescription").value = config.description || "";
|
|
318
|
+
document.getElementById("tourInitialScene").value =
|
|
319
|
+
config.initialSceneId || "";
|
|
320
|
+
document.getElementById("tourAutoRotate").checked =
|
|
321
|
+
config.autoRotate || false;
|
|
322
|
+
document.getElementById("tourShowCompass").checked =
|
|
323
|
+
config.showCompass || false;
|
|
324
|
+
|
|
325
|
+
// Also update project name in header if it exists
|
|
326
|
+
const projectName = document.getElementById("project-name");
|
|
327
|
+
if (projectName) {
|
|
328
|
+
projectName.value = config.title || "";
|
|
358
329
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Update target scene options in hotspot properties
|
|
334
|
+
*/
|
|
335
|
+
updateTargetSceneOptions() {
|
|
336
|
+
const select = document.getElementById("hotspotTarget");
|
|
337
|
+
if (!select) return;
|
|
338
|
+
|
|
339
|
+
const scenes = this.editor.sceneManager.getAllScenes();
|
|
340
|
+
const currentValue = select.value;
|
|
341
|
+
|
|
342
|
+
select.innerHTML = '<option value="">Select target scene...</option>';
|
|
343
|
+
|
|
344
|
+
scenes.forEach((scene) => {
|
|
345
|
+
const option = document.createElement("option");
|
|
346
|
+
option.value = scene.id;
|
|
347
|
+
option.textContent = scene.name;
|
|
348
|
+
select.appendChild(option);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
select.value = currentValue;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Update initial scene options in tour properties
|
|
356
|
+
*/
|
|
357
|
+
updateInitialSceneOptions() {
|
|
358
|
+
const select = document.getElementById("tourInitialScene");
|
|
359
|
+
if (!select) return;
|
|
360
|
+
|
|
361
|
+
const scenes = this.editor.sceneManager.getAllScenes();
|
|
362
|
+
const currentValue = select.value;
|
|
363
|
+
|
|
364
|
+
select.innerHTML = '<option value="">Select initial scene...</option>';
|
|
365
|
+
|
|
366
|
+
scenes.forEach((scene) => {
|
|
367
|
+
const option = document.createElement("option");
|
|
368
|
+
option.value = scene.id;
|
|
369
|
+
option.textContent = scene.name;
|
|
370
|
+
select.appendChild(option);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
select.value = currentValue;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Show/hide loading indicator
|
|
378
|
+
*/
|
|
379
|
+
setLoading(isLoading) {
|
|
380
|
+
const indicator = document.querySelector(".loading-indicator");
|
|
381
|
+
if (indicator) {
|
|
382
|
+
indicator.style.display = isLoading ? "block" : "none";
|
|
383
383
|
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Switch properties tab
|
|
388
|
+
*/
|
|
389
|
+
switchTab(tabName) {
|
|
390
|
+
// Update tab buttons
|
|
391
|
+
document.querySelectorAll(".tab-btn").forEach((btn) => {
|
|
392
|
+
btn.classList.toggle("active", btn.dataset.tab === tabName);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// Update tab content
|
|
396
|
+
document.querySelectorAll(".tab-content").forEach((content) => {
|
|
397
|
+
content.classList.toggle("active", content.id === tabName + "Tab");
|
|
398
|
+
});
|
|
399
|
+
}
|
|
384
400
|
}
|
|
385
401
|
|
|
386
|
-
|
|
387
402
|
export default UIController;
|