@almadar/ui 6.5.0 → 6.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/{EntityBindingContext-0Evn_LcT.d.cts → EntityBindingContext-Bn3ePJQC.d.cts} +1 -1
  2. package/dist/{EntityBindingContext-0Evn_LcT.d.ts → EntityBindingContext-Bn3ePJQC.d.ts} +1 -1
  3. package/dist/{GameAudioProvider-B48iXwz3.d.ts → GameAudioProvider-Ctceau1s.d.ts} +6 -18
  4. package/dist/{GameAudioProvider-CQAdPreB.d.cts → GameAudioProvider-Dk_Y3LN3.d.cts} +6 -18
  5. package/dist/avl/index.cjs +1268 -220
  6. package/dist/avl/index.js +1269 -221
  7. package/dist/{avl-schema-parser-dvJKXn-o.d.ts → avl-schema-parser-DncJLEn9.d.ts} +18 -0
  8. package/dist/{avl-schema-parser-Bt4NzAam.d.cts → avl-schema-parser-FQ1474v8.d.cts} +18 -0
  9. package/dist/{cn-DJSBBm5W.d.cts → cn-CCjFspAo.d.cts} +25 -1
  10. package/dist/{cn-BbhJq_nr.d.ts → cn-sgpqpN0U.d.ts} +25 -1
  11. package/dist/components/index.cjs +1092 -249
  12. package/dist/components/index.d.cts +193 -23
  13. package/dist/components/index.d.ts +193 -23
  14. package/dist/components/index.js +1089 -250
  15. package/dist/context/index.cjs +10 -13
  16. package/dist/context/index.js +10 -13
  17. package/dist/hooks/index.cjs +63 -13
  18. package/dist/hooks/index.d.cts +2 -2
  19. package/dist/hooks/index.d.ts +2 -2
  20. package/dist/hooks/index.js +64 -15
  21. package/dist/lib/drawable/three/index.cjs +126 -13
  22. package/dist/lib/drawable/three/index.d.cts +2 -2
  23. package/dist/lib/drawable/three/index.d.ts +2 -2
  24. package/dist/lib/drawable/three/index.js +127 -14
  25. package/dist/lib/index.cjs +180 -0
  26. package/dist/lib/index.d.cts +1 -1
  27. package/dist/lib/index.d.ts +1 -1
  28. package/dist/lib/index.js +178 -1
  29. package/dist/locales/index.cjs +6 -3
  30. package/dist/locales/index.js +6 -3
  31. package/dist/providers/index.cjs +974 -202
  32. package/dist/providers/index.d.cts +2 -2
  33. package/dist/providers/index.d.ts +2 -2
  34. package/dist/providers/index.js +974 -202
  35. package/dist/runtime/index.cjs +1594 -196
  36. package/dist/runtime/index.d.cts +79 -6
  37. package/dist/runtime/index.d.ts +79 -6
  38. package/dist/runtime/index.js +1592 -198
  39. package/dist/slot-host-Czc2JAxo.d.cts +47 -0
  40. package/dist/slot-host-Czc2JAxo.d.ts +47 -0
  41. package/dist/{useEventBus-CQWyAWpK.d.ts → useKeyboardRouter-D84cNWmA.d.ts} +31 -1
  42. package/dist/{useEventBus-Ckr4wqW3.d.cts → useKeyboardRouter-DQEE_9mq.d.cts} +31 -1
  43. package/locales/ar.json +2 -1
  44. package/locales/en.json +2 -1
  45. package/locales/sl.json +2 -1
  46. package/package.json +4 -4
  47. package/themes/comic.css +437 -0
  48. package/themes/index.css +1 -0
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var React = require('react');
4
- var logger = require('@almadar/logger');
5
4
  var core = require('@almadar/core');
5
+ var logger = require('@almadar/logger');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var providers = require('@almadar/ui/providers');
8
8
  var context = require('@almadar/ui/context');
@@ -96,18 +96,9 @@ function reconcileSlotProps(prev, next) {
96
96
  var log = logger.createLogger("almadar:ui:ui-slots");
97
97
  var DEFAULT_SOURCE_KEY = "__default__";
98
98
  var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
99
- var ALL_SLOTS = [
100
- "main",
101
- "sidebar",
102
- "modal",
103
- "drawer",
104
- "overlay",
105
- "center",
106
- "toast",
107
- "hud-top",
108
- "hud-bottom",
109
- "floating"
110
- ];
99
+ var ALL_SLOTS = core.UI_SLOTS.filter(
100
+ (slot) => !slot.includes(".") && slot !== "hud" && slot !== "screen"
101
+ );
111
102
  var DEFAULT_SLOTS = ALL_SLOTS.reduce(
112
103
  (acc, slot) => {
113
104
  acc[slot] = null;
@@ -605,6 +596,12 @@ var BUILT_IN_THEMES = [
605
596
  displayName: "Corporate",
606
597
  hasLightMode: true,
607
598
  hasDarkMode: true
599
+ },
600
+ {
601
+ name: "comic",
602
+ displayName: "Comic Studio",
603
+ hasLightMode: true,
604
+ hasDarkMode: true
608
605
  }
609
606
  ];
610
607
  var ThemeContext = React.createContext(void 0);
@@ -1,6 +1,6 @@
1
1
  import React, { createContext, useMemo, useContext, useState, useEffect, useCallback, useRef } from 'react';
2
+ import { UI_SLOTS, isRenderBindingMarker } from '@almadar/core';
2
3
  import { createLogger } from '@almadar/logger';
3
- import { isRenderBindingMarker } from '@almadar/core';
4
4
  import { jsx } from 'react/jsx-runtime';
5
5
  export { ANONYMOUS_USER, CurrentPagePathContext, CurrentPagePathProvider, OrbitalThemeProvider, UserContext, UserProvider, useCurrentPagePath, useHasPermission, useHasRole, useUser, useUserForEvaluation } from '@almadar/ui/providers';
6
6
  import { ThemeProvider as ThemeProvider$1, useTheme as useTheme$1 } from '@almadar/ui/context';
@@ -90,18 +90,9 @@ function reconcileSlotProps(prev, next) {
90
90
  var log = createLogger("almadar:ui:ui-slots");
91
91
  var DEFAULT_SOURCE_KEY = "__default__";
92
92
  var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
93
- var ALL_SLOTS = [
94
- "main",
95
- "sidebar",
96
- "modal",
97
- "drawer",
98
- "overlay",
99
- "center",
100
- "toast",
101
- "hud-top",
102
- "hud-bottom",
103
- "floating"
104
- ];
93
+ var ALL_SLOTS = UI_SLOTS.filter(
94
+ (slot) => !slot.includes(".") && slot !== "hud" && slot !== "screen"
95
+ );
105
96
  var DEFAULT_SLOTS = ALL_SLOTS.reduce(
106
97
  (acc, slot) => {
107
98
  acc[slot] = null;
@@ -599,6 +590,12 @@ var BUILT_IN_THEMES = [
599
590
  displayName: "Corporate",
600
591
  hasLightMode: true,
601
592
  hasDarkMode: true
593
+ },
594
+ {
595
+ name: "comic",
596
+ displayName: "Comic Studio",
597
+ hasLightMode: true,
598
+ hasDarkMode: true
602
599
  }
603
600
  ];
604
601
  var ThemeContext = createContext(void 0);
@@ -1023,6 +1023,63 @@ function useEmitEvent() {
1023
1023
  [eventBus]
1024
1024
  );
1025
1025
  }
1026
+ function useKeyboardRouter(options) {
1027
+ const {
1028
+ captureTable,
1029
+ editorFocusEvent = "EDITOR_FOCUS",
1030
+ editorBlurEvent = "EDITOR_BLUR",
1031
+ keyEvent = "KEY",
1032
+ enabled = true
1033
+ } = options;
1034
+ const eventBus = useEventBus();
1035
+ const captureTableRef = React.useRef(captureTable);
1036
+ captureTableRef.current = captureTable;
1037
+ const focusedEditorIdRef = React.useRef(null);
1038
+ React.useEffect(() => {
1039
+ const unsubFocus = eventBus.on(`UI:${editorFocusEvent}`, (event) => {
1040
+ const editorId = event.payload?.editorId;
1041
+ if (typeof editorId === "string") {
1042
+ focusedEditorIdRef.current = editorId;
1043
+ }
1044
+ });
1045
+ const unsubBlur = eventBus.on(`UI:${editorBlurEvent}`, (event) => {
1046
+ const editorId = event.payload?.editorId;
1047
+ if (typeof editorId === "string" && focusedEditorIdRef.current === editorId) {
1048
+ focusedEditorIdRef.current = null;
1049
+ }
1050
+ });
1051
+ return () => {
1052
+ unsubFocus();
1053
+ unsubBlur();
1054
+ };
1055
+ }, [eventBus, editorFocusEvent, editorBlurEvent]);
1056
+ React.useEffect(() => {
1057
+ if (!enabled) return;
1058
+ const handleKeyDown = (event) => {
1059
+ if (event.isComposing) return;
1060
+ const target = focusedEditorIdRef.current ?? "shell";
1061
+ const entry = captureTableRef.current[target];
1062
+ const captured = entry !== void 0 && (entry.mode === "any" || entry.keys.has(event.key));
1063
+ if (captured) {
1064
+ event.preventDefault();
1065
+ }
1066
+ eventBus.emit(`UI:${keyEvent}`, {
1067
+ editorId: target,
1068
+ key: event.key,
1069
+ code: event.code,
1070
+ ctrl: event.ctrlKey,
1071
+ alt: event.altKey,
1072
+ shift: event.shiftKey,
1073
+ meta: event.metaKey,
1074
+ repeat: event.repeat
1075
+ });
1076
+ };
1077
+ window.addEventListener("keydown", handleKeyDown, { capture: true });
1078
+ return () => {
1079
+ window.removeEventListener("keydown", handleKeyDown, { capture: true });
1080
+ };
1081
+ }, [eventBus, enabled, keyEvent]);
1082
+ }
1026
1083
  function expressionEqual(a, b) {
1027
1084
  if (Object.is(a, b)) return true;
1028
1085
  if (Array.isArray(a) && Array.isArray(b)) {
@@ -1108,18 +1165,9 @@ function reconcileSlotProps(prev, next) {
1108
1165
  var log11 = logger.createLogger("almadar:ui:ui-slots");
1109
1166
  var DEFAULT_SOURCE_KEY = "__default__";
1110
1167
  var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
1111
- var ALL_SLOTS = [
1112
- "main",
1113
- "sidebar",
1114
- "modal",
1115
- "drawer",
1116
- "overlay",
1117
- "center",
1118
- "toast",
1119
- "hud-top",
1120
- "hud-bottom",
1121
- "floating"
1122
- ];
1168
+ var ALL_SLOTS = core.UI_SLOTS.filter(
1169
+ (slot) => !slot.includes(".") && slot !== "hud" && slot !== "screen"
1170
+ );
1123
1171
  var DEFAULT_SLOTS = ALL_SLOTS.reduce(
1124
1172
  (acc, slot) => {
1125
1173
  acc[slot] = null;
@@ -2217,7 +2265,8 @@ var en_default = {
2217
2265
  "richTextEditor.image": "Insert image",
2218
2266
  "richTextEditor.imagePrompt": "Image URL",
2219
2267
  "richTextEditor.placeholder": "Start writing\u2026",
2220
- "form.imageUrlFallback": "\u2026or paste an image address"
2268
+ "form.imageUrlFallback": "\u2026or paste an image address",
2269
+ "mermaid.unrenderable": "This diagram could not be displayed. Its source is shown below."
2221
2270
  };
2222
2271
 
2223
2272
  // hooks/useTranslate.ts
@@ -2956,6 +3005,7 @@ exports.useGitHubRepo = useGitHubRepo;
2956
3005
  exports.useGitHubRepos = useGitHubRepos;
2957
3006
  exports.useGitHubStatus = useGitHubStatus;
2958
3007
  exports.useInfiniteScroll = useInfiniteScroll;
3008
+ exports.useKeyboardRouter = useKeyboardRouter;
2959
3009
  exports.useLongPress = useLongPress;
2960
3010
  exports.useMediaQuery = useMediaQuery;
2961
3011
  exports.useOrbitalHistory = useOrbitalHistory;
@@ -1,7 +1,7 @@
1
1
  import { OrbitalSchema, FieldValue, EventPayload, EntityFrameState, EntityFieldWrite } from '@almadar/core';
2
2
  export { BusEvent, BusEventListener, BusEventSource, BusEventListener as EventListener, UISlot, Unsubscribe } from '@almadar/core';
3
- import { a as useEventBus } from '../useEventBus-Ckr4wqW3.cjs';
4
- export { u as useEmitEvent, b as useEventListener } from '../useEventBus-Ckr4wqW3.cjs';
3
+ import { b as useEventBus } from '../useKeyboardRouter-DQEE_9mq.cjs';
4
+ export { E as EditorKeyEvent, a as KeyCaptureEntry, K as KeyCaptureTable, U as UseKeyboardRouterOptions, u as useEmitEvent, c as useEventListener, d as useKeyboardRouter } from '../useKeyboardRouter-DQEE_9mq.cjs';
5
5
  export { E as EventBusContextType } from '../event-bus-types-Bl78kokd.cjs';
6
6
  export { D as DEFAULT_SLOTS, S as SlotAnimation, a as SlotChangeCallback, b as SlotContent, c as SlotRenderConfig, U as UISlotManager, u as useUISlotManager } from '../useUISlots-GNwGLlW2.cjs';
7
7
  export { a as NavStackApi, u as useNavStack } from '../NavStackContext-bw2nlBgT.cjs';
@@ -1,7 +1,7 @@
1
1
  import { OrbitalSchema, FieldValue, EventPayload, EntityFrameState, EntityFieldWrite } from '@almadar/core';
2
2
  export { BusEvent, BusEventListener, BusEventSource, BusEventListener as EventListener, UISlot, Unsubscribe } from '@almadar/core';
3
- import { a as useEventBus } from '../useEventBus-CQWyAWpK.js';
4
- export { u as useEmitEvent, b as useEventListener } from '../useEventBus-CQWyAWpK.js';
3
+ import { b as useEventBus } from '../useKeyboardRouter-D84cNWmA.js';
4
+ export { E as EditorKeyEvent, a as KeyCaptureEntry, K as KeyCaptureTable, U as UseKeyboardRouterOptions, u as useEmitEvent, c as useEventListener, d as useKeyboardRouter } from '../useKeyboardRouter-D84cNWmA.js';
5
5
  export { E as EventBusContextType } from '../event-bus-types-Bl78kokd.js';
6
6
  export { D as DEFAULT_SLOTS, S as SlotAnimation, a as SlotChangeCallback, b as SlotContent, c as SlotRenderConfig, U as UISlotManager, u as useUISlotManager } from '../useUISlots-GNwGLlW2.js';
7
7
  export { a as NavStackApi, u as useNavStack } from '../NavStackContext-bw2nlBgT.js';
@@ -2,7 +2,7 @@ import React, { createContext, useCallback, useState, useEffect, useMemo, useCon
2
2
  import { createLogger } from '@almadar/logger';
3
3
  import { EventBusContext, useTraitScopeChain } from '@almadar/ui/providers';
4
4
  export { useNavStack } from '@almadar/ui/providers';
5
- import { mergeEntityFrame, isRenderBindingMarker } from '@almadar/core';
5
+ import { UI_SLOTS, mergeEntityFrame, isRenderBindingMarker } from '@almadar/core';
6
6
  import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
7
7
 
8
8
  var log = createLogger("almadar:ui:orbital-history");
@@ -1018,6 +1018,63 @@ function useEmitEvent() {
1018
1018
  [eventBus]
1019
1019
  );
1020
1020
  }
1021
+ function useKeyboardRouter(options) {
1022
+ const {
1023
+ captureTable,
1024
+ editorFocusEvent = "EDITOR_FOCUS",
1025
+ editorBlurEvent = "EDITOR_BLUR",
1026
+ keyEvent = "KEY",
1027
+ enabled = true
1028
+ } = options;
1029
+ const eventBus = useEventBus();
1030
+ const captureTableRef = useRef(captureTable);
1031
+ captureTableRef.current = captureTable;
1032
+ const focusedEditorIdRef = useRef(null);
1033
+ useEffect(() => {
1034
+ const unsubFocus = eventBus.on(`UI:${editorFocusEvent}`, (event) => {
1035
+ const editorId = event.payload?.editorId;
1036
+ if (typeof editorId === "string") {
1037
+ focusedEditorIdRef.current = editorId;
1038
+ }
1039
+ });
1040
+ const unsubBlur = eventBus.on(`UI:${editorBlurEvent}`, (event) => {
1041
+ const editorId = event.payload?.editorId;
1042
+ if (typeof editorId === "string" && focusedEditorIdRef.current === editorId) {
1043
+ focusedEditorIdRef.current = null;
1044
+ }
1045
+ });
1046
+ return () => {
1047
+ unsubFocus();
1048
+ unsubBlur();
1049
+ };
1050
+ }, [eventBus, editorFocusEvent, editorBlurEvent]);
1051
+ useEffect(() => {
1052
+ if (!enabled) return;
1053
+ const handleKeyDown = (event) => {
1054
+ if (event.isComposing) return;
1055
+ const target = focusedEditorIdRef.current ?? "shell";
1056
+ const entry = captureTableRef.current[target];
1057
+ const captured = entry !== void 0 && (entry.mode === "any" || entry.keys.has(event.key));
1058
+ if (captured) {
1059
+ event.preventDefault();
1060
+ }
1061
+ eventBus.emit(`UI:${keyEvent}`, {
1062
+ editorId: target,
1063
+ key: event.key,
1064
+ code: event.code,
1065
+ ctrl: event.ctrlKey,
1066
+ alt: event.altKey,
1067
+ shift: event.shiftKey,
1068
+ meta: event.metaKey,
1069
+ repeat: event.repeat
1070
+ });
1071
+ };
1072
+ window.addEventListener("keydown", handleKeyDown, { capture: true });
1073
+ return () => {
1074
+ window.removeEventListener("keydown", handleKeyDown, { capture: true });
1075
+ };
1076
+ }, [eventBus, enabled, keyEvent]);
1077
+ }
1021
1078
  function expressionEqual(a, b) {
1022
1079
  if (Object.is(a, b)) return true;
1023
1080
  if (Array.isArray(a) && Array.isArray(b)) {
@@ -1103,18 +1160,9 @@ function reconcileSlotProps(prev, next) {
1103
1160
  var log11 = createLogger("almadar:ui:ui-slots");
1104
1161
  var DEFAULT_SOURCE_KEY = "__default__";
1105
1162
  var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
1106
- var ALL_SLOTS = [
1107
- "main",
1108
- "sidebar",
1109
- "modal",
1110
- "drawer",
1111
- "overlay",
1112
- "center",
1113
- "toast",
1114
- "hud-top",
1115
- "hud-bottom",
1116
- "floating"
1117
- ];
1163
+ var ALL_SLOTS = UI_SLOTS.filter(
1164
+ (slot) => !slot.includes(".") && slot !== "hud" && slot !== "screen"
1165
+ );
1118
1166
  var DEFAULT_SLOTS = ALL_SLOTS.reduce(
1119
1167
  (acc, slot) => {
1120
1168
  acc[slot] = null;
@@ -2212,7 +2260,8 @@ var en_default = {
2212
2260
  "richTextEditor.image": "Insert image",
2213
2261
  "richTextEditor.imagePrompt": "Image URL",
2214
2262
  "richTextEditor.placeholder": "Start writing\u2026",
2215
- "form.imageUrlFallback": "\u2026or paste an image address"
2263
+ "form.imageUrlFallback": "\u2026or paste an image address",
2264
+ "mermaid.unrenderable": "This diagram could not be displayed. Its source is shown below."
2216
2265
  };
2217
2266
 
2218
2267
  // hooks/useTranslate.ts
@@ -2918,4 +2967,4 @@ function useGitHubBranches(owner, repo, enabled = true) {
2918
2967
  });
2919
2968
  }
2920
2969
 
2921
- export { ALMADAR_DND_MIME, DEFAULT_SLOTS, I18nProvider, SharedEntityStoreContext, createSharedEntityStore, createTranslate, parseQueryBinding, runTickFrame, useAgentChat, useAuthContext, useCanvasGestures, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useInfiniteScroll, useLongPress, useMediaQuery, useOrbitalHistory, usePreview, usePullToRefresh, useQuerySingleton, useRenderInterpolation, useSharedEntitySnapshot, useSharedEntityStore, useSharedEntityStoreContext, useSwipeGesture, useTapReveal, useTraitListens, useTranslate, useUIEvents, useUISlotManager, useValidation };
2970
+ export { ALMADAR_DND_MIME, DEFAULT_SLOTS, I18nProvider, SharedEntityStoreContext, createSharedEntityStore, createTranslate, parseQueryBinding, runTickFrame, useAgentChat, useAuthContext, useCanvasGestures, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useInfiniteScroll, useKeyboardRouter, useLongPress, useMediaQuery, useOrbitalHistory, usePreview, usePullToRefresh, useQuerySingleton, useRenderInterpolation, useSharedEntitySnapshot, useSharedEntityStore, useSharedEntityStoreContext, useSwipeGesture, useTapReveal, useTraitListens, useTranslate, useUIEvents, useUISlotManager, useValidation };
@@ -185,7 +185,7 @@ function collectDrawnItems(nodes) {
185
185
  for (const n of nodes) {
186
186
  switch (n.type) {
187
187
  case "draw-sprite":
188
- if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height });
188
+ if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height, rotation: n.rotation });
189
189
  break;
190
190
  case "draw-shape":
191
191
  case "draw-text":
@@ -195,7 +195,7 @@ function collectDrawnItems(nodes) {
195
195
  break;
196
196
  case "draw-sprite-layer":
197
197
  for (const it of n.items) {
198
- if (isValidScenePos(it.position)) out.push({ pos: it.position, id: it.id, anchor: it.anchor, width: it.width, height: it.height });
198
+ if (isValidScenePos(it.position)) out.push({ pos: it.position, id: it.id, anchor: it.anchor, width: it.width, height: it.height, rotation: it.rotation });
199
199
  }
200
200
  break;
201
201
  case "draw-shape-layer":
@@ -215,6 +215,22 @@ function buildHitIndex(items) {
215
215
  }
216
216
  return m;
217
217
  }
218
+ function withPreviewPosition(nodes, id, pos) {
219
+ return nodes.map((n) => {
220
+ if (n.type === "draw-sprite-layer" || n.type === "draw-shape-layer" || n.type === "draw-text-layer") {
221
+ if (!n.items.some((it) => it.id === id)) return n;
222
+ return { ...n, items: n.items.map((it) => it.id === id ? { ...it, position: pos } : it) };
223
+ }
224
+ if (n.type === "draw-group") {
225
+ if (!Array.isArray(n.items)) return n;
226
+ return { ...n, items: withPreviewPosition(n.items, id, pos) };
227
+ }
228
+ if (n.id === id && "position" in n) {
229
+ return { ...n, position: pos };
230
+ }
231
+ return n;
232
+ });
233
+ }
218
234
  function useGameCanvas3DEvents(options) {
219
235
  const {
220
236
  tileClickEvent,
@@ -1379,7 +1395,7 @@ function Sprite3D({ node, projector, groupOpacity = 1 }) {
1379
1395
  {
1380
1396
  url: asset.url,
1381
1397
  scale,
1382
- rotation: [0, (node.rotation ?? 0) * 180 / Math.PI, 0],
1398
+ rotation: [0, node.rotation ?? 0, 0],
1383
1399
  animation: node.animation,
1384
1400
  fallbackGeometry: "box",
1385
1401
  castShadow: true,
@@ -1854,6 +1870,8 @@ function cn(...inputs) {
1854
1870
  }
1855
1871
  var DEFAULT_GRID_CONFIG = {
1856
1872
  cellSize: 1};
1873
+ var EDIT_SELECTION_COLOR = "#3b82f6";
1874
+ var EDIT_SELECTION_RADIUS = 0.6;
1857
1875
  function CameraPose({
1858
1876
  position,
1859
1877
  fov,
@@ -1917,7 +1935,13 @@ var Canvas3DHost = React3.forwardRef(
1917
1935
  lighting,
1918
1936
  post,
1919
1937
  children,
1920
- drawables
1938
+ drawables,
1939
+ editable = false,
1940
+ selectedId = null,
1941
+ onSelect,
1942
+ onMove,
1943
+ selectEvent,
1944
+ moveEvent
1921
1945
  }, ref) => {
1922
1946
  const containerRef = React3.useRef(null);
1923
1947
  const controlsRef = React3.useRef(null);
@@ -2003,6 +2027,25 @@ var Canvas3DHost = React3.forwardRef(
2003
2027
  }),
2004
2028
  [gridBounds, gridConfig]
2005
2029
  );
2030
+ const editDragRef = React3.useRef(null);
2031
+ const [dragPreview, setDragPreview] = React3.useState(null);
2032
+ const groundPointToScene = React3.useCallback((point) => ({
2033
+ x: Math.round(point.x / gridConfig.cellSize + gridBounds.minX),
2034
+ y: Math.round(point.z / gridConfig.cellSize + gridBounds.minZ)
2035
+ }), [gridConfig, gridBounds]);
2036
+ const paintDrawables = React3.useMemo(
2037
+ () => dragPreview ? withPreviewPosition(allDrawables, dragPreview.id, { x: dragPreview.x, y: dragPreview.y }) : allDrawables,
2038
+ [allDrawables, dragPreview]
2039
+ );
2040
+ const selectedItem = React3.useMemo(
2041
+ () => editable && selectedId != null ? drawnItems.find((it) => it.id === selectedId) : void 0,
2042
+ [editable, selectedId, drawnItems]
2043
+ );
2044
+ const selectionPos = React3.useMemo(() => {
2045
+ if (!selectedItem) return void 0;
2046
+ if (dragPreview && dragPreview.id === selectedItem.id) return { x: dragPreview.x, y: dragPreview.y };
2047
+ return selectedItem.pos;
2048
+ }, [selectedItem, dragPreview]);
2006
2049
  React3.useImperativeHandle(ref, () => ({
2007
2050
  getCameraPosition: () => {
2008
2051
  if (controlsRef.current) {
@@ -2083,17 +2126,14 @@ var Canvas3DHost = React3.forwardRef(
2083
2126
  const handleGroundClick = React3.useCallback((e) => {
2084
2127
  if (!tileClickEvent && !unitClickEvent) return;
2085
2128
  e.stopPropagation();
2086
- const cell = {
2087
- x: Math.round(e.point.x / gridConfig.cellSize + gridBounds.minX),
2088
- y: Math.round(e.point.z / gridConfig.cellSize + gridBounds.minZ)
2089
- };
2129
+ const cell = groundPointToScene(e.point);
2090
2130
  const hitId = hitIndex.get(`${cell.x},${cell.y}`);
2091
2131
  if (hitId !== void 0 && unitClickEvent) {
2092
2132
  eventBus.emit(`UI:${unitClickEvent}`, { unitId: hitId, x: cell.x, z: cell.y });
2093
2133
  } else if (tileClickEvent) {
2094
2134
  eventBus.emit(`UI:${tileClickEvent}`, { x: cell.x, z: cell.y });
2095
2135
  }
2096
- }, [tileClickEvent, unitClickEvent, gridConfig, gridBounds, hitIndex, eventBus]);
2136
+ }, [tileClickEvent, unitClickEvent, groundPointToScene, hitIndex, eventBus]);
2097
2137
  const handleDrawableClick = React3.useCallback((e) => {
2098
2138
  if (!unitClickEvent) return;
2099
2139
  let obj = e.object;
@@ -2111,6 +2151,57 @@ var Canvas3DHost = React3.forwardRef(
2111
2151
  obj = obj.parent;
2112
2152
  }
2113
2153
  }, [unitClickEvent, gridConfig, gridBounds, eventBus]);
2154
+ const handleEditDrawablePointerDown = React3.useCallback((e) => {
2155
+ if (!editable) return;
2156
+ let obj = e.object;
2157
+ while (obj) {
2158
+ const id = obj.userData.drawableId;
2159
+ if (id !== void 0) {
2160
+ e.stopPropagation();
2161
+ editDragRef.current = {
2162
+ id,
2163
+ startClientX: e.nativeEvent.clientX,
2164
+ startClientY: e.nativeEvent.clientY,
2165
+ moved: false
2166
+ };
2167
+ return;
2168
+ }
2169
+ obj = obj.parent;
2170
+ }
2171
+ }, [editable]);
2172
+ const handleEditGroundPointerMove = React3.useCallback((e) => {
2173
+ const drag = editDragRef.current;
2174
+ if (!editable || !drag) return;
2175
+ const dx = e.nativeEvent.clientX - drag.startClientX;
2176
+ const dy = e.nativeEvent.clientY - drag.startClientY;
2177
+ if (!drag.moved && Math.abs(dx) + Math.abs(dy) <= 5) return;
2178
+ drag.moved = true;
2179
+ const cell = groundPointToScene(e.point);
2180
+ setDragPreview({ id: drag.id, x: cell.x, y: cell.y });
2181
+ }, [editable, groundPointToScene]);
2182
+ const handleEditPointerUp = React3.useCallback((_e) => {
2183
+ if (!editable) return;
2184
+ const drag = editDragRef.current;
2185
+ editDragRef.current = null;
2186
+ if (!drag) {
2187
+ onSelect?.(null);
2188
+ if (selectEvent) eventBus.emit(`UI:${selectEvent}`, { id: null });
2189
+ return;
2190
+ }
2191
+ if (drag.moved) {
2192
+ setDragPreview((preview) => {
2193
+ if (preview && preview.id === drag.id) {
2194
+ onMove?.(drag.id, preview.x, preview.y);
2195
+ if (moveEvent) eventBus.emit(`UI:${moveEvent}`, { id: drag.id, x: preview.x, y: preview.y });
2196
+ }
2197
+ return null;
2198
+ });
2199
+ return;
2200
+ }
2201
+ const next = selectedId === drag.id ? null : drag.id;
2202
+ onSelect?.(next);
2203
+ if (selectEvent) eventBus.emit(`UI:${selectEvent}`, { id: next });
2204
+ }, [editable, selectedId, onMove, moveEvent, onSelect, selectEvent, eventBus]);
2114
2205
  if (externalLoading) {
2115
2206
  return /* @__PURE__ */ jsxRuntime.jsx(
2116
2207
  Canvas3DLoadingState,
@@ -2162,6 +2253,7 @@ var Canvas3DHost = React3.forwardRef(
2162
2253
  eventHandlers.handleCanvasClick(e);
2163
2254
  }
2164
2255
  },
2256
+ onPointerUp: handleEditPointerUp,
2165
2257
  children: [
2166
2258
  /* @__PURE__ */ jsxRuntime.jsx(CameraController3D, { onCameraChange: eventHandlers.handleCameraChange }),
2167
2259
  cameraMode !== "follow" && cameraMode !== "chase" && /* @__PURE__ */ jsxRuntime.jsx(CameraPose, { position: cameraConfig.position, fov: cameraConfig.fov, controls: controlsRef }),
@@ -2209,8 +2301,28 @@ var Canvas3DHost = React3.forwardRef(
2209
2301
  fadeStrength: 1
2210
2302
  }
2211
2303
  ),
2212
- allDrawables.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(BoneRegistryContext.Provider, { value: boneStore, children: /* @__PURE__ */ jsxRuntime.jsx("group", { onClick: unitClickEvent ? handleDrawableClick : void 0, children: allDrawables.map((node, i) => /* @__PURE__ */ jsxRuntime.jsx(Drawable3D, { node, projector: drawableProjector }, i)) }) }),
2213
- (tileClickEvent || unitClickEvent) && /* @__PURE__ */ jsxRuntime.jsxs(
2304
+ allDrawables.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(BoneRegistryContext.Provider, { value: boneStore, children: /* @__PURE__ */ jsxRuntime.jsx(
2305
+ "group",
2306
+ {
2307
+ onClick: !editable && unitClickEvent ? handleDrawableClick : void 0,
2308
+ onPointerDown: editable ? handleEditDrawablePointerDown : void 0,
2309
+ children: paintDrawables.map((node, i) => /* @__PURE__ */ jsxRuntime.jsx(Drawable3D, { node, projector: drawableProjector }, i))
2310
+ }
2311
+ ) }),
2312
+ selectionPos && /* @__PURE__ */ jsxRuntime.jsx(
2313
+ Shape3D,
2314
+ {
2315
+ node: {
2316
+ type: "draw-shape",
2317
+ shape: "ellipse",
2318
+ position: selectionPos,
2319
+ radiusX: EDIT_SELECTION_RADIUS,
2320
+ stroke: EDIT_SELECTION_COLOR
2321
+ },
2322
+ projector: drawableProjector
2323
+ }
2324
+ ),
2325
+ (tileClickEvent || unitClickEvent || editable) && /* @__PURE__ */ jsxRuntime.jsxs(
2214
2326
  "mesh",
2215
2327
  {
2216
2328
  rotation: [-Math.PI / 2, 0, 0],
@@ -2219,7 +2331,8 @@ var Canvas3DHost = React3.forwardRef(
2219
2331
  0,
2220
2332
  (gridBounds.maxZ - gridBounds.minZ) / 2 * cellSize
2221
2333
  ],
2222
- onClick: handleGroundClick,
2334
+ onClick: !editable ? handleGroundClick : void 0,
2335
+ onPointerMove: editable ? handleEditGroundPointerMove : void 0,
2223
2336
  children: [
2224
2337
  /* @__PURE__ */ jsxRuntime.jsx(
2225
2338
  "planeGeometry",
@@ -2239,7 +2352,7 @@ var Canvas3DHost = React3.forwardRef(
2239
2352
  drei.OrbitControls,
2240
2353
  {
2241
2354
  ref: controlsRef,
2242
- enabled: controlsEnabled && cameraMode !== "follow" && cameraMode !== "chase",
2355
+ enabled: controlsEnabled && !editable && cameraMode !== "follow" && cameraMode !== "chase",
2243
2356
  target: cameraTarget,
2244
2357
  enableDamping: true,
2245
2358
  dampingFactor: 0.05,
@@ -1,5 +1,5 @@
1
- import { C as CanvasPointLightConfig, I as IsometricTile, a as IsometricUnit, b as IsometricFeature, A as ApplicationLevelData, O as OrbitalLevelData, T as TraitLevelData, c as TransitionLevelData } from '../../../avl-schema-parser-Bt4NzAam.cjs';
2
- export { d as Canvas3DHost, e as Canvas3DHostHandle, f as Canvas3DHostProps, d as GameCanvas3D, U as UnitAnimationState } from '../../../avl-schema-parser-Bt4NzAam.cjs';
1
+ import { C as CanvasPointLightConfig, I as IsometricTile, a as IsometricUnit, b as IsometricFeature, A as ApplicationLevelData, O as OrbitalLevelData, T as TraitLevelData, c as TransitionLevelData } from '../../../avl-schema-parser-FQ1474v8.cjs';
2
+ export { d as Canvas3DHost, e as Canvas3DHostHandle, f as Canvas3DHostProps, d as GameCanvas3D, U as UnitAnimationState } from '../../../avl-schema-parser-FQ1474v8.cjs';
3
3
  import * as React$1 from 'react';
4
4
  import React__default, { Component, ReactNode, ErrorInfo } from 'react';
5
5
  import * as THREE from 'three';
@@ -1,5 +1,5 @@
1
- import { C as CanvasPointLightConfig, I as IsometricTile, a as IsometricUnit, b as IsometricFeature, A as ApplicationLevelData, O as OrbitalLevelData, T as TraitLevelData, c as TransitionLevelData } from '../../../avl-schema-parser-dvJKXn-o.js';
2
- export { d as Canvas3DHost, e as Canvas3DHostHandle, f as Canvas3DHostProps, d as GameCanvas3D, U as UnitAnimationState } from '../../../avl-schema-parser-dvJKXn-o.js';
1
+ import { C as CanvasPointLightConfig, I as IsometricTile, a as IsometricUnit, b as IsometricFeature, A as ApplicationLevelData, O as OrbitalLevelData, T as TraitLevelData, c as TransitionLevelData } from '../../../avl-schema-parser-DncJLEn9.js';
2
+ export { d as Canvas3DHost, e as Canvas3DHostHandle, f as Canvas3DHostProps, d as GameCanvas3D, U as UnitAnimationState } from '../../../avl-schema-parser-DncJLEn9.js';
3
3
  import * as React$1 from 'react';
4
4
  import React__default, { Component, ReactNode, ErrorInfo } from 'react';
5
5
  import * as THREE from 'three';