@nice2dev/ui-3d 1.3.15 → 1.3.17
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 +13 -0
- package/dist/cjs/collaborative/collaborativeScene.js +160 -206
- package/dist/cjs/core/editorAsk.js +51 -41
- package/dist/cjs/core/i18n.js +13 -14
- package/dist/cjs/core/threeCompat.js +29 -42
- package/dist/cjs/dance/DanceBridge.js +148 -177
- package/dist/cjs/dance/DanceScoreEngine.js +209 -221
- package/dist/cjs/dance/PoseDetector.js +302 -275
- package/dist/cjs/index.js +110 -113
- package/dist/cjs/material/MaterialEditor.module.css.js +242 -5
- package/dist/cjs/material/NiceMaterialEditor.js +1213 -757
- package/dist/cjs/material/materialEditorTypes.js +35 -49
- package/dist/cjs/material/materialEditorUtils.js +684 -747
- package/dist/cjs/material/materialNodeDefinitions.js +1411 -1459
- package/dist/cjs/model/ModelEditor.js +90 -40
- package/dist/cjs/model/ModelEditor.module.css.js +257 -5
- package/dist/cjs/model/ModelEditorLeftPanel.js +126 -47
- package/dist/cjs/model/ModelEditorMenuBar.js +359 -32
- package/dist/cjs/model/ModelEditorRightPanel.js +647 -131
- package/dist/cjs/model/ModelEditorSubComponents.js +277 -112
- package/dist/cjs/model/ModelEditorTimeline.js +232 -34
- package/dist/cjs/model/ModelEditorToolbar.js +170 -14
- package/dist/cjs/model/ModelEditorViewport.js +231 -39
- package/dist/cjs/model/ModelViewer.js +388 -63
- package/dist/cjs/model/ModelViewer.module.css.js +77 -5
- package/dist/cjs/model/NiceArmatureEditor.js +473 -274
- package/dist/cjs/model/NiceMorphTargetEditor.js +333 -227
- package/dist/cjs/model/NiceOctree.js +372 -320
- package/dist/cjs/model/NicePhysicsSimulation.js +528 -298
- package/dist/cjs/model/NiceProceduralGeometry.js +673 -300
- package/dist/cjs/model/NiceTerrainEditor.js +595 -385
- package/dist/cjs/model/NiceWeightPainter.js +384 -261
- package/dist/cjs/model/NiceXRPreview.js +385 -308
- package/dist/cjs/model/cadModeUtils.js +103 -106
- package/dist/cjs/model/editorShortcuts.js +171 -194
- package/dist/cjs/model/modelEditorTypes.js +100 -118
- package/dist/cjs/model/modelEditorUtils.js +1061 -1146
- package/dist/cjs/model/simsModeUtils.js +1023 -1053
- package/dist/cjs/model/useModelEditor.js +1977 -1933
- package/dist/cjs/model/useModelViewer.js +633 -665
- package/dist/cjs/nice2dev-ui-3d.css +1 -1
- package/dist/cjs/particle/NiceParticleEditor.js +1186 -571
- package/dist/cjs/particle/ParticleEditor.module.css.js +122 -5
- package/dist/cjs/particle/particleEditorTypes.js +62 -70
- package/dist/cjs/particle/particleEditorUtils.js +998 -1013
- package/dist/cjs/rendering/NiceCascadedShadows.js +381 -259
- package/dist/cjs/rendering/NiceRenderExport.js +424 -337
- package/dist/cjs/rendering/NiceSSAO.js +454 -337
- package/dist/cjs/rendering/NiceSSR.js +387 -261
- package/dist/cjs/rendering/NiceWebGPURenderer.js +359 -230
- package/dist/cjs/uv/NiceUVEditor.js +787 -550
- package/dist/cjs/uv/UVEditor.module.css.js +53 -5
- package/dist/cjs/uv/uvEditorTypes.js +59 -76
- package/dist/cjs/uv/uvEditorUtils.js +501 -575
- package/dist/esm/collaborative/collaborativeScene.js +163 -205
- package/dist/esm/core/editorAsk.js +53 -40
- package/dist/esm/core/i18n.js +15 -13
- package/dist/esm/core/threeCompat.js +18 -26
- package/dist/esm/dance/DanceBridge.js +151 -176
- package/dist/esm/dance/DanceScoreEngine.js +211 -220
- package/dist/esm/dance/PoseDetector.js +304 -274
- package/dist/esm/index.js +258 -36
- package/dist/esm/material/MaterialEditor.module.css.js +242 -3
- package/dist/esm/material/NiceMaterialEditor.js +1215 -756
- package/dist/esm/material/materialEditorTypes.js +45 -48
- package/dist/esm/material/materialEditorUtils.js +713 -746
- package/dist/esm/material/materialNodeDefinitions.js +1415 -1458
- package/dist/esm/model/ModelEditor.js +91 -39
- package/dist/esm/model/ModelEditor.module.css.js +257 -3
- package/dist/esm/model/ModelEditorLeftPanel.js +126 -45
- package/dist/esm/model/ModelEditorMenuBar.js +359 -30
- package/dist/esm/model/ModelEditorRightPanel.js +634 -114
- package/dist/esm/model/ModelEditorSubComponents.js +265 -94
- package/dist/esm/model/ModelEditorTimeline.js +232 -32
- package/dist/esm/model/ModelEditorToolbar.js +170 -12
- package/dist/esm/model/ModelEditorViewport.js +231 -37
- package/dist/esm/model/ModelViewer.js +389 -62
- package/dist/esm/model/ModelViewer.module.css.js +77 -3
- package/dist/esm/model/NiceArmatureEditor.js +462 -258
- package/dist/esm/model/NiceMorphTargetEditor.js +322 -211
- package/dist/esm/model/NiceOctree.js +361 -304
- package/dist/esm/model/NicePhysicsSimulation.js +517 -282
- package/dist/esm/model/NiceProceduralGeometry.js +667 -284
- package/dist/esm/model/NiceTerrainEditor.js +584 -369
- package/dist/esm/model/NiceWeightPainter.js +373 -245
- package/dist/esm/model/NiceXRPreview.js +374 -292
- package/dist/esm/model/cadModeUtils.js +95 -88
- package/dist/esm/model/editorShortcuts.js +172 -193
- package/dist/esm/model/modelEditorTypes.js +90 -100
- package/dist/esm/model/modelEditorUtils.js +1056 -1128
- package/dist/esm/model/simsModeUtils.js +1021 -1035
- package/dist/esm/model/useModelEditor.js +1946 -1920
- package/dist/esm/model/useModelViewer.js +619 -647
- package/dist/esm/nice2dev-ui-3d.css +1 -1
- package/dist/esm/particle/NiceParticleEditor.js +1188 -570
- package/dist/esm/particle/ParticleEditor.module.css.js +122 -3
- package/dist/esm/particle/particleEditorTypes.js +69 -69
- package/dist/esm/particle/particleEditorUtils.js +1027 -1012
- package/dist/esm/rendering/NiceCascadedShadows.js +370 -243
- package/dist/esm/rendering/NiceRenderExport.js +413 -321
- package/dist/esm/rendering/NiceSSAO.js +450 -328
- package/dist/esm/rendering/NiceSSR.js +383 -252
- package/dist/esm/rendering/NiceWebGPURenderer.js +326 -214
- package/dist/esm/uv/NiceUVEditor.js +788 -549
- package/dist/esm/uv/UVEditor.module.css.js +53 -3
- package/dist/esm/uv/uvEditorTypes.js +68 -75
- package/dist/esm/uv/uvEditorUtils.js +515 -557
- package/package.json +7 -12
|
@@ -1,102 +1,109 @@
|
|
|
1
|
-
import * as THREE from
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* cadModeUtils.ts — CAD-style design utilities for ModelEditor.
|
|
5
|
-
*
|
|
6
|
-
* Provides: wall/floor/room drawing, dimension annotations,
|
|
7
|
-
* snap-to-grid, measurement overlay, DXF import/export,
|
|
8
|
-
* floor plan tools — inspired by AutoCAD & ArchiCAD.
|
|
9
|
-
*/
|
|
10
|
-
/* ════════════════════════════════════════════════════════════════
|
|
11
|
-
CONSTANTS
|
|
12
|
-
════════════════════════════════════════════════════════════════ */
|
|
1
|
+
import * as THREE from "three";
|
|
13
2
|
const DEFAULT_CAD_SETTINGS = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
3
|
+
grid: {
|
|
4
|
+
enabled: true,
|
|
5
|
+
spacing: 1,
|
|
6
|
+
subdivisions: 10,
|
|
7
|
+
unit: "m",
|
|
8
|
+
showLabels: true,
|
|
9
|
+
color: "#444466",
|
|
10
|
+
subColor: "#333344"
|
|
11
|
+
},
|
|
12
|
+
snapModes: ["grid", "endpoint", "midpoint", "intersection"],
|
|
13
|
+
snapRadius: 0.15,
|
|
14
|
+
defaultWallHeight: 2.8,
|
|
15
|
+
defaultWallThickness: 0.2,
|
|
16
|
+
defaultDoorWidth: 0.9,
|
|
17
|
+
defaultDoorHeight: 2.1,
|
|
18
|
+
defaultWindowWidth: 1.2,
|
|
19
|
+
defaultWindowHeight: 1.4,
|
|
20
|
+
defaultWindowSill: 0.9,
|
|
21
|
+
unit: "m",
|
|
22
|
+
precision: 2,
|
|
23
|
+
orthogonalLock: false,
|
|
24
|
+
showDimensions: true,
|
|
25
|
+
currentFloor: 0
|
|
37
26
|
};
|
|
38
27
|
const UNIT_SCALES = {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
28
|
+
mm: 1e-3,
|
|
29
|
+
cm: 0.01,
|
|
30
|
+
m: 1,
|
|
31
|
+
in: 0.0254,
|
|
32
|
+
ft: 0.3048
|
|
44
33
|
};
|
|
45
34
|
const ROOM_COLORS = {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
35
|
+
living: "#e8d5b0",
|
|
36
|
+
bedroom: "#c5d8e8",
|
|
37
|
+
bathroom: "#b0dde8",
|
|
38
|
+
kitchen: "#e8d0b5",
|
|
39
|
+
dining: "#d8c8a8",
|
|
40
|
+
office: "#c8d5c0",
|
|
41
|
+
garage: "#c0c0c0",
|
|
42
|
+
hallway: "#d5d0c8",
|
|
43
|
+
closet: "#d8cbe8",
|
|
44
|
+
laundry: "#b8d8d0",
|
|
45
|
+
balcony: "#c0e0c0",
|
|
46
|
+
stairwell: "#d0c8b8",
|
|
47
|
+
custom: "#d0d0d0"
|
|
59
48
|
};
|
|
60
49
|
const WALL_MATERIALS = [
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
50
|
+
{ id: "concrete", name: "Concrete", color: "#b8b8b8" },
|
|
51
|
+
{ id: "brick", name: "Brick", color: "#c07050" },
|
|
52
|
+
{ id: "drywall", name: "Drywall", color: "#f0e8d8" },
|
|
53
|
+
{ id: "glass", name: "Glass", color: "#c8e0f0" },
|
|
54
|
+
{ id: "wood", name: "Wood Panel", color: "#c8a060" },
|
|
55
|
+
{ id: "stone", name: "Stone", color: "#a0a098" },
|
|
56
|
+
{ id: "metal", name: "Metal Panel", color: "#b0b0b8" }
|
|
68
57
|
];
|
|
69
58
|
const FLOOR_MATERIALS = [
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
59
|
+
{ id: "hardwood", name: "Hardwood", color: "#c09050" },
|
|
60
|
+
{ id: "tile-white", name: "White Tile", color: "#f0f0f0" },
|
|
61
|
+
{ id: "tile-grey", name: "Grey Tile", color: "#b0b0b0" },
|
|
62
|
+
{ id: "carpet", name: "Carpet", color: "#908070" },
|
|
63
|
+
{ id: "marble", name: "Marble", color: "#e8e0d8" },
|
|
64
|
+
{ id: "concrete", name: "Polished Concrete", color: "#c0c0c0" },
|
|
65
|
+
{ id: "laminate", name: "Laminate", color: "#d0b080" },
|
|
66
|
+
{ id: "vinyl", name: "Vinyl", color: "#d0d0c8" }
|
|
78
67
|
];
|
|
79
|
-
/* ════════════════════════════════════════════════════════════════
|
|
80
|
-
GRID & SNAP
|
|
81
|
-
════════════════════════════════════════════════════════════════ */
|
|
82
68
|
function createCADGrid(settings) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
69
|
+
const group = new THREE.Group();
|
|
70
|
+
group.name = "__cadGrid";
|
|
71
|
+
const size = settings.spacing * 50;
|
|
72
|
+
const mainGrid = new THREE.GridHelper(
|
|
73
|
+
size * 2,
|
|
74
|
+
Math.floor(size * 2 / settings.spacing),
|
|
75
|
+
settings.color,
|
|
76
|
+
settings.subColor
|
|
77
|
+
);
|
|
78
|
+
mainGrid.position.y = 1e-3;
|
|
79
|
+
group.add(mainGrid);
|
|
80
|
+
if (settings.subdivisions > 1) {
|
|
81
|
+
const subSize = settings.spacing / settings.subdivisions;
|
|
82
|
+
const subGrid = new THREE.GridHelper(
|
|
83
|
+
size * 2,
|
|
84
|
+
Math.floor(size * 2 / subSize),
|
|
85
|
+
settings.subColor,
|
|
86
|
+
`${settings.subColor}44`
|
|
87
|
+
);
|
|
88
|
+
subGrid.position.y = 0;
|
|
89
|
+
group.add(subGrid);
|
|
90
|
+
}
|
|
91
|
+
return group;
|
|
96
92
|
}
|
|
97
93
|
function snapToCADGrid(point, settings) {
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
const snap = settings.grid.spacing / settings.grid.subdivisions;
|
|
95
|
+
return new THREE.Vector3(
|
|
96
|
+
Math.round(point.x / snap) * snap,
|
|
97
|
+
Math.round(point.y / snap) * snap,
|
|
98
|
+
Math.round(point.z / snap) * snap
|
|
99
|
+
);
|
|
100
100
|
}
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
export {
|
|
102
|
+
DEFAULT_CAD_SETTINGS,
|
|
103
|
+
FLOOR_MATERIALS,
|
|
104
|
+
ROOM_COLORS,
|
|
105
|
+
UNIT_SCALES,
|
|
106
|
+
WALL_MATERIALS,
|
|
107
|
+
createCADGrid,
|
|
108
|
+
snapToCADGrid
|
|
109
|
+
};
|
|
@@ -1,218 +1,197 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* editorShortcuts.ts — Customizable keyboard shortcuts for the 3D ModelEditor.
|
|
3
|
-
*
|
|
4
|
-
* Features:
|
|
5
|
-
* - Pre-configured default shortcuts matching current ModelEditor
|
|
6
|
-
* - Rebindable keys with persistence support
|
|
7
|
-
* - Shortcut categories for organized display
|
|
8
|
-
* - Conflict detection
|
|
9
|
-
*/
|
|
10
|
-
/* ═══════════════════════════════════════════
|
|
11
|
-
Helpers
|
|
12
|
-
═══════════════════════════════════════════ */
|
|
13
1
|
function normalizeKey(key) {
|
|
14
|
-
|
|
2
|
+
return key.length === 1 ? key.toLowerCase() : key;
|
|
15
3
|
}
|
|
16
4
|
function bindingEquals(a, b) {
|
|
17
|
-
|
|
18
|
-
!!a.ctrl === !!b.ctrl &&
|
|
19
|
-
!!a.shift === !!b.shift &&
|
|
20
|
-
!!a.alt === !!b.alt &&
|
|
21
|
-
!!a.meta === !!b.meta);
|
|
5
|
+
return normalizeKey(a.key) === normalizeKey(b.key) && !!a.ctrl === !!b.ctrl && !!a.shift === !!b.shift && !!a.alt === !!b.alt && !!a.meta === !!b.meta;
|
|
22
6
|
}
|
|
23
7
|
function eventToBinding(e) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
8
|
+
return {
|
|
9
|
+
key: e.key,
|
|
10
|
+
ctrl: e.ctrlKey || e.metaKey,
|
|
11
|
+
shift: e.shiftKey,
|
|
12
|
+
alt: e.altKey
|
|
13
|
+
};
|
|
30
14
|
}
|
|
31
|
-
/* ═══════════════════════════════════════════
|
|
32
|
-
Default Shortcuts
|
|
33
|
-
═══════════════════════════════════════════ */
|
|
34
15
|
const DEFAULT_SHORTCUTS = [
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
16
|
+
// Transform
|
|
17
|
+
{
|
|
18
|
+
id: "transform.translate",
|
|
19
|
+
label: "Move (Translate)",
|
|
20
|
+
category: "Transform",
|
|
21
|
+
binding: { key: "g" }
|
|
22
|
+
},
|
|
23
|
+
{ id: "transform.rotate", label: "Rotate", category: "Transform", binding: { key: "r" } },
|
|
24
|
+
{ id: "transform.scale", label: "Scale", category: "Transform", binding: { key: "s" } },
|
|
25
|
+
{
|
|
26
|
+
id: "transform.delete",
|
|
27
|
+
label: "Delete Selected",
|
|
28
|
+
category: "Transform",
|
|
29
|
+
binding: { key: "Delete" }
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: "transform.deleteAlt",
|
|
33
|
+
label: "Delete (Ctrl+X)",
|
|
34
|
+
category: "Transform",
|
|
35
|
+
binding: { key: "x", ctrl: true }
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "transform.duplicate",
|
|
39
|
+
label: "Duplicate",
|
|
40
|
+
category: "Transform",
|
|
41
|
+
binding: { key: "d", shift: true }
|
|
42
|
+
},
|
|
43
|
+
// Camera
|
|
44
|
+
{ id: "camera.focus", label: "Focus on Object", category: "Camera", binding: { key: "f" } },
|
|
45
|
+
{ id: "camera.front", label: "Front View", category: "Camera", binding: { key: "1" } },
|
|
46
|
+
{ id: "camera.right", label: "Right View", category: "Camera", binding: { key: "3" } },
|
|
47
|
+
{ id: "camera.top", label: "Top View", category: "Camera", binding: { key: "7" } },
|
|
48
|
+
{ id: "camera.left", label: "Left View", category: "Camera", binding: { key: "4" } },
|
|
49
|
+
{ id: "camera.back", label: "Back View", category: "Camera", binding: { key: "9" } },
|
|
50
|
+
{ id: "camera.bottom", label: "Bottom View", category: "Camera", binding: { key: "2" } },
|
|
51
|
+
// Animation
|
|
52
|
+
{ id: "anim.playPause", label: "Play / Pause", category: "Animation", binding: { key: " " } },
|
|
53
|
+
// Edit
|
|
54
|
+
{ id: "edit.undo", label: "Undo", category: "Edit", binding: { key: "z", ctrl: true } },
|
|
55
|
+
{ id: "edit.redo", label: "Redo", category: "Edit", binding: { key: "y", ctrl: true } },
|
|
56
|
+
{
|
|
57
|
+
id: "edit.redoAlt",
|
|
58
|
+
label: "Redo (Ctrl+Shift+Z)",
|
|
59
|
+
category: "Edit",
|
|
60
|
+
binding: { key: "z", ctrl: true, shift: true }
|
|
61
|
+
},
|
|
62
|
+
// Mode
|
|
63
|
+
{ id: "mode.cycle", label: "Cycle Editor Mode", category: "Mode", binding: { key: "Tab" } },
|
|
64
|
+
{ id: "mode.escape", label: "Close/Cancel", category: "Mode", binding: { key: "Escape" } }
|
|
84
65
|
];
|
|
85
|
-
/* ═══════════════════════════════════════════
|
|
86
|
-
EditorShortcutManager
|
|
87
|
-
═══════════════════════════════════════════ */
|
|
88
66
|
class EditorShortcutManager {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
67
|
+
constructor() {
|
|
68
|
+
this.shortcuts = /* @__PURE__ */ new Map();
|
|
69
|
+
this.callbacks = /* @__PURE__ */ new Map();
|
|
70
|
+
for (const def of DEFAULT_SHORTCUTS) {
|
|
71
|
+
this.shortcuts.set(def.id, { ...def, enabled: true });
|
|
95
72
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
73
|
+
}
|
|
74
|
+
/** Register a callback for a shortcut ID. */
|
|
75
|
+
on(id, callback) {
|
|
76
|
+
this.callbacks.set(id, callback);
|
|
77
|
+
}
|
|
78
|
+
/** Unregister a callback. */
|
|
79
|
+
off(id) {
|
|
80
|
+
this.callbacks.delete(id);
|
|
81
|
+
}
|
|
82
|
+
/** Rebind a shortcut to a new key combination. */
|
|
83
|
+
rebind(id, binding) {
|
|
84
|
+
const shortcut = this.shortcuts.get(id);
|
|
85
|
+
if (shortcut) {
|
|
86
|
+
shortcut.binding = binding;
|
|
99
87
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
88
|
+
}
|
|
89
|
+
/** Enable/disable a shortcut. */
|
|
90
|
+
setEnabled(id, enabled) {
|
|
91
|
+
const shortcut = this.shortcuts.get(id);
|
|
92
|
+
if (shortcut) {
|
|
93
|
+
shortcut.enabled = enabled;
|
|
103
94
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
95
|
+
}
|
|
96
|
+
/** Reset all bindings to defaults. */
|
|
97
|
+
resetAll() {
|
|
98
|
+
this.shortcuts.clear();
|
|
99
|
+
for (const def of DEFAULT_SHORTCUTS) {
|
|
100
|
+
this.shortcuts.set(def.id, { ...def, enabled: true });
|
|
110
101
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
102
|
+
}
|
|
103
|
+
/** Get all shortcuts grouped by category. */
|
|
104
|
+
getGrouped() {
|
|
105
|
+
const groups = {};
|
|
106
|
+
for (const s of this.shortcuts.values()) {
|
|
107
|
+
if (!groups[s.category]) {
|
|
108
|
+
groups[s.category] = [];
|
|
109
|
+
}
|
|
110
|
+
groups[s.category].push(s);
|
|
117
111
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
112
|
+
return groups;
|
|
113
|
+
}
|
|
114
|
+
/** Get all shortcuts as a flat array. */
|
|
115
|
+
getAll() {
|
|
116
|
+
return [...this.shortcuts.values()];
|
|
117
|
+
}
|
|
118
|
+
/** Find conflicts (two shortcuts with the same binding). */
|
|
119
|
+
findConflicts() {
|
|
120
|
+
const conflicts = [];
|
|
121
|
+
const entries = [...this.shortcuts.values()];
|
|
122
|
+
for (let i = 0; i < entries.length; i++) {
|
|
123
|
+
for (let j = i + 1; j < entries.length; j++) {
|
|
124
|
+
if (bindingEquals(entries[i].binding, entries[j].binding)) {
|
|
125
|
+
conflicts.push([entries[i], entries[j]]);
|
|
123
126
|
}
|
|
127
|
+
}
|
|
124
128
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
129
|
+
return conflicts;
|
|
130
|
+
}
|
|
131
|
+
/** Export bindings for persistence. */
|
|
132
|
+
exportConfig() {
|
|
133
|
+
return [...this.shortcuts.values()].map((s) => ({
|
|
134
|
+
id: s.id,
|
|
135
|
+
binding: s.binding
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
/** Import bindings from saved config. */
|
|
139
|
+
importConfig(configs) {
|
|
140
|
+
for (const config of configs) {
|
|
141
|
+
this.rebind(config.id, config.binding);
|
|
135
142
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
143
|
+
}
|
|
144
|
+
/** Match a keyboard event to a shortcut and return its ID (or null). */
|
|
145
|
+
match(e) {
|
|
146
|
+
const evtBinding = eventToBinding(e);
|
|
147
|
+
for (const [id, shortcut] of this.shortcuts) {
|
|
148
|
+
if (!shortcut.enabled) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (bindingEquals(evtBinding, shortcut.binding)) {
|
|
152
|
+
return id;
|
|
153
|
+
}
|
|
139
154
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
conflicts.push([entries[i], entries[j]]);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return conflicts;
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
/** Handle a keyboard event — fires callback if matched. Returns true if handled. */
|
|
158
|
+
handleKeyDown(e) {
|
|
159
|
+
const id = this.match(e);
|
|
160
|
+
if (!id) {
|
|
161
|
+
return false;
|
|
152
162
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}));
|
|
163
|
+
const cb = this.callbacks.get(id);
|
|
164
|
+
if (cb) {
|
|
165
|
+
e.preventDefault();
|
|
166
|
+
cb();
|
|
167
|
+
return true;
|
|
159
168
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
/** Format a binding for UI display. */
|
|
172
|
+
formatBinding(id) {
|
|
173
|
+
const shortcut = this.shortcuts.get(id);
|
|
174
|
+
if (!shortcut) {
|
|
175
|
+
return "";
|
|
165
176
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (!shortcut.enabled) {
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
if (bindingEquals(evtBinding, shortcut.binding)) {
|
|
174
|
-
return id;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return null;
|
|
177
|
+
const b = shortcut.binding;
|
|
178
|
+
const parts = [];
|
|
179
|
+
if (b.ctrl) {
|
|
180
|
+
parts.push("Ctrl");
|
|
178
181
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const id = this.match(e);
|
|
182
|
-
if (!id) {
|
|
183
|
-
return false;
|
|
184
|
-
}
|
|
185
|
-
const cb = this.callbacks.get(id);
|
|
186
|
-
if (cb) {
|
|
187
|
-
e.preventDefault();
|
|
188
|
-
cb();
|
|
189
|
-
return true;
|
|
190
|
-
}
|
|
191
|
-
return false;
|
|
182
|
+
if (b.shift) {
|
|
183
|
+
parts.push("Shift");
|
|
192
184
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
const b = shortcut.binding;
|
|
200
|
-
const parts = [];
|
|
201
|
-
if (b.ctrl) {
|
|
202
|
-
parts.push('Ctrl');
|
|
203
|
-
}
|
|
204
|
-
if (b.shift) {
|
|
205
|
-
parts.push('Shift');
|
|
206
|
-
}
|
|
207
|
-
if (b.alt) {
|
|
208
|
-
parts.push('Alt');
|
|
209
|
-
}
|
|
210
|
-
if (b.meta) {
|
|
211
|
-
parts.push('Meta');
|
|
212
|
-
}
|
|
213
|
-
parts.push(b.key === ' ' ? 'Space' : b.key.length === 1 ? b.key.toUpperCase() : b.key);
|
|
214
|
-
return parts.join('+');
|
|
185
|
+
if (b.alt) {
|
|
186
|
+
parts.push("Alt");
|
|
187
|
+
}
|
|
188
|
+
if (b.meta) {
|
|
189
|
+
parts.push("Meta");
|
|
215
190
|
}
|
|
191
|
+
parts.push(b.key === " " ? "Space" : b.key.length === 1 ? b.key.toUpperCase() : b.key);
|
|
192
|
+
return parts.join("+");
|
|
193
|
+
}
|
|
216
194
|
}
|
|
217
|
-
|
|
218
|
-
|
|
195
|
+
export {
|
|
196
|
+
EditorShortcutManager
|
|
197
|
+
};
|