opencroc 1.8.0 → 1.8.2
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/dist/cli/index.js +1107 -49
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +128 -1
- package/dist/index.js +548 -0
- package/dist/index.js.map +1 -1
- package/dist/web/dist/assets/main-Ccg3eDNK.js +1 -0
- package/dist/web/dist/assets/office-runtime-B3iNctxE.css +1 -0
- package/dist/web/dist/assets/office-runtime-BsCh82Pj.js +183 -0
- package/dist/web/dist/assets/pixel-page-3BYGm7dH.js +470 -0
- package/dist/web/dist/assets/react-vendor-C8RhVn0h.js +49 -0
- package/dist/web/dist/assets/studio-page-BInoyoV2.css +1 -0
- package/dist/web/dist/assets/studio-page-o3SCvE_v.js +351 -0
- package/dist/web/dist/assets/three-addons-BdrPp04O.js +470 -0
- package/dist/web/dist/assets/three-core-CsxM1PCY.js +4057 -0
- package/dist/web/dist/index.html +15 -0
- package/dist/web/index.html +11 -572
- package/dist/web/public/botreview/char_0.png +0 -0
- package/dist/web/public/botreview/char_1.png +0 -0
- package/dist/web/public/botreview/char_2.png +0 -0
- package/dist/web/public/botreview/coffee-machine.gif +0 -0
- package/dist/web/public/botreview/server.gif +0 -0
- package/dist/web/public/botreview/walls.png +0 -0
- package/dist/web/public/star/desk-v3.webp +0 -0
- package/dist/web/public/star/office_bg_small.webp +0 -0
- package/dist/web/public/star/star-idle-v5.png +0 -0
- package/dist/web/public/star/star-working-spritesheet-grid.webp +0 -0
- package/dist/web/src/app/AppLayout.tsx +34 -0
- package/dist/web/src/app/AppRouter.tsx +46 -0
- package/dist/web/src/app/bootstrap.tsx +22 -0
- package/dist/web/src/app/routes.tsx +52 -0
- package/dist/web/src/features/office/runtime/index.ts +1 -0
- package/dist/web/src/features/office/runtime/mount.ts +809 -0
- package/dist/web/src/features/pixel/runtime/index.ts +1 -0
- package/dist/web/src/features/pixel/runtime/mount.ts +728 -0
- package/dist/web/src/features/studio/runtime/index.ts +1 -0
- package/dist/web/src/features/studio/runtime/mount.ts +664 -0
- package/dist/web/src/features/three/engine/index.ts +1 -0
- package/dist/web/src/main.tsx +7 -0
- package/dist/web/src/pages/office/index.ts +1 -0
- package/dist/web/src/pages/office/page.tsx +283 -0
- package/dist/web/src/pages/pixel/index.ts +1 -0
- package/dist/web/src/pages/pixel/page.tsx +564 -0
- package/dist/web/src/pages/studio/index.ts +1 -0
- package/dist/web/src/pages/studio/page.tsx +446 -0
- package/dist/web/{js/agents.js → src/runtime/agents.ts} +304 -31
- package/dist/web/{js/camera.js → src/runtime/camera.ts} +12 -5
- package/dist/web/{js/dataviz.js → src/runtime/dataviz.ts} +38 -14
- package/dist/web/{js/effects.js → src/runtime/effects.ts} +139 -2
- package/dist/web/{js/engine.js → src/runtime/engine.ts} +45 -6
- package/dist/web/{js/office.js → src/runtime/office.ts} +136 -20
- package/dist/web/{js/ui.js → src/runtime/ui.ts} +11 -7
- package/dist/web/src/shared/assets.ts +4 -0
- package/dist/web/src/shared/navigation.ts +47 -0
- package/dist/web/src/styles/app-layout.css +19 -0
- package/dist/web/src/styles/office.css +268 -0
- package/dist/web/tsconfig.json +28 -0
- package/dist/web/vite.config.ts +93 -0
- package/package.json +11 -2
- package/dist/web/index-studio.html +0 -804
- package/dist/web/index-v2-pixel.html +0 -1571
- /package/dist/web/{assets → dist}/botreview/char_0.png +0 -0
- /package/dist/web/{assets → dist}/botreview/char_1.png +0 -0
- /package/dist/web/{assets → dist}/botreview/char_2.png +0 -0
- /package/dist/web/{assets → dist}/botreview/coffee-machine.gif +0 -0
- /package/dist/web/{assets → dist}/botreview/server.gif +0 -0
- /package/dist/web/{assets → dist}/botreview/walls.png +0 -0
- /package/dist/web/{assets → dist}/star/desk-v3.webp +0 -0
- /package/dist/web/{assets → dist}/star/office_bg_small.webp +0 -0
- /package/dist/web/{assets → dist}/star/star-idle-v5.png +0 -0
- /package/dist/web/{assets → dist}/star/star-working-spritesheet-grid.webp +0 -0
- /package/dist/web/{js/state.js → src/runtime/state.ts} +0 -0
|
@@ -15,11 +15,21 @@ import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
|
|
|
15
15
|
/* ─── Module-level singletons ──────────────────────────────────────────────── */
|
|
16
16
|
let renderer = null;
|
|
17
17
|
let scene = null;
|
|
18
|
-
let camera = null;
|
|
19
|
-
let composer = null;
|
|
20
|
-
let clock = null;
|
|
21
|
-
let bloomPass = null;
|
|
22
|
-
let fxaaPass = null;
|
|
18
|
+
let camera = null;
|
|
19
|
+
let composer = null;
|
|
20
|
+
let clock = null;
|
|
21
|
+
let bloomPass = null;
|
|
22
|
+
let fxaaPass = null;
|
|
23
|
+
|
|
24
|
+
function disposeMaterial(material) {
|
|
25
|
+
if (!material) return;
|
|
26
|
+
for (const value of Object.values(material)) {
|
|
27
|
+
if (value && typeof value === 'object' && value.isTexture) {
|
|
28
|
+
value.dispose();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
material.dispose?.();
|
|
32
|
+
}
|
|
23
33
|
|
|
24
34
|
/* ═══════════════════════════════════════════════════════════════════════════════
|
|
25
35
|
1. createEngine — Initialize the full Three.js rendering pipeline
|
|
@@ -482,7 +492,36 @@ export function updateEngineTheme(theme) {
|
|
|
482
492
|
/* ═══════════════════════════════════════════════════════════════════════════════
|
|
483
493
|
10. Getters
|
|
484
494
|
═══════════════════════════════════════════════════════════════════════════════ */
|
|
485
|
-
export function
|
|
495
|
+
export function disposeEngine() {
|
|
496
|
+
if (scene) {
|
|
497
|
+
scene.traverse((child) => {
|
|
498
|
+
child.geometry?.dispose?.();
|
|
499
|
+
if (Array.isArray(child.material)) {
|
|
500
|
+
child.material.forEach(disposeMaterial);
|
|
501
|
+
} else {
|
|
502
|
+
disposeMaterial(child.material);
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
while (scene.children.length > 0) {
|
|
507
|
+
scene.remove(scene.children[0]);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
composer?.passes?.forEach?.((pass) => pass.dispose?.());
|
|
512
|
+
composer?.dispose?.();
|
|
513
|
+
renderer?.dispose?.();
|
|
514
|
+
|
|
515
|
+
renderer = null;
|
|
516
|
+
scene = null;
|
|
517
|
+
camera = null;
|
|
518
|
+
composer = null;
|
|
519
|
+
clock = null;
|
|
520
|
+
bloomPass = null;
|
|
521
|
+
fxaaPass = null;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
export function getRenderer() { return renderer; }
|
|
486
525
|
export function getScene() { return scene; }
|
|
487
526
|
export function getCamera() { return camera; }
|
|
488
527
|
export function getComposer() { return composer; }
|
|
@@ -5,13 +5,33 @@
|
|
|
5
5
|
═══════════════════════════════════════════════════════════════════════════════ */
|
|
6
6
|
|
|
7
7
|
import * as THREE from 'three';
|
|
8
|
-
import { getScene } from './engine
|
|
8
|
+
import { getScene } from './engine';
|
|
9
9
|
|
|
10
10
|
let officeGroup = null;
|
|
11
11
|
let currentTheme = 'dark';
|
|
12
12
|
|
|
13
13
|
/* ─── Material Cache ───────────────────────────────────────────────────────── */
|
|
14
|
-
const MAT = {};
|
|
14
|
+
const MAT = {};
|
|
15
|
+
|
|
16
|
+
function disposeMaterialCache() {
|
|
17
|
+
Object.keys(MAT).forEach((key) => {
|
|
18
|
+
MAT[key]?.dispose?.();
|
|
19
|
+
delete MAT[key];
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function disposeOfficeGroup(scene) {
|
|
24
|
+
if (!officeGroup) return;
|
|
25
|
+
scene?.remove(officeGroup);
|
|
26
|
+
officeGroup.traverse((child) => {
|
|
27
|
+
child.geometry?.dispose?.();
|
|
28
|
+
});
|
|
29
|
+
officeGroup = null;
|
|
30
|
+
DESK_POSITIONS.length = 0;
|
|
31
|
+
POND_POSITIONS.length = 0;
|
|
32
|
+
DESK_INDICATORS.clear();
|
|
33
|
+
disposeMaterialCache();
|
|
34
|
+
}
|
|
15
35
|
|
|
16
36
|
function initMaterials(theme) {
|
|
17
37
|
const dk = theme === 'dark';
|
|
@@ -41,9 +61,10 @@ function initMaterials(theme) {
|
|
|
41
61
|
/* ═══════════════════════════════════════════════════════════════════════════════
|
|
42
62
|
createOffice — Build the full 3D office environment
|
|
43
63
|
═══════════════════════════════════════════════════════════════════════════════ */
|
|
44
|
-
export async function createOffice(theme) {
|
|
45
|
-
currentTheme = theme;
|
|
46
|
-
|
|
64
|
+
export async function createOffice(theme) {
|
|
65
|
+
currentTheme = theme;
|
|
66
|
+
disposeOfficeGroup(getScene());
|
|
67
|
+
initMaterials(theme);
|
|
47
68
|
|
|
48
69
|
const scene = getScene();
|
|
49
70
|
officeGroup = new THREE.Group();
|
|
@@ -52,6 +73,7 @@ export async function createOffice(theme) {
|
|
|
52
73
|
buildFloor();
|
|
53
74
|
buildWalls();
|
|
54
75
|
buildGlassPartitions();
|
|
76
|
+
buildPondZone();
|
|
55
77
|
buildDesks(6);
|
|
56
78
|
buildServerRack();
|
|
57
79
|
buildCoffeeMachine();
|
|
@@ -164,8 +186,70 @@ function buildGlassPartitions() {
|
|
|
164
186
|
Agent Desks — Each agent gets a desk with monitor and chair
|
|
165
187
|
═══════════════════════════════════════════════════════════════════════════════ */
|
|
166
188
|
export const DESK_POSITIONS = [];
|
|
189
|
+
export const POND_POSITIONS = [];
|
|
190
|
+
const DESK_INDICATORS = new Map();
|
|
191
|
+
|
|
192
|
+
function buildPondZone() {
|
|
193
|
+
const pond = new THREE.Group();
|
|
194
|
+
pond.name = 'agent-pond';
|
|
195
|
+
|
|
196
|
+
const centerX = -9;
|
|
197
|
+
const centerZ = 6.2;
|
|
198
|
+
|
|
199
|
+
const rim = new THREE.Mesh(
|
|
200
|
+
new THREE.CylinderGeometry(3.4, 3.5, 0.16, 24),
|
|
201
|
+
MAT.frame,
|
|
202
|
+
);
|
|
203
|
+
rim.position.set(centerX, 0.25, centerZ);
|
|
204
|
+
rim.receiveShadow = true;
|
|
205
|
+
pond.add(rim);
|
|
206
|
+
|
|
207
|
+
const water = new THREE.Mesh(
|
|
208
|
+
new THREE.CylinderGeometry(3.1, 3.2, 0.1, 24),
|
|
209
|
+
new THREE.MeshStandardMaterial({
|
|
210
|
+
color: currentTheme === 'dark' ? 0x0f3a5d : 0x7dd3fc,
|
|
211
|
+
roughness: 0.18,
|
|
212
|
+
metalness: 0.2,
|
|
213
|
+
transparent: true,
|
|
214
|
+
opacity: currentTheme === 'dark' ? 0.72 : 0.52,
|
|
215
|
+
emissive: currentTheme === 'dark' ? 0x0ea5e9 : 0x0369a1,
|
|
216
|
+
emissiveIntensity: currentTheme === 'dark' ? 0.16 : 0.06,
|
|
217
|
+
}),
|
|
218
|
+
);
|
|
219
|
+
water.position.set(centerX, 0.3, centerZ);
|
|
220
|
+
pond.add(water);
|
|
221
|
+
|
|
222
|
+
// Pixel-style stepping stones around the pond edge.
|
|
223
|
+
for (let i = 0; i < 10; i++) {
|
|
224
|
+
const a = (i / 10) * Math.PI * 2;
|
|
225
|
+
const r = 3.7 + (i % 2) * 0.25;
|
|
226
|
+
const stone = new THREE.Mesh(
|
|
227
|
+
new THREE.BoxGeometry(0.32, 0.06, 0.32),
|
|
228
|
+
MAT.deskTop,
|
|
229
|
+
);
|
|
230
|
+
stone.position.set(centerX + Math.cos(a) * r, 0.23, centerZ + Math.sin(a) * r);
|
|
231
|
+
stone.rotation.y = a * 0.7;
|
|
232
|
+
stone.receiveShadow = true;
|
|
233
|
+
pond.add(stone);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
POND_POSITIONS.length = 0;
|
|
237
|
+
const count = 24;
|
|
238
|
+
for (let i = 0; i < count; i++) {
|
|
239
|
+
const a = (i / count) * Math.PI * 2;
|
|
240
|
+
const r = 1.55 + (i % 3) * 0.34;
|
|
241
|
+
POND_POSITIONS.push({
|
|
242
|
+
x: centerX + Math.cos(a) * r,
|
|
243
|
+
z: centerZ + Math.sin(a) * r,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
officeGroup.add(pond);
|
|
248
|
+
}
|
|
167
249
|
|
|
168
250
|
function buildDesks(count) {
|
|
251
|
+
DESK_POSITIONS.length = 0;
|
|
252
|
+
DESK_INDICATORS.clear();
|
|
169
253
|
const rows = 2;
|
|
170
254
|
const cols = Math.ceil(count / rows);
|
|
171
255
|
const xStart = -2;
|
|
@@ -304,10 +388,42 @@ function buildSingleDesk(x, z, idx) {
|
|
|
304
388
|
desk.add(mug);
|
|
305
389
|
}
|
|
306
390
|
|
|
391
|
+
// Occupancy beacon near each desk (idle=dim, occupied=bright).
|
|
392
|
+
const indicator = new THREE.Mesh(
|
|
393
|
+
new THREE.CylinderGeometry(0.06, 0.06, 0.04, 12),
|
|
394
|
+
new THREE.MeshStandardMaterial({
|
|
395
|
+
color: currentTheme === 'dark' ? 0x475569 : 0x94a3b8,
|
|
396
|
+
roughness: 0.25,
|
|
397
|
+
metalness: 0.4,
|
|
398
|
+
emissive: currentTheme === 'dark' ? 0x0f172a : 0x64748b,
|
|
399
|
+
emissiveIntensity: 0.12,
|
|
400
|
+
}),
|
|
401
|
+
);
|
|
402
|
+
indicator.position.set(1.0, 1.08, -0.32);
|
|
403
|
+
desk.add(indicator);
|
|
404
|
+
DESK_INDICATORS.set(idx, indicator);
|
|
405
|
+
|
|
307
406
|
desk.position.set(x, 0.2, z);
|
|
308
407
|
officeGroup.add(desk);
|
|
309
408
|
}
|
|
310
409
|
|
|
410
|
+
export function setDeskOccupied(index, occupied) {
|
|
411
|
+
const indicator = DESK_INDICATORS.get(index);
|
|
412
|
+
if (!indicator) return;
|
|
413
|
+
const mat = indicator.material;
|
|
414
|
+
if (!mat) return;
|
|
415
|
+
|
|
416
|
+
if (occupied) {
|
|
417
|
+
mat.color.setHex(currentTheme === 'dark' ? 0x34d399 : 0x059669);
|
|
418
|
+
mat.emissive.setHex(currentTheme === 'dark' ? 0x34d399 : 0x047857);
|
|
419
|
+
mat.emissiveIntensity = currentTheme === 'dark' ? 0.62 : 0.35;
|
|
420
|
+
} else {
|
|
421
|
+
mat.color.setHex(currentTheme === 'dark' ? 0x475569 : 0x94a3b8);
|
|
422
|
+
mat.emissive.setHex(currentTheme === 'dark' ? 0x0f172a : 0x64748b);
|
|
423
|
+
mat.emissiveIntensity = 0.12;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
311
427
|
/* ═══════════════════════════════════════════════════════════════════════════════
|
|
312
428
|
Server Rack
|
|
313
429
|
═══════════════════════════════════════════════════════════════════════════════ */
|
|
@@ -775,29 +891,29 @@ function buildDecorativeElements() {
|
|
|
775
891
|
/* ═══════════════════════════════════════════════════════════════════════════════
|
|
776
892
|
Floor Y position getter
|
|
777
893
|
═══════════════════════════════════════════════════════════════════════════════ */
|
|
778
|
-
export function getFloorY() {
|
|
779
|
-
return 0.2;
|
|
780
|
-
}
|
|
894
|
+
export function getFloorY() {
|
|
895
|
+
return 0.2;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
export function disposeOffice() {
|
|
899
|
+
disposeOfficeGroup(getScene());
|
|
900
|
+
}
|
|
781
901
|
|
|
782
902
|
/* ═══════════════════════════════════════════════════════════════════════════════
|
|
783
903
|
Theme Update
|
|
784
904
|
═══════════════════════════════════════════════════════════════════════════════ */
|
|
785
|
-
export function updateOfficeLighting(theme) {
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
// We rebuild the office to apply new materials
|
|
793
|
-
const scene = getScene();
|
|
794
|
-
scene.remove(officeGroup);
|
|
795
|
-
officeGroup = new THREE.Group();
|
|
796
|
-
officeGroup.name = 'office';
|
|
905
|
+
export function updateOfficeLighting(theme) {
|
|
906
|
+
currentTheme = theme;
|
|
907
|
+
const scene = getScene();
|
|
908
|
+
disposeOfficeGroup(scene);
|
|
909
|
+
initMaterials(theme);
|
|
910
|
+
officeGroup = new THREE.Group();
|
|
911
|
+
officeGroup.name = 'office';
|
|
797
912
|
|
|
798
913
|
buildFloor();
|
|
799
914
|
buildWalls();
|
|
800
915
|
buildGlassPartitions();
|
|
916
|
+
buildPondZone();
|
|
801
917
|
buildDesks(6);
|
|
802
918
|
buildServerRack();
|
|
803
919
|
buildCoffeeMachine();
|
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
═══════════════════════════════════════════════════════════════════════════════ */
|
|
5
5
|
|
|
6
6
|
const STATUS_LABEL = {
|
|
7
|
-
idle: '空闲', scanning: '扫描中', navigating: '导航中',
|
|
7
|
+
idle: '空闲', working: '工作中', scanning: '扫描中', navigating: '导航中',
|
|
8
8
|
interacting: '交互中', asserting: '断言中', reporting: '报告中',
|
|
9
|
-
thinking: '思考中', complete: '完成', error: '错误',
|
|
9
|
+
thinking: '思考中', complete: '完成', done: '完成', error: '错误',
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
const STATUS_DOT_CLASS = {
|
|
13
|
-
idle: 'dot-idle', scanning: 'dot-active', navigating: 'dot-active',
|
|
13
|
+
idle: 'dot-idle', working: 'dot-active', scanning: 'dot-active', navigating: 'dot-active',
|
|
14
14
|
interacting: 'dot-active', asserting: 'dot-active', reporting: 'dot-active',
|
|
15
|
-
thinking: 'dot-active', complete: 'dot-ok', error: 'dot-err',
|
|
15
|
+
thinking: 'dot-active', complete: 'dot-ok', done: 'dot-ok', error: 'dot-err',
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
/* ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -27,6 +27,7 @@ export class UIManager {
|
|
|
27
27
|
this._logCount = 0;
|
|
28
28
|
this._esc = options?.esc || (s => String(s));
|
|
29
29
|
this._ROLE_ICONS = options?.ROLE_ICONS || {};
|
|
30
|
+
this._resolveIcon = options?.resolveRoleIcon || (name => this._ROLE_ICONS[name] || '🐊');
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
/* ── init: store callbacks (event binding done in index.html) ────────── */
|
|
@@ -135,11 +136,13 @@ export class UIManager {
|
|
|
135
136
|
entries.forEach(agent => {
|
|
136
137
|
const name = agent.name || agent.role || '';
|
|
137
138
|
const status = agent.status || 'idle';
|
|
139
|
+
const role = agent.role || name;
|
|
138
140
|
const item = document.createElement('div');
|
|
139
141
|
item.className = 'agent-sidebar-item';
|
|
142
|
+
const category = agent.category ? ` <span style="font-size:9px;color:var(--text-subtle);opacity:0.7">${this._esc(agent.category)}</span>` : '';
|
|
140
143
|
item.innerHTML = `
|
|
141
|
-
<span class="agent-icon">${this.
|
|
142
|
-
<span class="agent-name">${this._esc(name)}</span>
|
|
144
|
+
<span class="agent-icon">${this._resolveIcon(role)}</span>
|
|
145
|
+
<span class="agent-name">${this._esc(name)}${category}</span>
|
|
143
146
|
<span class="agent-status-dot ${STATUS_DOT_CLASS[status] || 'dot-idle'}"></span>
|
|
144
147
|
<span class="agent-status-text">${STATUS_LABEL[status] || status}</span>
|
|
145
148
|
`;
|
|
@@ -179,8 +182,9 @@ export class UIManager {
|
|
|
179
182
|
_renderDeskCard(name, info) {
|
|
180
183
|
const status = info.status || 'idle';
|
|
181
184
|
const progress = info.progress || 0;
|
|
185
|
+
const role = info.role || name;
|
|
182
186
|
return `
|
|
183
|
-
<div class="dc-icon">${this.
|
|
187
|
+
<div class="dc-icon">${this._resolveIcon(role)}</div>
|
|
184
188
|
<div class="dc-name">${this._esc(name)}</div>
|
|
185
189
|
<div class="dc-status ${STATUS_DOT_CLASS[status] || ''}">${STATUS_LABEL[status] || status}</div>
|
|
186
190
|
<div class="dc-bar"><div class="dc-fill" style="width:${progress}%"></div></div>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const ROUTE_CHANGE_EVENT = 'opencroc:route-change';
|
|
2
|
+
|
|
3
|
+
const LEGACY_ROUTE_ALIASES: Record<string, string> = {
|
|
4
|
+
'/index.html': '/',
|
|
5
|
+
'/index-studio.html': '/studio',
|
|
6
|
+
'/index-v2-pixel.html': '/pixel',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function normalizeAppPath(pathname: string): string {
|
|
10
|
+
if (!pathname) {
|
|
11
|
+
return '/';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const [pathOnly] = pathname.split('?');
|
|
15
|
+
const withLeadingSlash = pathOnly.startsWith('/') ? pathOnly : `/${pathOnly}`;
|
|
16
|
+
const trimmed = withLeadingSlash.length > 1 && withLeadingSlash.endsWith('/')
|
|
17
|
+
? withLeadingSlash.slice(0, -1)
|
|
18
|
+
: withLeadingSlash;
|
|
19
|
+
|
|
20
|
+
return LEGACY_ROUTE_ALIASES[trimmed] || trimmed;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getCurrentAppPath(): string {
|
|
24
|
+
return normalizeAppPath(window.location.pathname);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function navigate(to: string, options?: { replace?: boolean }): void {
|
|
28
|
+
const nextPath = normalizeAppPath(to);
|
|
29
|
+
const currentPath = getCurrentAppPath();
|
|
30
|
+
|
|
31
|
+
if (nextPath !== currentPath) {
|
|
32
|
+
const method = options?.replace ? 'replaceState' : 'pushState';
|
|
33
|
+
window.history[method]({}, '', nextPath);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
window.dispatchEvent(new Event(ROUTE_CHANGE_EVENT));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function subscribeNavigation(callback: () => void): () => void {
|
|
40
|
+
window.addEventListener('popstate', callback);
|
|
41
|
+
window.addEventListener(ROUTE_CHANGE_EVENT, callback);
|
|
42
|
+
|
|
43
|
+
return () => {
|
|
44
|
+
window.removeEventListener('popstate', callback);
|
|
45
|
+
window.removeEventListener(ROUTE_CHANGE_EVENT, callback);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
html,
|
|
2
|
+
body,
|
|
3
|
+
#root,
|
|
4
|
+
.app-layout {
|
|
5
|
+
width: 100%;
|
|
6
|
+
height: 100%;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
body[data-route-variant='office'] {
|
|
10
|
+
background: #000;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
body[data-route-variant='graph'] {
|
|
14
|
+
background: #08101d;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
body[data-route-variant='pixel'] {
|
|
18
|
+
background: #0a1220;
|
|
19
|
+
}
|