@archie/devtools 0.0.12 → 0.0.13
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/client/client.d.mts +38 -1
- package/dist/client/client.d.ts +38 -1
- package/dist/client/client.js +1460 -717
- package/dist/client/client.mjs +1420 -677
- package/dist/index.js +9 -3
- package/dist/index.mjs +9 -3
- package/package.json +1 -1
package/dist/client/client.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "../chunk-QZ7TP4HQ.mjs";
|
|
4
4
|
|
|
5
5
|
// src/client/dnd/DndProvider.tsx
|
|
6
|
-
import { useCallback as useCallback9, useEffect as
|
|
6
|
+
import { useCallback as useCallback9, useEffect as useEffect13 } from "react";
|
|
7
7
|
|
|
8
8
|
// node_modules/@dnd-kit/core/dist/core.esm.js
|
|
9
9
|
import React2, { createContext, useContext, useEffect as useEffect2, useState as useState2, useCallback as useCallback3, useMemo as useMemo2, useRef as useRef2, memo, useReducer, cloneElement, forwardRef } from "react";
|
|
@@ -4070,18 +4070,498 @@ function mergeInspectorTheme(theme) {
|
|
|
4070
4070
|
};
|
|
4071
4071
|
}
|
|
4072
4072
|
|
|
4073
|
+
// src/client/dnd/design-mode/structural-policy.ts
|
|
4074
|
+
var STRUCTURAL_COMPONENT_RE = /(?:Page|Provider|RootLayout)$/;
|
|
4075
|
+
var STRUCTURAL_TAGS = /* @__PURE__ */ new Set(["html", "body"]);
|
|
4076
|
+
function isStructuralComponentName(componentName) {
|
|
4077
|
+
return !!componentName && STRUCTURAL_COMPONENT_RE.test(componentName);
|
|
4078
|
+
}
|
|
4079
|
+
function isHighLevelStructuralElement(el) {
|
|
4080
|
+
if (!el) return false;
|
|
4081
|
+
if (el.hasAttribute("data-dnd-structural")) return true;
|
|
4082
|
+
const tag = el.tagName.toLowerCase();
|
|
4083
|
+
if (STRUCTURAL_TAGS.has(tag)) return true;
|
|
4084
|
+
if (el.id === "root") return true;
|
|
4085
|
+
return tag === "main" && (el.parentElement === document.body || el.parentElement?.id === "root");
|
|
4086
|
+
}
|
|
4087
|
+
function isStructuralScannedElement(scanned) {
|
|
4088
|
+
if (!scanned) return false;
|
|
4089
|
+
if (isHighLevelStructuralElement(scanned.element)) return true;
|
|
4090
|
+
return isStructuralComponentName(scanned.editorMeta?.componentName);
|
|
4091
|
+
}
|
|
4092
|
+
function isStructuralContainer(containerId, nodeMap) {
|
|
4093
|
+
return isStructuralScannedElement(nodeMap.get(containerId));
|
|
4094
|
+
}
|
|
4095
|
+
function isValidLaneContainer(containerId, projection, nodeMap) {
|
|
4096
|
+
const container = projection.containerIndex.get(containerId);
|
|
4097
|
+
if (!container) return false;
|
|
4098
|
+
if (container.children.length === 0) return false;
|
|
4099
|
+
return !isStructuralContainer(containerId, nodeMap);
|
|
4100
|
+
}
|
|
4101
|
+
function buildBlockedStructuralContainerIds(projection, nodeMap, sourceContainerId) {
|
|
4102
|
+
const blocked = /* @__PURE__ */ new Set();
|
|
4103
|
+
for (const containerId of projection.containerIndex.keys()) {
|
|
4104
|
+
if (containerId === sourceContainerId) continue;
|
|
4105
|
+
if (isStructuralContainer(containerId, nodeMap)) blocked.add(containerId);
|
|
4106
|
+
}
|
|
4107
|
+
return blocked;
|
|
4108
|
+
}
|
|
4109
|
+
|
|
4110
|
+
// src/client/dnd/design-mode/fiber-bridge.ts
|
|
4111
|
+
var FIBER_KEY_PREFIX = "__reactFiber$";
|
|
4112
|
+
var PROPS_KEY_PREFIX = "__reactProps$";
|
|
4113
|
+
var metaCache = /* @__PURE__ */ new WeakMap();
|
|
4114
|
+
var directMetaCache = /* @__PURE__ */ new WeakMap();
|
|
4115
|
+
function hasEditorMetaInFiberChain(fiber) {
|
|
4116
|
+
let current = fiber;
|
|
4117
|
+
while (current) {
|
|
4118
|
+
if (current.memoizedProps?.__editorMeta) return true;
|
|
4119
|
+
current = current.return;
|
|
4120
|
+
}
|
|
4121
|
+
return false;
|
|
4122
|
+
}
|
|
4123
|
+
function compareFiberCandidates(candidate, currentBest) {
|
|
4124
|
+
if (!currentBest) return 1;
|
|
4125
|
+
const candidateScore = [
|
|
4126
|
+
candidate.hasMatchingProps ? 1 : 0,
|
|
4127
|
+
candidate.hasDirectEditorMeta ? 1 : 0,
|
|
4128
|
+
candidate.hasCompanionPropsKey ? 1 : 0,
|
|
4129
|
+
candidate.hasEditorMetaInReturnChain ? 1 : 0,
|
|
4130
|
+
candidate.order
|
|
4131
|
+
];
|
|
4132
|
+
const currentScore = [
|
|
4133
|
+
currentBest.hasMatchingProps ? 1 : 0,
|
|
4134
|
+
currentBest.hasDirectEditorMeta ? 1 : 0,
|
|
4135
|
+
currentBest.hasCompanionPropsKey ? 1 : 0,
|
|
4136
|
+
currentBest.hasEditorMetaInReturnChain ? 1 : 0,
|
|
4137
|
+
currentBest.order
|
|
4138
|
+
];
|
|
4139
|
+
for (let i = 0; i < candidateScore.length; i += 1) {
|
|
4140
|
+
if (candidateScore[i] === currentScore[i]) continue;
|
|
4141
|
+
return candidateScore[i] > currentScore[i] ? 1 : -1;
|
|
4142
|
+
}
|
|
4143
|
+
return 0;
|
|
4144
|
+
}
|
|
4145
|
+
function getFiberSelectionFromElement(el) {
|
|
4146
|
+
const record = el;
|
|
4147
|
+
const keys = Object.keys(record);
|
|
4148
|
+
let bestCandidate = null;
|
|
4149
|
+
for (let i = 0; i < keys.length; i += 1) {
|
|
4150
|
+
const fiberKey = keys[i];
|
|
4151
|
+
if (!fiberKey.startsWith(FIBER_KEY_PREFIX)) continue;
|
|
4152
|
+
const fiber = record[fiberKey];
|
|
4153
|
+
if (!fiber) continue;
|
|
4154
|
+
if (fiber.stateNode && fiber.stateNode !== el) continue;
|
|
4155
|
+
const suffix = fiberKey.slice(FIBER_KEY_PREFIX.length);
|
|
4156
|
+
const propsKey = `${PROPS_KEY_PREFIX}${suffix}`;
|
|
4157
|
+
const hasCompanionPropsKey = Object.prototype.hasOwnProperty.call(record, propsKey);
|
|
4158
|
+
const companionProps = hasCompanionPropsKey ? record[propsKey] : void 0;
|
|
4159
|
+
const candidate = {
|
|
4160
|
+
fiber,
|
|
4161
|
+
fiberKey,
|
|
4162
|
+
hasMatchingProps: hasCompanionPropsKey && fiber.memoizedProps === companionProps,
|
|
4163
|
+
hasDirectEditorMeta: !!fiber.memoizedProps?.__editorMeta,
|
|
4164
|
+
hasCompanionPropsKey,
|
|
4165
|
+
hasEditorMetaInReturnChain: hasEditorMetaInFiberChain(fiber),
|
|
4166
|
+
// Tiebreaker: higher index in Object.keys = later React fiber attachment.
|
|
4167
|
+
// Relies on spec-guaranteed insertion-order for string keys (ES2015+).
|
|
4168
|
+
order: i
|
|
4169
|
+
};
|
|
4170
|
+
if (compareFiberCandidates(candidate, bestCandidate) > 0) {
|
|
4171
|
+
bestCandidate = candidate;
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4174
|
+
if (!bestCandidate) {
|
|
4175
|
+
return { fiber: null, fiberKey: null };
|
|
4176
|
+
}
|
|
4177
|
+
return { fiber: bestCandidate.fiber, fiberKey: bestCandidate.fiberKey };
|
|
4178
|
+
}
|
|
4179
|
+
function readFiberAwareCache(cache, el, selection) {
|
|
4180
|
+
const cached = cache.get(el);
|
|
4181
|
+
if (!cached) return void 0;
|
|
4182
|
+
if (cached.fiber !== selection.fiber) return void 0;
|
|
4183
|
+
if (cached.fiberKey !== selection.fiberKey) return void 0;
|
|
4184
|
+
return cached.value;
|
|
4185
|
+
}
|
|
4186
|
+
function writeFiberAwareCache(cache, el, selection, value) {
|
|
4187
|
+
cache.set(el, {
|
|
4188
|
+
fiber: selection.fiber,
|
|
4189
|
+
fiberKey: selection.fiberKey,
|
|
4190
|
+
value
|
|
4191
|
+
});
|
|
4192
|
+
return value;
|
|
4193
|
+
}
|
|
4194
|
+
function isRootSvg(el) {
|
|
4195
|
+
return el instanceof SVGElement && el.tagName.toLowerCase() === "svg";
|
|
4196
|
+
}
|
|
4197
|
+
function isPassthroughMeta(meta) {
|
|
4198
|
+
return !!meta?.staticProps && meta.staticProps.asChild === true;
|
|
4199
|
+
}
|
|
4200
|
+
function isWrapperMeta(meta) {
|
|
4201
|
+
if (!meta?.componentName) return false;
|
|
4202
|
+
return meta.componentName[0] !== meta.componentName[0].toLowerCase();
|
|
4203
|
+
}
|
|
4204
|
+
function hasOwnTextContent(el) {
|
|
4205
|
+
for (const node of Array.from(el.childNodes)) {
|
|
4206
|
+
if (node.nodeType !== Node.TEXT_NODE) continue;
|
|
4207
|
+
if (node.textContent?.trim()) return true;
|
|
4208
|
+
}
|
|
4209
|
+
return false;
|
|
4210
|
+
}
|
|
4211
|
+
function getCandidateChildren(el) {
|
|
4212
|
+
return Array.from(el.children).filter((child) => {
|
|
4213
|
+
if (!(child instanceof HTMLElement)) return false;
|
|
4214
|
+
if (child.hasAttribute("data-design-mode-ui")) return false;
|
|
4215
|
+
if (child.hasAttribute("data-design-mode-allow")) return false;
|
|
4216
|
+
return !shouldIgnoreAsVirtualRuntime(child);
|
|
4217
|
+
});
|
|
4218
|
+
}
|
|
4219
|
+
function shouldIgnoreAsVirtualRuntime(el) {
|
|
4220
|
+
const tag = el.tagName.toLowerCase();
|
|
4221
|
+
if (tag === "canvas") return true;
|
|
4222
|
+
if (tag === "script" || tag === "style" || tag === "noscript") return true;
|
|
4223
|
+
if (el instanceof SVGElement && !isRootSvg(el)) return true;
|
|
4224
|
+
return false;
|
|
4225
|
+
}
|
|
4226
|
+
function getDirectEditorMeta(el) {
|
|
4227
|
+
const selection = getFiberSelectionFromElement(el);
|
|
4228
|
+
const cached = readFiberAwareCache(directMetaCache, el, selection);
|
|
4229
|
+
if (cached !== void 0) return cached;
|
|
4230
|
+
const meta = selection.fiber?.memoizedProps?.__editorMeta;
|
|
4231
|
+
return writeFiberAwareCache(directMetaCache, el, selection, meta ?? null);
|
|
4232
|
+
}
|
|
4233
|
+
function getEditorMeta(el) {
|
|
4234
|
+
const selection = getFiberSelectionFromElement(el);
|
|
4235
|
+
const cached = readFiberAwareCache(metaCache, el, selection);
|
|
4236
|
+
if (cached !== void 0) return cached;
|
|
4237
|
+
let fiber = selection.fiber;
|
|
4238
|
+
while (fiber) {
|
|
4239
|
+
const meta = fiber.memoizedProps?.__editorMeta;
|
|
4240
|
+
if (meta) {
|
|
4241
|
+
return writeFiberAwareCache(metaCache, el, selection, meta);
|
|
4242
|
+
}
|
|
4243
|
+
fiber = fiber.return;
|
|
4244
|
+
}
|
|
4245
|
+
return writeFiberAwareCache(metaCache, el, selection, null);
|
|
4246
|
+
}
|
|
4247
|
+
function findMetaOwnerElement(el) {
|
|
4248
|
+
let current = el;
|
|
4249
|
+
while (current) {
|
|
4250
|
+
if (getEditorMeta(current)) return current;
|
|
4251
|
+
current = current.parentElement;
|
|
4252
|
+
}
|
|
4253
|
+
return null;
|
|
4254
|
+
}
|
|
4255
|
+
function resolveHostElementFromFiberChain(el) {
|
|
4256
|
+
if (shouldIgnoreAsVirtualRuntime(el)) return null;
|
|
4257
|
+
const directMeta = getDirectEditorMeta(el);
|
|
4258
|
+
const ownerMeta = directMeta ?? getEditorMeta(el);
|
|
4259
|
+
if (isPassthroughMeta(ownerMeta)) {
|
|
4260
|
+
const children = getCandidateChildren(el);
|
|
4261
|
+
if (children.length !== 1) return null;
|
|
4262
|
+
return children[0];
|
|
4263
|
+
}
|
|
4264
|
+
if (directMeta && !isWrapperMeta(directMeta)) return el;
|
|
4265
|
+
const MAX_STEPS = 10;
|
|
4266
|
+
const ownerNodeId = ownerMeta?.nodeId ?? null;
|
|
4267
|
+
let outermost = el;
|
|
4268
|
+
let parent = el.parentElement;
|
|
4269
|
+
for (let i = 0; i < MAX_STEPS; i += 1) {
|
|
4270
|
+
if (!parent || parent === document.body) break;
|
|
4271
|
+
if (shouldIgnoreAsVirtualRuntime(parent)) break;
|
|
4272
|
+
const parentMeta = getEditorMeta(parent);
|
|
4273
|
+
if (!parentMeta) break;
|
|
4274
|
+
if (ownerNodeId && parentMeta.nodeId !== ownerNodeId) break;
|
|
4275
|
+
if (isStructuralComponentName(parentMeta.componentName)) break;
|
|
4276
|
+
if (!isWrapperMeta(parentMeta)) break;
|
|
4277
|
+
if (isPassthroughMeta(parentMeta)) break;
|
|
4278
|
+
if (hasOwnTextContent(parent)) break;
|
|
4279
|
+
const children = getCandidateChildren(parent);
|
|
4280
|
+
if (children.length !== 1) break;
|
|
4281
|
+
outermost = parent;
|
|
4282
|
+
parent = parent.parentElement;
|
|
4283
|
+
}
|
|
4284
|
+
return outermost;
|
|
4285
|
+
}
|
|
4286
|
+
function resolveDragSurface(el) {
|
|
4287
|
+
const host = resolveHostElementFromFiberChain(el);
|
|
4288
|
+
if (!host) {
|
|
4289
|
+
return { host: null, metaOwner: null, kind: "direct-host" };
|
|
4290
|
+
}
|
|
4291
|
+
const metaOwner = findMetaOwnerElement(host) ?? findMetaOwnerElement(el);
|
|
4292
|
+
const ownerMeta = metaOwner ? getEditorMeta(metaOwner) : null;
|
|
4293
|
+
const kind = isPassthroughMeta(ownerMeta) ? "passthrough-slot" : metaOwner && metaOwner !== host && isWrapperMeta(ownerMeta) ? "wrapper-to-host" : "direct-host";
|
|
4294
|
+
return {
|
|
4295
|
+
host,
|
|
4296
|
+
metaOwner,
|
|
4297
|
+
kind
|
|
4298
|
+
};
|
|
4299
|
+
}
|
|
4300
|
+
|
|
4301
|
+
// src/client/dnd/design-mode/node-key.ts
|
|
4302
|
+
var _runtimeKeyMap = /* @__PURE__ */ new WeakMap();
|
|
4303
|
+
var _runtimeKeyCounter = 0;
|
|
4304
|
+
function getRuntimeKey(el) {
|
|
4305
|
+
const existing = _runtimeKeyMap.get(el);
|
|
4306
|
+
if (existing) return existing;
|
|
4307
|
+
const key2 = `runtime:${++_runtimeKeyCounter}`;
|
|
4308
|
+
_runtimeKeyMap.set(el, key2);
|
|
4309
|
+
return key2;
|
|
4310
|
+
}
|
|
4311
|
+
function getNodeKeyFromElement(el) {
|
|
4312
|
+
const resolved = resolveDragSurface(el);
|
|
4313
|
+
const host = resolved.host ?? el;
|
|
4314
|
+
const meta = getEditorMeta(host);
|
|
4315
|
+
if (meta) return meta.nodeId;
|
|
4316
|
+
const persisted = host.getAttribute("data-dnd-node-id");
|
|
4317
|
+
if (persisted) return persisted;
|
|
4318
|
+
return getRuntimeKey(host);
|
|
4319
|
+
}
|
|
4320
|
+
function getInspectorRefFromElement(el) {
|
|
4321
|
+
const resolved = resolveDragSurface(el);
|
|
4322
|
+
const host = resolved.host ?? el;
|
|
4323
|
+
const meta = getEditorMeta(host);
|
|
4324
|
+
if (!meta) return null;
|
|
4325
|
+
return { relativePath: meta.file, line: meta.line, column: meta.col };
|
|
4326
|
+
}
|
|
4327
|
+
|
|
4328
|
+
// src/client/dnd/design-mode/arrange-persistence-diagnostics.ts
|
|
4329
|
+
function isSourceBackedRuntimeNodeKey(nodeKey) {
|
|
4330
|
+
return typeof nodeKey === "string" && nodeKey.trim().length > 0 && !nodeKey.startsWith("runtime:");
|
|
4331
|
+
}
|
|
4332
|
+
function mergeRuntimeArrangeDiagnostics(diagnosticsEntries) {
|
|
4333
|
+
const blockedReasons = /* @__PURE__ */ new Set();
|
|
4334
|
+
const notes = /* @__PURE__ */ new Set();
|
|
4335
|
+
let structurallyUnsafe = false;
|
|
4336
|
+
for (const diagnostics of diagnosticsEntries) {
|
|
4337
|
+
if (!diagnostics) continue;
|
|
4338
|
+
if (diagnostics.structurallyUnsafe === true) {
|
|
4339
|
+
structurallyUnsafe = true;
|
|
4340
|
+
}
|
|
4341
|
+
for (const reason of diagnostics.blockedReasons ?? []) {
|
|
4342
|
+
if (typeof reason === "string" && reason.trim().length > 0) {
|
|
4343
|
+
blockedReasons.add(reason);
|
|
4344
|
+
}
|
|
4345
|
+
}
|
|
4346
|
+
for (const note of diagnostics.notes ?? []) {
|
|
4347
|
+
if (typeof note === "string" && note.trim().length > 0) {
|
|
4348
|
+
notes.add(note);
|
|
4349
|
+
}
|
|
4350
|
+
}
|
|
4351
|
+
}
|
|
4352
|
+
if (!structurallyUnsafe && blockedReasons.size === 0 && notes.size === 0) {
|
|
4353
|
+
return null;
|
|
4354
|
+
}
|
|
4355
|
+
return {
|
|
4356
|
+
...structurallyUnsafe ? { structurallyUnsafe: true } : {},
|
|
4357
|
+
...blockedReasons.size > 0 ? { blockedReasons: Array.from(blockedReasons) } : {},
|
|
4358
|
+
...notes.size > 0 ? { notes: Array.from(notes) } : {}
|
|
4359
|
+
};
|
|
4360
|
+
}
|
|
4361
|
+
function detectDuplicateSourceBackedNodeKeys(nodeKeys) {
|
|
4362
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
4363
|
+
const duplicateKeys = /* @__PURE__ */ new Set();
|
|
4364
|
+
for (const nodeKey of nodeKeys) {
|
|
4365
|
+
if (!isSourceBackedRuntimeNodeKey(nodeKey)) {
|
|
4366
|
+
continue;
|
|
4367
|
+
}
|
|
4368
|
+
if (seenKeys.has(nodeKey)) {
|
|
4369
|
+
duplicateKeys.add(nodeKey);
|
|
4370
|
+
continue;
|
|
4371
|
+
}
|
|
4372
|
+
seenKeys.add(nodeKey);
|
|
4373
|
+
}
|
|
4374
|
+
return Array.from(duplicateKeys);
|
|
4375
|
+
}
|
|
4376
|
+
function buildOccurrenceNodeId(nodeKey, occurrenceIndex) {
|
|
4377
|
+
return `${nodeKey}::${occurrenceIndex}`;
|
|
4378
|
+
}
|
|
4379
|
+
function buildEffectiveNodeIdsByCommitNodeId(commitNodeIds, nodeKeysByCommitNodeId) {
|
|
4380
|
+
const countsByNodeKey = /* @__PURE__ */ new Map();
|
|
4381
|
+
for (const commitNodeId of commitNodeIds) {
|
|
4382
|
+
const nodeKey = nodeKeysByCommitNodeId.get(commitNodeId);
|
|
4383
|
+
if (!isSourceBackedRuntimeNodeKey(nodeKey)) {
|
|
4384
|
+
continue;
|
|
4385
|
+
}
|
|
4386
|
+
countsByNodeKey.set(nodeKey, (countsByNodeKey.get(nodeKey) ?? 0) + 1);
|
|
4387
|
+
}
|
|
4388
|
+
const seenByNodeKey = /* @__PURE__ */ new Map();
|
|
4389
|
+
const effectiveNodeIdsByCommitNodeId = /* @__PURE__ */ new Map();
|
|
4390
|
+
for (const commitNodeId of commitNodeIds) {
|
|
4391
|
+
const nodeKey = nodeKeysByCommitNodeId.get(commitNodeId);
|
|
4392
|
+
if (!isSourceBackedRuntimeNodeKey(nodeKey) || (countsByNodeKey.get(nodeKey) ?? 0) < 2) {
|
|
4393
|
+
effectiveNodeIdsByCommitNodeId.set(commitNodeId, null);
|
|
4394
|
+
continue;
|
|
4395
|
+
}
|
|
4396
|
+
const occurrenceIndex = seenByNodeKey.get(nodeKey) ?? 0;
|
|
4397
|
+
seenByNodeKey.set(nodeKey, occurrenceIndex + 1);
|
|
4398
|
+
effectiveNodeIdsByCommitNodeId.set(
|
|
4399
|
+
commitNodeId,
|
|
4400
|
+
buildOccurrenceNodeId(nodeKey, occurrenceIndex)
|
|
4401
|
+
);
|
|
4402
|
+
}
|
|
4403
|
+
return effectiveNodeIdsByCommitNodeId;
|
|
4404
|
+
}
|
|
4405
|
+
function createCommitLaneIdentitySnapshot(params) {
|
|
4406
|
+
return {
|
|
4407
|
+
laneContainerId: params.laneContainerId,
|
|
4408
|
+
commitNodeIds: [...params.commitNodeIds],
|
|
4409
|
+
nodeKeysByCommitNodeId: new Map(params.nodeKeysByCommitNodeId),
|
|
4410
|
+
effectiveNodeIdsByCommitNodeId: buildEffectiveNodeIdsByCommitNodeId(
|
|
4411
|
+
params.commitNodeIds,
|
|
4412
|
+
params.nodeKeysByCommitNodeId
|
|
4413
|
+
),
|
|
4414
|
+
committable: true
|
|
4415
|
+
};
|
|
4416
|
+
}
|
|
4417
|
+
function hasSafeOccurrenceIdentities(lane, duplicateNodeKey) {
|
|
4418
|
+
const effectiveNodeIds = lane.commitNodeIds.filter(
|
|
4419
|
+
(commitNodeId) => lane.nodeKeysByCommitNodeId.get(commitNodeId) === duplicateNodeKey
|
|
4420
|
+
).map(
|
|
4421
|
+
(commitNodeId) => lane.effectiveNodeIdsByCommitNodeId.get(commitNodeId) ?? null
|
|
4422
|
+
);
|
|
4423
|
+
return effectiveNodeIds.length > 1 && effectiveNodeIds.every(
|
|
4424
|
+
(effectiveNodeId) => typeof effectiveNodeId === "string" && effectiveNodeId.length > 0
|
|
4425
|
+
) && new Set(effectiveNodeIds).size === effectiveNodeIds.length;
|
|
4426
|
+
}
|
|
4427
|
+
function buildLanePersistenceDiagnostics(params) {
|
|
4428
|
+
const duplicateNodeKeys = detectDuplicateSourceBackedNodeKeys(
|
|
4429
|
+
buildNodeKeysFromCommitIds(
|
|
4430
|
+
params.lane.commitNodeIds,
|
|
4431
|
+
params.lane.nodeKeysByCommitNodeId
|
|
4432
|
+
)
|
|
4433
|
+
);
|
|
4434
|
+
if (duplicateNodeKeys.length === 0) {
|
|
4435
|
+
return null;
|
|
4436
|
+
}
|
|
4437
|
+
const safeDuplicateNodeKeys = duplicateNodeKeys.filter(
|
|
4438
|
+
(nodeKey) => hasSafeOccurrenceIdentities(params.lane, nodeKey)
|
|
4439
|
+
);
|
|
4440
|
+
if (safeDuplicateNodeKeys.length === duplicateNodeKeys.length) {
|
|
4441
|
+
return {
|
|
4442
|
+
notes: safeDuplicateNodeKeys.map(
|
|
4443
|
+
(nodeKey) => `Arrange lane ${params.lane.laneContainerId} repeats source-backed nodeKey ${nodeKey}, but runtime assigned explicit occurrence identities for each commit root.`
|
|
4444
|
+
)
|
|
4445
|
+
};
|
|
4446
|
+
}
|
|
4447
|
+
return {
|
|
4448
|
+
structurallyUnsafe: true,
|
|
4449
|
+
blockedReasons: duplicateNodeKeys.map(
|
|
4450
|
+
(nodeKey) => `Arrange lane ${params.lane.laneContainerId} contains multiple commit roots with the same source-backed nodeKey (${nodeKey}), so local save is unsafe.`
|
|
4451
|
+
),
|
|
4452
|
+
notes: [
|
|
4453
|
+
`Lane ${params.lane.laneContainerId} duplicates source-backed nodeKeys: ${duplicateNodeKeys.join(", ")}`
|
|
4454
|
+
]
|
|
4455
|
+
};
|
|
4456
|
+
}
|
|
4457
|
+
function removeCommitNodeId(commitNodeIds, commitNodeId) {
|
|
4458
|
+
return commitNodeIds.filter((candidate) => candidate !== commitNodeId);
|
|
4459
|
+
}
|
|
4460
|
+
function buildNodeKeysFromCommitIds(commitNodeIds, nodeKeysByCommitNodeId) {
|
|
4461
|
+
return commitNodeIds.map(
|
|
4462
|
+
(commitNodeId) => nodeKeysByCommitNodeId.get(commitNodeId) ?? null
|
|
4463
|
+
);
|
|
4464
|
+
}
|
|
4465
|
+
function captureCommitLaneIdentitySnapshot(lane, resolveNodeElement) {
|
|
4466
|
+
if (!lane?.committable) return null;
|
|
4467
|
+
const nodeKeysByCommitNodeId = /* @__PURE__ */ new Map();
|
|
4468
|
+
for (const commitNodeId of lane.orderedCommitNodeIds) {
|
|
4469
|
+
const element = resolveNodeElement(commitNodeId);
|
|
4470
|
+
if (!element) return null;
|
|
4471
|
+
nodeKeysByCommitNodeId.set(commitNodeId, getNodeKeyFromElement(element));
|
|
4472
|
+
}
|
|
4473
|
+
return createCommitLaneIdentitySnapshot({
|
|
4474
|
+
laneContainerId: lane.laneContainerId,
|
|
4475
|
+
nodeKeysByCommitNodeId,
|
|
4476
|
+
commitNodeIds: lane.orderedCommitNodeIds
|
|
4477
|
+
});
|
|
4478
|
+
}
|
|
4479
|
+
function projectCommitNodeIdsForMove(params) {
|
|
4480
|
+
const withoutMovedCommit = removeCommitNodeId(
|
|
4481
|
+
params.commitNodeIds,
|
|
4482
|
+
params.movedCommitNodeId
|
|
4483
|
+
);
|
|
4484
|
+
if (typeof params.targetIndex !== "number" || !Number.isFinite(params.targetIndex)) {
|
|
4485
|
+
return [...withoutMovedCommit, params.movedCommitNodeId];
|
|
4486
|
+
}
|
|
4487
|
+
const insertionIndex = Math.max(
|
|
4488
|
+
0,
|
|
4489
|
+
Math.min(params.targetIndex, withoutMovedCommit.length)
|
|
4490
|
+
);
|
|
4491
|
+
return [
|
|
4492
|
+
...withoutMovedCommit.slice(0, insertionIndex),
|
|
4493
|
+
params.movedCommitNodeId,
|
|
4494
|
+
...withoutMovedCommit.slice(insertionIndex)
|
|
4495
|
+
];
|
|
4496
|
+
}
|
|
4497
|
+
function buildMovePersistenceDiagnostics(params) {
|
|
4498
|
+
const {
|
|
4499
|
+
sourceLane,
|
|
4500
|
+
targetLane,
|
|
4501
|
+
movedCommitNodeId,
|
|
4502
|
+
sourceLaneContainerId,
|
|
4503
|
+
targetLaneContainerId,
|
|
4504
|
+
targetIndex
|
|
4505
|
+
} = params;
|
|
4506
|
+
const sameLane = !!sourceLaneContainerId && !!targetLaneContainerId && sourceLaneContainerId === targetLaneContainerId;
|
|
4507
|
+
const targetSnapshot = sameLane ? targetLane ?? sourceLane : targetLane;
|
|
4508
|
+
const sourceLaneAfter = !sameLane && sourceLane?.committable ? createCommitLaneIdentitySnapshot({
|
|
4509
|
+
laneContainerId: sourceLane.laneContainerId,
|
|
4510
|
+
commitNodeIds: removeCommitNodeId(
|
|
4511
|
+
sourceLane.commitNodeIds,
|
|
4512
|
+
movedCommitNodeId
|
|
4513
|
+
),
|
|
4514
|
+
nodeKeysByCommitNodeId: sourceLane.nodeKeysByCommitNodeId
|
|
4515
|
+
}) : null;
|
|
4516
|
+
const targetLaneAfter = targetSnapshot?.committable ? createCommitLaneIdentitySnapshot({
|
|
4517
|
+
laneContainerId: targetSnapshot.laneContainerId,
|
|
4518
|
+
commitNodeIds: projectCommitNodeIdsForMove({
|
|
4519
|
+
commitNodeIds: targetSnapshot.commitNodeIds,
|
|
4520
|
+
movedCommitNodeId,
|
|
4521
|
+
targetIndex
|
|
4522
|
+
}),
|
|
4523
|
+
nodeKeysByCommitNodeId: new Map([
|
|
4524
|
+
...targetSnapshot.nodeKeysByCommitNodeId,
|
|
4525
|
+
...sourceLane?.nodeKeysByCommitNodeId.has(movedCommitNodeId) ? [
|
|
4526
|
+
[
|
|
4527
|
+
movedCommitNodeId,
|
|
4528
|
+
sourceLane.nodeKeysByCommitNodeId.get(movedCommitNodeId) ?? null
|
|
4529
|
+
]
|
|
4530
|
+
] : []
|
|
4531
|
+
])
|
|
4532
|
+
}) : null;
|
|
4533
|
+
return mergeRuntimeArrangeDiagnostics([
|
|
4534
|
+
sourceLaneAfter ? buildLanePersistenceDiagnostics({ lane: sourceLaneAfter }) : null,
|
|
4535
|
+
targetLaneAfter ? buildLanePersistenceDiagnostics({ lane: targetLaneAfter }) : null
|
|
4536
|
+
]);
|
|
4537
|
+
}
|
|
4538
|
+
|
|
4073
4539
|
// src/client/dnd/design-mode/store.ts
|
|
4074
4540
|
function summarizeEditorChangeEvent(event) {
|
|
4541
|
+
if (event.kind === "insert") {
|
|
4542
|
+
return `insert ${event.displayName} at ${event.containerId}[${event.index}]`;
|
|
4543
|
+
}
|
|
4075
4544
|
if (event.kind === "move") {
|
|
4076
4545
|
const selectedHint = event.selectedNodeKey && event.selectedNodeKey !== event.nodeKey ? ` (selected ${event.selectedNodeKey})` : "";
|
|
4077
4546
|
const anchorHint = event.anchorNodeKey && event.anchorNodeKey !== event.nodeKey ? ` (anchor ${event.anchorNodeKey})` : "";
|
|
4078
|
-
|
|
4547
|
+
const beforeParentNodeId = event.payload.before.effectiveContainerNodeId ?? event.payload.before.parentNodeId;
|
|
4548
|
+
const afterParentNodeId = event.payload.after.effectiveContainerNodeId ?? event.payload.after.parentNodeId;
|
|
4549
|
+
const commitNodeId = event.payload.after.effectiveCommitNodeId ?? event.payload.after.commitNodeId;
|
|
4550
|
+
const beforeNodeId = event.payload.after.effectiveBeforeNodeId ?? event.payload.after.beforeNodeId;
|
|
4551
|
+
const afterNodeId = event.payload.after.effectiveAfterNodeId ?? event.payload.after.afterNodeId;
|
|
4552
|
+
return `move ${event.nodeKey}: parent ${beforeParentNodeId}[${event.payload.before.index}] \u2192 ${afterParentNodeId}[${event.payload.after.index}]${commitNodeId ? ` (commit ${commitNodeId}` : ""}${beforeNodeId ? ` before ${beforeNodeId}` : ""}${afterNodeId ? ` after ${afterNodeId}` : ""}${commitNodeId ? ")" : ""}${selectedHint}${anchorHint}`;
|
|
4079
4553
|
}
|
|
4080
4554
|
if (event.payload.after.mode === "grid-span") {
|
|
4081
4555
|
return `resize(grid-span) ${event.nodeKey}: col ${event.payload.before.colSpan ?? 1}\u2192${event.payload.after.colSpan ?? 1}, row ${event.payload.before.rowSpan ?? 1}\u2192${event.payload.after.rowSpan ?? 1}`;
|
|
4082
4556
|
}
|
|
4083
4557
|
return `resize(px) ${event.nodeKey}: ${event.payload.before.width}\xD7${event.payload.before.height} \u2192 ${event.payload.after.width}\xD7${event.payload.after.height}`;
|
|
4084
4558
|
}
|
|
4559
|
+
function buildFallbackOperationId(now, nodeId) {
|
|
4560
|
+
return `${now}:${nodeId}`;
|
|
4561
|
+
}
|
|
4562
|
+
function inferMoveCommandType(before, after) {
|
|
4563
|
+
return before.parentNodeId === after.parentNodeId ? "reorder" : "moveCrossContainer";
|
|
4564
|
+
}
|
|
4085
4565
|
function rebuildNodeEdit(orderedChanges, nodeKey) {
|
|
4086
4566
|
let latestMove;
|
|
4087
4567
|
let latestResize;
|
|
@@ -4096,11 +4576,36 @@ function rebuildNodeEdit(orderedChanges, nodeKey) {
|
|
|
4096
4576
|
nodeId = ev.nodeId;
|
|
4097
4577
|
inspectorRef = ev.inspectorRef;
|
|
4098
4578
|
if (ev.kind === "move") latestMove = ev.payload;
|
|
4099
|
-
else latestResize = ev.payload;
|
|
4579
|
+
else if (ev.kind === "resize") latestResize = ev.payload;
|
|
4100
4580
|
}
|
|
4101
4581
|
if (count === 0) return null;
|
|
4102
4582
|
return { nodeKey, nodeId, inspectorRef, latestMove, latestResize, changeCount: count, lastUpdatedAt: lastAt };
|
|
4103
4583
|
}
|
|
4584
|
+
function coalesceOrderedChanges(changes) {
|
|
4585
|
+
if (changes.length <= 1) return changes;
|
|
4586
|
+
const result = [];
|
|
4587
|
+
for (const event of changes) {
|
|
4588
|
+
const prev = result[result.length - 1];
|
|
4589
|
+
if (prev && prev.kind === "resize" && event.kind === "resize" && prev.nodeKey === event.nodeKey) {
|
|
4590
|
+
result[result.length - 1] = {
|
|
4591
|
+
...event,
|
|
4592
|
+
payload: { before: prev.payload.before, after: event.payload.after }
|
|
4593
|
+
};
|
|
4594
|
+
} else {
|
|
4595
|
+
result.push(event);
|
|
4596
|
+
}
|
|
4597
|
+
}
|
|
4598
|
+
return result;
|
|
4599
|
+
}
|
|
4600
|
+
function buildEmptyArrangeSessionState() {
|
|
4601
|
+
return {
|
|
4602
|
+
nodeEditsByKey: {},
|
|
4603
|
+
orderedChanges: [],
|
|
4604
|
+
undoneChanges: [],
|
|
4605
|
+
canUndo: false,
|
|
4606
|
+
canRedo: false
|
|
4607
|
+
};
|
|
4608
|
+
}
|
|
4104
4609
|
var useDesignModeStore = create((set, get) => ({
|
|
4105
4610
|
// UI state
|
|
4106
4611
|
enabled: false,
|
|
@@ -4116,8 +4621,10 @@ var useDesignModeStore = create((set, get) => ({
|
|
|
4116
4621
|
nodeEditsByKey: {},
|
|
4117
4622
|
orderedChanges: [],
|
|
4118
4623
|
undoneChanges: [],
|
|
4624
|
+
// External drag
|
|
4625
|
+
externalDrag: null,
|
|
4119
4626
|
// UI actions
|
|
4120
|
-
toggle: () => set((s) => ({ enabled: !s.enabled })),
|
|
4627
|
+
toggle: () => set((s) => ({ enabled: !s.enabled, ...buildEmptyArrangeSessionState() })),
|
|
4121
4628
|
setEnabled: (enabled) => set({ enabled }),
|
|
4122
4629
|
setHovered: (selector) => set({ hoveredSelector: selector }),
|
|
4123
4630
|
setSelected: (selector) => set({ selectedSelector: selector }),
|
|
@@ -4132,8 +4639,11 @@ var useDesignModeStore = create((set, get) => ({
|
|
|
4132
4639
|
canUndo: false,
|
|
4133
4640
|
canRedo: false
|
|
4134
4641
|
}),
|
|
4642
|
+
resetArrangeSession: () => set(buildEmptyArrangeSessionState()),
|
|
4135
4643
|
// Change tracking actions
|
|
4136
4644
|
recordMoveChange: ({
|
|
4645
|
+
operationId,
|
|
4646
|
+
commandType,
|
|
4137
4647
|
nodeKey,
|
|
4138
4648
|
nodeId,
|
|
4139
4649
|
inspectorRef,
|
|
@@ -4142,13 +4652,24 @@ var useDesignModeStore = create((set, get) => ({
|
|
|
4142
4652
|
selectedInspectorRef,
|
|
4143
4653
|
anchorNodeKey,
|
|
4144
4654
|
anchorNodeId,
|
|
4655
|
+
sourceContainer,
|
|
4656
|
+
sourceBeforeSibling,
|
|
4657
|
+
sourceAfterSibling,
|
|
4658
|
+
container,
|
|
4659
|
+
beforeSibling,
|
|
4660
|
+
afterSibling,
|
|
4661
|
+
diagnostics,
|
|
4145
4662
|
componentName,
|
|
4146
4663
|
before,
|
|
4147
4664
|
after
|
|
4148
4665
|
}) => {
|
|
4149
4666
|
const now = Date.now();
|
|
4667
|
+
const normalizedOperationId = operationId ?? buildFallbackOperationId(now, nodeId);
|
|
4668
|
+
const normalizedCommandType = commandType ?? inferMoveCommandType(before, after);
|
|
4150
4669
|
const event = {
|
|
4151
4670
|
kind: "move",
|
|
4671
|
+
operationId: normalizedOperationId,
|
|
4672
|
+
commandType: normalizedCommandType,
|
|
4152
4673
|
nodeKey,
|
|
4153
4674
|
nodeId,
|
|
4154
4675
|
inspectorRef,
|
|
@@ -4157,6 +4678,13 @@ var useDesignModeStore = create((set, get) => ({
|
|
|
4157
4678
|
selectedInspectorRef,
|
|
4158
4679
|
anchorNodeKey,
|
|
4159
4680
|
anchorNodeId,
|
|
4681
|
+
sourceContainer: sourceContainer ?? null,
|
|
4682
|
+
sourceBeforeSibling: sourceBeforeSibling ?? null,
|
|
4683
|
+
sourceAfterSibling: sourceAfterSibling ?? null,
|
|
4684
|
+
container: container ?? null,
|
|
4685
|
+
beforeSibling: beforeSibling ?? null,
|
|
4686
|
+
afterSibling: afterSibling ?? null,
|
|
4687
|
+
diagnostics: diagnostics ?? null,
|
|
4160
4688
|
payload: { before, after },
|
|
4161
4689
|
at: now
|
|
4162
4690
|
};
|
|
@@ -4179,10 +4707,20 @@ var useDesignModeStore = create((set, get) => ({
|
|
|
4179
4707
|
};
|
|
4180
4708
|
});
|
|
4181
4709
|
},
|
|
4182
|
-
recordResizeChange: ({
|
|
4710
|
+
recordResizeChange: ({
|
|
4711
|
+
operationId,
|
|
4712
|
+
nodeKey,
|
|
4713
|
+
nodeId,
|
|
4714
|
+
inspectorRef,
|
|
4715
|
+
componentName,
|
|
4716
|
+
before,
|
|
4717
|
+
after
|
|
4718
|
+
}) => {
|
|
4183
4719
|
const now = Date.now();
|
|
4720
|
+
const normalizedOperationId = operationId ?? buildFallbackOperationId(now, nodeId);
|
|
4184
4721
|
const event = {
|
|
4185
4722
|
kind: "resize",
|
|
4723
|
+
operationId: normalizedOperationId,
|
|
4186
4724
|
nodeKey,
|
|
4187
4725
|
nodeId,
|
|
4188
4726
|
inspectorRef,
|
|
@@ -4208,6 +4746,52 @@ var useDesignModeStore = create((set, get) => ({
|
|
|
4208
4746
|
};
|
|
4209
4747
|
});
|
|
4210
4748
|
},
|
|
4749
|
+
recordInsertChange: ({
|
|
4750
|
+
operationId,
|
|
4751
|
+
nodeKey,
|
|
4752
|
+
nodeId,
|
|
4753
|
+
inspectorRef,
|
|
4754
|
+
displayName,
|
|
4755
|
+
html,
|
|
4756
|
+
containerId,
|
|
4757
|
+
containerNodeKey,
|
|
4758
|
+
index
|
|
4759
|
+
}) => {
|
|
4760
|
+
const now = Date.now();
|
|
4761
|
+
const normalizedOperationId = operationId ?? buildFallbackOperationId(now, nodeId);
|
|
4762
|
+
const event = {
|
|
4763
|
+
kind: "insert",
|
|
4764
|
+
operationId: normalizedOperationId,
|
|
4765
|
+
nodeKey,
|
|
4766
|
+
nodeId,
|
|
4767
|
+
inspectorRef,
|
|
4768
|
+
displayName,
|
|
4769
|
+
html,
|
|
4770
|
+
containerId,
|
|
4771
|
+
containerNodeKey,
|
|
4772
|
+
index,
|
|
4773
|
+
at: now
|
|
4774
|
+
};
|
|
4775
|
+
set((s) => {
|
|
4776
|
+
const prev = s.nodeEditsByKey[nodeKey];
|
|
4777
|
+
const meta = {
|
|
4778
|
+
nodeKey,
|
|
4779
|
+
nodeId,
|
|
4780
|
+
inspectorRef,
|
|
4781
|
+
latestMove: prev?.latestMove,
|
|
4782
|
+
latestResize: prev?.latestResize,
|
|
4783
|
+
changeCount: (prev?.changeCount ?? 0) + 1,
|
|
4784
|
+
lastUpdatedAt: now
|
|
4785
|
+
};
|
|
4786
|
+
return {
|
|
4787
|
+
orderedChanges: [...s.orderedChanges, event],
|
|
4788
|
+
nodeEditsByKey: { ...s.nodeEditsByKey, [nodeKey]: meta },
|
|
4789
|
+
undoneChanges: []
|
|
4790
|
+
};
|
|
4791
|
+
});
|
|
4792
|
+
},
|
|
4793
|
+
startExternalDrag: (component) => set({ externalDrag: component }),
|
|
4794
|
+
cancelExternalDrag: () => set({ externalDrag: null }),
|
|
4211
4795
|
undoLastChange: () => {
|
|
4212
4796
|
const { orderedChanges, undoneChanges } = get();
|
|
4213
4797
|
if (orderedChanges.length === 0) return null;
|
|
@@ -4233,7 +4817,8 @@ var useDesignModeStore = create((set, get) => ({
|
|
|
4233
4817
|
},
|
|
4234
4818
|
clearChanges: () => set({ orderedChanges: [], undoneChanges: [], nodeEditsByKey: {} }),
|
|
4235
4819
|
exportChangesForAI: () => {
|
|
4236
|
-
const { orderedChanges, nodeEditsByKey } = get();
|
|
4820
|
+
const { orderedChanges: rawChanges, nodeEditsByKey } = get();
|
|
4821
|
+
const orderedChanges = coalesceOrderedChanges(rawChanges);
|
|
4237
4822
|
const changesByFile = {};
|
|
4238
4823
|
for (const event of orderedChanges) {
|
|
4239
4824
|
const fileKey = event.inspectorRef?.relativePath ?? "__runtime__";
|
|
@@ -4247,180 +4832,127 @@ var useDesignModeStore = create((set, get) => ({
|
|
|
4247
4832
|
lines.push(` ${summarizeEditorChangeEvent(ev)}`);
|
|
4248
4833
|
}
|
|
4249
4834
|
}
|
|
4835
|
+
const diagnostics = mergeRuntimeArrangeDiagnostics(
|
|
4836
|
+
orderedChanges.map(
|
|
4837
|
+
(event) => event.kind === "move" ? event.diagnostics : null
|
|
4838
|
+
)
|
|
4839
|
+
);
|
|
4250
4840
|
return {
|
|
4251
4841
|
changesByFile,
|
|
4252
4842
|
finalSnapshot: { ...nodeEditsByKey },
|
|
4253
|
-
aiPromptContext: lines.join("\n")
|
|
4843
|
+
aiPromptContext: lines.join("\n"),
|
|
4844
|
+
...diagnostics ? { diagnostics } : {}
|
|
4254
4845
|
};
|
|
4255
4846
|
}
|
|
4256
4847
|
}));
|
|
4257
4848
|
|
|
4258
4849
|
// src/client/dnd/design-mode/DesignModeOverlay.tsx
|
|
4259
|
-
import { useRef as
|
|
4850
|
+
import { useRef as useRef10, useCallback as useCallback8, useEffect as useEffect12, useLayoutEffect as useLayoutEffect2, useState as useState6, useMemo as useMemo6 } from "react";
|
|
4260
4851
|
|
|
4261
4852
|
// src/client/dnd/design-mode/useElementScanner.ts
|
|
4262
4853
|
import { useState as useState4, useEffect as useEffect4, useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
4263
4854
|
|
|
4264
|
-
// src/client/dnd/design-mode/
|
|
4265
|
-
var
|
|
4266
|
-
var
|
|
4267
|
-
|
|
4268
|
-
|
|
4855
|
+
// src/client/dnd/design-mode/helpers.ts
|
|
4856
|
+
var DND_NODE_ID_ATTR = "data-dnd-node-id";
|
|
4857
|
+
var PAGE_EDGE_GUARD = 10;
|
|
4858
|
+
var HARD_PAGE_EDGE_GUARD = 4;
|
|
4859
|
+
var SAFE_ZONE_INSET = 8;
|
|
4860
|
+
function clamp(value, min, max) {
|
|
4861
|
+
return Math.max(min, Math.min(max, value));
|
|
4269
4862
|
}
|
|
4270
|
-
function
|
|
4271
|
-
|
|
4272
|
-
const tag = el.tagName.toLowerCase();
|
|
4273
|
-
if (STRUCTURAL_TAGS.has(tag)) return true;
|
|
4274
|
-
if (el.id === "root") return true;
|
|
4275
|
-
return tag === "main" && (el.parentElement === document.body || el.parentElement?.id === "root");
|
|
4863
|
+
function buildNodeSelector(nodeId) {
|
|
4864
|
+
return `[${DND_NODE_ID_ATTR}="${escapeCssAttrValue(nodeId)}"]`;
|
|
4276
4865
|
}
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4866
|
+
var LARGE_NODE_NEST_BLOCK_MIN_WIDTH = 520;
|
|
4867
|
+
var LARGE_NODE_NEST_BLOCK_MIN_HEIGHT = 260;
|
|
4868
|
+
var LARGE_NODE_NEST_BLOCK_MIN_AREA = 18e4;
|
|
4869
|
+
function isEligibleForDrag(sc) {
|
|
4870
|
+
const source = sc.eligibilitySource ?? "editor-meta";
|
|
4871
|
+
return source === "editor-meta" || source === "force-allow" || source === "inferred-sortable-descendant";
|
|
4281
4872
|
}
|
|
4282
|
-
function
|
|
4283
|
-
return
|
|
4873
|
+
function isPointerInEdgeZone(px, py, guard) {
|
|
4874
|
+
return px <= guard || py <= guard || px >= window.innerWidth - guard || py >= window.innerHeight - guard;
|
|
4284
4875
|
}
|
|
4285
|
-
function
|
|
4286
|
-
|
|
4287
|
-
if (!container) return false;
|
|
4288
|
-
if (container.children.length < 2) return false;
|
|
4289
|
-
return !isStructuralContainer(containerId, nodeMap);
|
|
4876
|
+
function isPointerInPageEdgeZone(px, py) {
|
|
4877
|
+
return isPointerInEdgeZone(px, py, PAGE_EDGE_GUARD);
|
|
4290
4878
|
}
|
|
4291
|
-
function
|
|
4292
|
-
|
|
4293
|
-
for (const containerId of projection.containerIndex.keys()) {
|
|
4294
|
-
if (containerId === sourceContainerId) continue;
|
|
4295
|
-
if (isStructuralContainer(containerId, nodeMap)) blocked.add(containerId);
|
|
4296
|
-
}
|
|
4297
|
-
return blocked;
|
|
4879
|
+
function isPointerInHardPageEdgeZone(px, py) {
|
|
4880
|
+
return isPointerInEdgeZone(px, py, HARD_PAGE_EDGE_GUARD);
|
|
4298
4881
|
}
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
const
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
}
|
|
4310
|
-
return null;
|
|
4882
|
+
function resolveSafeZoneRect() {
|
|
4883
|
+
const root = document.getElementById("root");
|
|
4884
|
+
if (!root) return null;
|
|
4885
|
+
const rect = root.getBoundingClientRect();
|
|
4886
|
+
const left = rect.left + SAFE_ZONE_INSET;
|
|
4887
|
+
const top = rect.top + SAFE_ZONE_INSET;
|
|
4888
|
+
const right = rect.right - SAFE_ZONE_INSET;
|
|
4889
|
+
const bottom = rect.bottom - SAFE_ZONE_INSET;
|
|
4890
|
+
if (right <= left || bottom <= top) return null;
|
|
4891
|
+
return { left, top, right, bottom };
|
|
4311
4892
|
}
|
|
4312
|
-
function
|
|
4313
|
-
|
|
4893
|
+
function isPointerInSafeZone(px, py, safeZone) {
|
|
4894
|
+
if (!safeZone) return true;
|
|
4895
|
+
return px >= safeZone.left && px <= safeZone.right && py >= safeZone.top && py <= safeZone.bottom;
|
|
4314
4896
|
}
|
|
4315
|
-
function
|
|
4316
|
-
return
|
|
4897
|
+
function isPointerOutsideSafeZone(px, py, safeZone) {
|
|
4898
|
+
return !isPointerInSafeZone(px, py, safeZone);
|
|
4317
4899
|
}
|
|
4318
|
-
function
|
|
4319
|
-
|
|
4320
|
-
return meta.componentName[0] !== meta.componentName[0].toLowerCase();
|
|
4900
|
+
function isPointerBlocked(px, py, safeZone) {
|
|
4901
|
+
return isPointerInHardPageEdgeZone(px, py) || isPointerOutsideSafeZone(px, py, safeZone);
|
|
4321
4902
|
}
|
|
4322
|
-
function
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4903
|
+
function shouldBlockNestForLargeNode(w, h) {
|
|
4904
|
+
return w >= LARGE_NODE_NEST_BLOCK_MIN_WIDTH || h >= LARGE_NODE_NEST_BLOCK_MIN_HEIGHT || w * h >= LARGE_NODE_NEST_BLOCK_MIN_AREA;
|
|
4905
|
+
}
|
|
4906
|
+
function findScrollableAncestor(start, dx, dy) {
|
|
4907
|
+
let el = start;
|
|
4908
|
+
while (el && el !== document.body) {
|
|
4909
|
+
const cs = getComputedStyle(el);
|
|
4910
|
+
if (dy !== 0 && (cs.overflowY === "auto" || cs.overflowY === "scroll") && el.scrollHeight > el.clientHeight + 1) return el;
|
|
4911
|
+
if (dx !== 0 && (cs.overflowX === "auto" || cs.overflowX === "scroll") && el.scrollWidth > el.clientWidth + 1) return el;
|
|
4912
|
+
el = el.parentElement;
|
|
4326
4913
|
}
|
|
4327
|
-
return
|
|
4914
|
+
return null;
|
|
4328
4915
|
}
|
|
4329
|
-
function
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
if (child.hasAttribute("data-design-mode-ui")) return false;
|
|
4333
|
-
if (child.hasAttribute("data-design-mode-allow")) return false;
|
|
4334
|
-
return !shouldIgnoreAsVirtualRuntime(child);
|
|
4335
|
-
});
|
|
4916
|
+
function escapeCssAttrValue(value) {
|
|
4917
|
+
const esc = globalThis.CSS?.escape;
|
|
4918
|
+
return esc ? esc(value) : value.replace(/["\\]/g, "\\$&");
|
|
4336
4919
|
}
|
|
4337
|
-
function
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
if (tag === "script" || tag === "style" || tag === "noscript") return true;
|
|
4341
|
-
if (el instanceof SVGElement && !isRootSvg(el)) return true;
|
|
4342
|
-
return false;
|
|
4920
|
+
function isDesignModeUiElement(el) {
|
|
4921
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
4922
|
+
return el.hasAttribute("data-design-mode-ui") || !!el.closest("[data-design-mode-ui]");
|
|
4343
4923
|
}
|
|
4344
|
-
function
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
const
|
|
4348
|
-
const
|
|
4349
|
-
|
|
4350
|
-
directMetaCache.set(el, resolved);
|
|
4351
|
-
return resolved;
|
|
4924
|
+
function isElementScrollable(el) {
|
|
4925
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
4926
|
+
const cs = getComputedStyle(el);
|
|
4927
|
+
const overY = cs.overflowY;
|
|
4928
|
+
const overX = cs.overflowX;
|
|
4929
|
+
return (overY === "auto" || overY === "scroll") && el.scrollHeight > el.clientHeight || (overX === "auto" || overX === "scroll") && el.scrollWidth > el.clientWidth;
|
|
4352
4930
|
}
|
|
4353
|
-
function
|
|
4354
|
-
const
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
metaCache.set(el, meta);
|
|
4361
|
-
return meta;
|
|
4931
|
+
function resolveScrollableFromHitStack(stack) {
|
|
4932
|
+
for (const hit of stack) {
|
|
4933
|
+
if (isDesignModeUiElement(hit)) continue;
|
|
4934
|
+
let el = hit;
|
|
4935
|
+
while (el) {
|
|
4936
|
+
if (isElementScrollable(el)) return el;
|
|
4937
|
+
el = el.parentElement;
|
|
4362
4938
|
}
|
|
4363
|
-
fiber = fiber.return;
|
|
4364
4939
|
}
|
|
4365
|
-
metaCache.set(el, null);
|
|
4366
4940
|
return null;
|
|
4367
4941
|
}
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
}
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
const ownerMeta = directMeta ?? getEditorMeta(el);
|
|
4380
|
-
if (isPassthroughMeta(ownerMeta)) {
|
|
4381
|
-
const children = getCandidateChildren(el);
|
|
4382
|
-
if (children.length !== 1) return null;
|
|
4383
|
-
return children[0];
|
|
4384
|
-
}
|
|
4385
|
-
if (directMeta && !isWrapperMeta(directMeta)) return el;
|
|
4386
|
-
const MAX_STEPS = 10;
|
|
4387
|
-
const ownerNodeId = ownerMeta?.nodeId ?? null;
|
|
4388
|
-
let outermost = el;
|
|
4389
|
-
let parent = el.parentElement;
|
|
4390
|
-
for (let i = 0; i < MAX_STEPS; i += 1) {
|
|
4391
|
-
if (!parent || parent === document.body) break;
|
|
4392
|
-
if (shouldIgnoreAsVirtualRuntime(parent)) break;
|
|
4393
|
-
const parentMeta = getEditorMeta(parent);
|
|
4394
|
-
if (!parentMeta) break;
|
|
4395
|
-
if (ownerNodeId && parentMeta.nodeId !== ownerNodeId) break;
|
|
4396
|
-
if (isStructuralComponentName(parentMeta.componentName)) break;
|
|
4397
|
-
if (!isWrapperMeta(parentMeta)) break;
|
|
4398
|
-
if (isPassthroughMeta(parentMeta)) break;
|
|
4399
|
-
if (hasOwnTextContent(parent)) break;
|
|
4400
|
-
const children = getCandidateChildren(parent);
|
|
4401
|
-
if (children.length !== 1) break;
|
|
4402
|
-
outermost = parent;
|
|
4403
|
-
parent = parent.parentElement;
|
|
4404
|
-
}
|
|
4405
|
-
return outermost;
|
|
4406
|
-
}
|
|
4407
|
-
function resolveDragSurface(el) {
|
|
4408
|
-
const host = resolveHostElementFromFiberChain(el);
|
|
4409
|
-
if (!host) {
|
|
4410
|
-
return { host: null, metaOwner: null, kind: "direct-host" };
|
|
4411
|
-
}
|
|
4412
|
-
const metaOwner = findMetaOwnerElement(host) ?? findMetaOwnerElement(el);
|
|
4413
|
-
const ownerMeta = metaOwner ? getEditorMeta(metaOwner) : null;
|
|
4414
|
-
const kind = isPassthroughMeta(ownerMeta) ? "passthrough-slot" : metaOwner && metaOwner !== host && isWrapperMeta(ownerMeta) ? "wrapper-to-host" : "direct-host";
|
|
4415
|
-
return {
|
|
4416
|
-
host,
|
|
4417
|
-
metaOwner,
|
|
4418
|
-
kind
|
|
4419
|
-
};
|
|
4942
|
+
var _undoParentNodeIdMap = /* @__PURE__ */ new WeakMap();
|
|
4943
|
+
var _undoParentNodeIdCounter = 0;
|
|
4944
|
+
function ensureParentNodeId(el) {
|
|
4945
|
+
const existing = el.getAttribute(DND_NODE_ID_ATTR);
|
|
4946
|
+
if (existing) return existing;
|
|
4947
|
+
if (!_undoParentNodeIdMap.has(el)) {
|
|
4948
|
+
_undoParentNodeIdMap.set(el, `dnd-parent:${++_undoParentNodeIdCounter}`);
|
|
4949
|
+
}
|
|
4950
|
+
const id = _undoParentNodeIdMap.get(el);
|
|
4951
|
+
el.setAttribute(DND_NODE_ID_ATTR, id);
|
|
4952
|
+
return id;
|
|
4420
4953
|
}
|
|
4421
4954
|
|
|
4422
4955
|
// src/client/dnd/design-mode/useElementScanner.ts
|
|
4423
|
-
var DND_NODE_ID_ATTR = "data-dnd-node-id";
|
|
4424
4956
|
function resolveContainerStrategy(el) {
|
|
4425
4957
|
const cs = getComputedStyle(el);
|
|
4426
4958
|
const d = cs.display;
|
|
@@ -4451,7 +4983,7 @@ function getRuntimeNodeId(el) {
|
|
|
4451
4983
|
}
|
|
4452
4984
|
function getEditorMetaNodeId(el) {
|
|
4453
4985
|
if (!(el instanceof HTMLElement)) return null;
|
|
4454
|
-
return
|
|
4986
|
+
return getDirectEditorMeta(el)?.nodeId ?? null;
|
|
4455
4987
|
}
|
|
4456
4988
|
function isInspectorEligible(el) {
|
|
4457
4989
|
if (getEditorMetaNodeId(el) !== null) return "editor-meta";
|
|
@@ -4475,7 +5007,7 @@ function inferEligibilityFromSortableParent(el) {
|
|
|
4475
5007
|
if (child.hasAttribute("data-design-mode-ui")) return false;
|
|
4476
5008
|
return !isRuntimeVisualInternal(child);
|
|
4477
5009
|
});
|
|
4478
|
-
if (siblingElements.length <
|
|
5010
|
+
if (siblingElements.length < 1) return null;
|
|
4479
5011
|
return "inferred-sortable-descendant";
|
|
4480
5012
|
}
|
|
4481
5013
|
function ensureUniqueNodeId(base, used) {
|
|
@@ -5029,7 +5561,15 @@ function buildProjection(elements, rectOverrides) {
|
|
|
5029
5561
|
}
|
|
5030
5562
|
const containerIndex = /* @__PURE__ */ new Map();
|
|
5031
5563
|
for (const node of nodeMap.values()) {
|
|
5032
|
-
if (node.children.length === 0)
|
|
5564
|
+
if (node.children.length === 0) {
|
|
5565
|
+
const scanInfo = elementIndex.get(node.id);
|
|
5566
|
+
const eligible = scanInfo?.editorMeta || scanInfo?.eligibilitySource === "force-allow";
|
|
5567
|
+
if (!eligible) continue;
|
|
5568
|
+
node.sortable = true;
|
|
5569
|
+
node.strategy = "vertical";
|
|
5570
|
+
containerIndex.set(node.id, node);
|
|
5571
|
+
continue;
|
|
5572
|
+
}
|
|
5033
5573
|
node.sortable = true;
|
|
5034
5574
|
const firstChild = elementIndex.get(node.children[0]);
|
|
5035
5575
|
node.strategy = firstChild?.containerStrategy ?? "vertical";
|
|
@@ -5100,31 +5640,25 @@ function buildVerticalSlotRect(children, nodeMap, cRect, i, n) {
|
|
|
5100
5640
|
const top = i === 0 ? cRect.top : nodeMap.get(children[i - 1])?.rect.bottom ?? cRect.top;
|
|
5101
5641
|
const bottom = i === n ? cRect.bottom : nodeMap.get(children[i])?.rect.top ?? cRect.bottom;
|
|
5102
5642
|
const height = Math.max(0, bottom - top);
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
width: cRect.width,
|
|
5111
|
-
height
|
|
5112
|
-
};
|
|
5643
|
+
const prevRect = i > 0 ? nodeMap.get(children[i - 1])?.rect : null;
|
|
5644
|
+
const nextRect = i < n ? nodeMap.get(children[i])?.rect : null;
|
|
5645
|
+
const refRect = prevRect ?? nextRect;
|
|
5646
|
+
const left = refRect ? refRect.left : cRect.left;
|
|
5647
|
+
const right = refRect ? refRect.right : cRect.right;
|
|
5648
|
+
const width = Math.max(0, right - left);
|
|
5649
|
+
return { x: left, y: top, left, top, right, bottom: top + height, width, height };
|
|
5113
5650
|
}
|
|
5114
5651
|
function buildHorizontalSlotRect(children, nodeMap, cRect, i, n) {
|
|
5115
5652
|
const left = i === 0 ? cRect.left : nodeMap.get(children[i - 1])?.rect.right ?? cRect.left;
|
|
5116
5653
|
const right = i === n ? cRect.right : nodeMap.get(children[i])?.rect.left ?? cRect.right;
|
|
5117
5654
|
const width = Math.max(0, right - left);
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
width,
|
|
5126
|
-
height: cRect.height
|
|
5127
|
-
};
|
|
5655
|
+
const prevRect = i > 0 ? nodeMap.get(children[i - 1])?.rect : null;
|
|
5656
|
+
const nextRect = i < n ? nodeMap.get(children[i])?.rect : null;
|
|
5657
|
+
const refRect = prevRect ?? nextRect;
|
|
5658
|
+
const top = refRect ? refRect.top : cRect.top;
|
|
5659
|
+
const bottom = refRect ? refRect.bottom : cRect.bottom;
|
|
5660
|
+
const height = Math.max(0, bottom - top);
|
|
5661
|
+
return { x: left, y: top, left, top, right: left + width, bottom, width, height };
|
|
5128
5662
|
}
|
|
5129
5663
|
function buildRectSlotRect(children, nodeMap, cRect, i, n, rows) {
|
|
5130
5664
|
const findRow = (childIdx) => rows.find((r) => childIdx >= r.startIdx && childIdx <= r.endIdx) ?? null;
|
|
@@ -5188,6 +5722,79 @@ function buildRectSlotRect(children, nodeMap, cRect, i, n, rows) {
|
|
|
5188
5722
|
};
|
|
5189
5723
|
}
|
|
5190
5724
|
|
|
5725
|
+
// src/client/dnd/design-mode/history.ts
|
|
5726
|
+
function applyInsertCommand(command, direction, resolveNodeElement) {
|
|
5727
|
+
if (direction === "undo") {
|
|
5728
|
+
const el = resolveNodeElement(command.nodeId);
|
|
5729
|
+
if (el?.isConnected) el.remove();
|
|
5730
|
+
return true;
|
|
5731
|
+
}
|
|
5732
|
+
const container = resolveNodeElement(command.containerId);
|
|
5733
|
+
if (!container) return false;
|
|
5734
|
+
const existing = resolveNodeElement(command.nodeId);
|
|
5735
|
+
if (existing?.isConnected) return true;
|
|
5736
|
+
const template = document.createElement("template");
|
|
5737
|
+
template.innerHTML = command.html.trim();
|
|
5738
|
+
const newEl = template.content.firstElementChild;
|
|
5739
|
+
if (!newEl) return false;
|
|
5740
|
+
newEl.setAttribute("data-dnd-node-id", command.nodeId);
|
|
5741
|
+
newEl.setAttribute("data-dnd-force-allow", "");
|
|
5742
|
+
newEl.setAttribute("data-dnd-moved", "");
|
|
5743
|
+
const kids = Array.from(container.children).filter(
|
|
5744
|
+
(c) => c instanceof HTMLElement
|
|
5745
|
+
);
|
|
5746
|
+
container.insertBefore(newEl, kids[command.index] ?? null);
|
|
5747
|
+
return true;
|
|
5748
|
+
}
|
|
5749
|
+
var DEFAULT_LIMIT = 300;
|
|
5750
|
+
var DndCommandHistory = class {
|
|
5751
|
+
constructor(limit = DEFAULT_LIMIT) {
|
|
5752
|
+
this.limit = limit;
|
|
5753
|
+
__publicField(this, "undoStack", []);
|
|
5754
|
+
__publicField(this, "redoStack", []);
|
|
5755
|
+
}
|
|
5756
|
+
execute(command, apply) {
|
|
5757
|
+
const applied = apply(command, "redo");
|
|
5758
|
+
if (!applied) return false;
|
|
5759
|
+
this.undoStack.push(command);
|
|
5760
|
+
if (this.undoStack.length > this.limit) this.undoStack.shift();
|
|
5761
|
+
this.redoStack = [];
|
|
5762
|
+
return true;
|
|
5763
|
+
}
|
|
5764
|
+
undo(apply) {
|
|
5765
|
+
const command = this.undoStack.pop();
|
|
5766
|
+
if (!command) return false;
|
|
5767
|
+
const applied = apply(command, "undo");
|
|
5768
|
+
if (!applied) {
|
|
5769
|
+
this.undoStack.push(command);
|
|
5770
|
+
return false;
|
|
5771
|
+
}
|
|
5772
|
+
this.redoStack.push(command);
|
|
5773
|
+
return true;
|
|
5774
|
+
}
|
|
5775
|
+
redo(apply) {
|
|
5776
|
+
const command = this.redoStack.pop();
|
|
5777
|
+
if (!command) return false;
|
|
5778
|
+
const applied = apply(command, "redo");
|
|
5779
|
+
if (!applied) {
|
|
5780
|
+
this.redoStack.push(command);
|
|
5781
|
+
return false;
|
|
5782
|
+
}
|
|
5783
|
+
this.undoStack.push(command);
|
|
5784
|
+
return true;
|
|
5785
|
+
}
|
|
5786
|
+
clear() {
|
|
5787
|
+
this.undoStack = [];
|
|
5788
|
+
this.redoStack = [];
|
|
5789
|
+
}
|
|
5790
|
+
canUndo() {
|
|
5791
|
+
return this.undoStack.length > 0;
|
|
5792
|
+
}
|
|
5793
|
+
canRedo() {
|
|
5794
|
+
return this.redoStack.length > 0;
|
|
5795
|
+
}
|
|
5796
|
+
};
|
|
5797
|
+
|
|
5191
5798
|
// src/client/dnd/design-mode/resize.ts
|
|
5192
5799
|
function splitTopLevelSpaceSeparated(value) {
|
|
5193
5800
|
const tokens = [];
|
|
@@ -5219,9 +5826,6 @@ function parseSpanValue(value) {
|
|
|
5219
5826
|
const parsed = Number.parseInt(match[1], 10);
|
|
5220
5827
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : 1;
|
|
5221
5828
|
}
|
|
5222
|
-
function clamp(value, min, max) {
|
|
5223
|
-
return Math.max(min, Math.min(max, value));
|
|
5224
|
-
}
|
|
5225
5829
|
function resolveResizeTargetElement(selectedEl) {
|
|
5226
5830
|
const elementChildren = Array.from(selectedEl.children).filter(
|
|
5227
5831
|
(child2) => child2 instanceof HTMLElement
|
|
@@ -5302,100 +5906,6 @@ function applyStyleSnapshot(el, snapshot) {
|
|
|
5302
5906
|
}
|
|
5303
5907
|
}
|
|
5304
5908
|
|
|
5305
|
-
// src/client/dnd/design-mode/helpers.ts
|
|
5306
|
-
var DND_NODE_ID_ATTR2 = "data-dnd-node-id";
|
|
5307
|
-
var PAGE_EDGE_GUARD = 10;
|
|
5308
|
-
var HARD_PAGE_EDGE_GUARD = 4;
|
|
5309
|
-
var SAFE_ZONE_INSET = 8;
|
|
5310
|
-
var LARGE_NODE_NEST_BLOCK_MIN_WIDTH = 520;
|
|
5311
|
-
var LARGE_NODE_NEST_BLOCK_MIN_HEIGHT = 260;
|
|
5312
|
-
var LARGE_NODE_NEST_BLOCK_MIN_AREA = 18e4;
|
|
5313
|
-
function isEligibleForDrag(sc) {
|
|
5314
|
-
const source = sc.eligibilitySource ?? "editor-meta";
|
|
5315
|
-
return source === "editor-meta" || source === "force-allow" || source === "inferred-sortable-descendant";
|
|
5316
|
-
}
|
|
5317
|
-
function isPointerInPageEdgeZone(px, py) {
|
|
5318
|
-
return px <= PAGE_EDGE_GUARD || py <= PAGE_EDGE_GUARD || px >= window.innerWidth - PAGE_EDGE_GUARD || py >= window.innerHeight - PAGE_EDGE_GUARD;
|
|
5319
|
-
}
|
|
5320
|
-
function isPointerInHardPageEdgeZone(px, py) {
|
|
5321
|
-
return px <= HARD_PAGE_EDGE_GUARD || py <= HARD_PAGE_EDGE_GUARD || px >= window.innerWidth - HARD_PAGE_EDGE_GUARD || py >= window.innerHeight - HARD_PAGE_EDGE_GUARD;
|
|
5322
|
-
}
|
|
5323
|
-
function resolveSafeZoneRect() {
|
|
5324
|
-
const root = document.getElementById("root");
|
|
5325
|
-
if (!root) return null;
|
|
5326
|
-
const rect = root.getBoundingClientRect();
|
|
5327
|
-
const left = rect.left + SAFE_ZONE_INSET;
|
|
5328
|
-
const top = rect.top + SAFE_ZONE_INSET;
|
|
5329
|
-
const right = rect.right - SAFE_ZONE_INSET;
|
|
5330
|
-
const bottom = rect.bottom - SAFE_ZONE_INSET;
|
|
5331
|
-
if (right <= left || bottom <= top) return null;
|
|
5332
|
-
return { left, top, right, bottom };
|
|
5333
|
-
}
|
|
5334
|
-
function isPointerInSafeZone(px, py, safeZone) {
|
|
5335
|
-
if (!safeZone) return true;
|
|
5336
|
-
return px >= safeZone.left && px <= safeZone.right && py >= safeZone.top && py <= safeZone.bottom;
|
|
5337
|
-
}
|
|
5338
|
-
function isPointerOutsideSafeZone(px, py, safeZone) {
|
|
5339
|
-
return !isPointerInSafeZone(px, py, safeZone);
|
|
5340
|
-
}
|
|
5341
|
-
function isPointerBlocked(px, py, safeZone) {
|
|
5342
|
-
return isPointerInHardPageEdgeZone(px, py) || isPointerOutsideSafeZone(px, py, safeZone);
|
|
5343
|
-
}
|
|
5344
|
-
function shouldBlockNestForLargeNode(w, h) {
|
|
5345
|
-
return w >= LARGE_NODE_NEST_BLOCK_MIN_WIDTH || h >= LARGE_NODE_NEST_BLOCK_MIN_HEIGHT || w * h >= LARGE_NODE_NEST_BLOCK_MIN_AREA;
|
|
5346
|
-
}
|
|
5347
|
-
function findScrollableAncestor(start, dx, dy) {
|
|
5348
|
-
let el = start;
|
|
5349
|
-
while (el && el !== document.body) {
|
|
5350
|
-
const cs = getComputedStyle(el);
|
|
5351
|
-
if (dy !== 0 && (cs.overflowY === "auto" || cs.overflowY === "scroll") && el.scrollHeight > el.clientHeight + 1) return el;
|
|
5352
|
-
if (dx !== 0 && (cs.overflowX === "auto" || cs.overflowX === "scroll") && el.scrollWidth > el.clientWidth + 1) return el;
|
|
5353
|
-
el = el.parentElement;
|
|
5354
|
-
}
|
|
5355
|
-
return null;
|
|
5356
|
-
}
|
|
5357
|
-
function escapeCssAttrValue(value) {
|
|
5358
|
-
const esc = globalThis.CSS?.escape;
|
|
5359
|
-
return esc ? esc(value) : value.replace(/["\\]/g, "\\$&");
|
|
5360
|
-
}
|
|
5361
|
-
function isDesignModeUiElement(el) {
|
|
5362
|
-
if (!(el instanceof HTMLElement)) return false;
|
|
5363
|
-
return el.hasAttribute("data-design-mode-ui") || !!el.closest("[data-design-mode-ui]");
|
|
5364
|
-
}
|
|
5365
|
-
function isElementScrollable(el) {
|
|
5366
|
-
if (!(el instanceof HTMLElement)) return false;
|
|
5367
|
-
const cs = getComputedStyle(el);
|
|
5368
|
-
const overY = cs.overflowY;
|
|
5369
|
-
const overX = cs.overflowX;
|
|
5370
|
-
return (overY === "auto" || overY === "scroll") && el.scrollHeight > el.clientHeight || (overX === "auto" || overX === "scroll") && el.scrollWidth > el.clientWidth;
|
|
5371
|
-
}
|
|
5372
|
-
function resolveScrollableFromHitStack(stack) {
|
|
5373
|
-
for (const hit of stack) {
|
|
5374
|
-
if (isDesignModeUiElement(hit)) continue;
|
|
5375
|
-
let el = hit;
|
|
5376
|
-
while (el) {
|
|
5377
|
-
if (isElementScrollable(el)) return el;
|
|
5378
|
-
el = el.parentElement;
|
|
5379
|
-
}
|
|
5380
|
-
}
|
|
5381
|
-
return null;
|
|
5382
|
-
}
|
|
5383
|
-
var _undoParentNodeIdMap = /* @__PURE__ */ new WeakMap();
|
|
5384
|
-
var _undoParentNodeIdCounter = 0;
|
|
5385
|
-
function ensureParentNodeId(el) {
|
|
5386
|
-
const existing = el.getAttribute(DND_NODE_ID_ATTR2);
|
|
5387
|
-
if (existing) return existing;
|
|
5388
|
-
if (!_undoParentNodeIdMap.has(el)) {
|
|
5389
|
-
_undoParentNodeIdMap.set(el, `dnd-parent:${++_undoParentNodeIdCounter}`);
|
|
5390
|
-
}
|
|
5391
|
-
const id = _undoParentNodeIdMap.get(el);
|
|
5392
|
-
el.setAttribute(DND_NODE_ID_ATTR2, id);
|
|
5393
|
-
return id;
|
|
5394
|
-
}
|
|
5395
|
-
function ensureElementNodeId(el) {
|
|
5396
|
-
return ensureParentNodeId(el);
|
|
5397
|
-
}
|
|
5398
|
-
|
|
5399
5909
|
// src/client/dnd/design-mode/drag-preview.tsx
|
|
5400
5910
|
import { useRef as useRef7, useEffect as useEffect6 } from "react";
|
|
5401
5911
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
@@ -5496,7 +6006,7 @@ function findNearestContainerId(nodeId, projection, nodeMap, allowStructuralFall
|
|
|
5496
6006
|
const parentId = node?.parentId ?? null;
|
|
5497
6007
|
if (!parentId) break;
|
|
5498
6008
|
const parentContainer = projection.containerIndex.get(parentId);
|
|
5499
|
-
if (parentContainer && parentContainer.children.length >=
|
|
6009
|
+
if (parentContainer && parentContainer.children.length >= 1) {
|
|
5500
6010
|
if (isValidLaneContainer(parentId, projection, nodeMap)) return parentId;
|
|
5501
6011
|
if (allowStructuralFallback && !structuralFallback) structuralFallback = parentId;
|
|
5502
6012
|
}
|
|
@@ -5581,12 +6091,6 @@ function findDirectChildUnderParent(child, parent) {
|
|
|
5581
6091
|
}
|
|
5582
6092
|
return null;
|
|
5583
6093
|
}
|
|
5584
|
-
function isContiguous(indices) {
|
|
5585
|
-
if (indices.length === 0) return false;
|
|
5586
|
-
const min = Math.min(...indices);
|
|
5587
|
-
const max = Math.max(...indices);
|
|
5588
|
-
return max - min + 1 === indices.length;
|
|
5589
|
-
}
|
|
5590
6094
|
function createPlacement(parentNodeId, laneContainerId, commitNodeId, beforeNodeId, afterNodeId, index) {
|
|
5591
6095
|
return {
|
|
5592
6096
|
parentNodeId,
|
|
@@ -5600,7 +6104,16 @@ function createPlacement(parentNodeId, laneContainerId, commitNodeId, beforeNode
|
|
|
5600
6104
|
function buildCommitLaneSnapshot(laneContainerId, projection, nodeMap) {
|
|
5601
6105
|
const lane = projection.containerIndex.get(laneContainerId);
|
|
5602
6106
|
const laneElement = nodeMap.get(laneContainerId)?.element ?? null;
|
|
5603
|
-
if (!lane ||
|
|
6107
|
+
if (!lane || !laneElement) return null;
|
|
6108
|
+
if (lane.children.length === 0) {
|
|
6109
|
+
return {
|
|
6110
|
+
laneContainerId,
|
|
6111
|
+
commitParentNodeId: ensureParentNodeId(laneElement),
|
|
6112
|
+
orderedCommitNodeIds: [],
|
|
6113
|
+
commitNodeByAnchorId: /* @__PURE__ */ new Map(),
|
|
6114
|
+
committable: true
|
|
6115
|
+
};
|
|
6116
|
+
}
|
|
5604
6117
|
const childElements = lane.children.map((childId) => nodeMap.get(childId)?.element ?? null).filter((el) => !!el);
|
|
5605
6118
|
if (childElements.length !== lane.children.length) return null;
|
|
5606
6119
|
const commitParent = findLowestCommonAncestor(childElements, laneElement);
|
|
@@ -5617,19 +6130,18 @@ function buildCommitLaneSnapshot(laneContainerId, projection, nodeMap) {
|
|
|
5617
6130
|
if (seenRoots.has(commitRoot)) return null;
|
|
5618
6131
|
seenRoots.add(commitRoot);
|
|
5619
6132
|
commitRoots.push(commitRoot);
|
|
5620
|
-
commitNodeByAnchorId.set(anchorNodeId,
|
|
6133
|
+
commitNodeByAnchorId.set(anchorNodeId, ensureParentNodeId(commitRoot));
|
|
5621
6134
|
}
|
|
5622
6135
|
const childList = Array.from(commitParent.children).filter(
|
|
5623
6136
|
(child) => child instanceof HTMLElement
|
|
5624
6137
|
);
|
|
5625
6138
|
const commitRootIndices = commitRoots.map((root) => childList.indexOf(root));
|
|
5626
6139
|
if (commitRootIndices.some((index) => index < 0)) return null;
|
|
5627
|
-
if (!isContiguous(commitRootIndices)) return null;
|
|
5628
6140
|
const domOrderedRoots = [...commitRoots].sort(
|
|
5629
6141
|
(a, b) => childList.indexOf(a) - childList.indexOf(b)
|
|
5630
6142
|
);
|
|
5631
|
-
const projectionOrderedIds = commitRoots.map((root) =>
|
|
5632
|
-
const domOrderedIds = domOrderedRoots.map((root) =>
|
|
6143
|
+
const projectionOrderedIds = commitRoots.map((root) => ensureParentNodeId(root));
|
|
6144
|
+
const domOrderedIds = domOrderedRoots.map((root) => ensureParentNodeId(root));
|
|
5633
6145
|
if (projectionOrderedIds.length !== domOrderedIds.length) return null;
|
|
5634
6146
|
if (projectionOrderedIds.some((id, index) => id !== domOrderedIds[index])) return null;
|
|
5635
6147
|
return {
|
|
@@ -5674,7 +6186,7 @@ function resolveAppendCommitPlacement(parentEl, commitNodeId, laneContainerId) {
|
|
|
5674
6186
|
const children = Array.from(parentEl.children).filter(
|
|
5675
6187
|
(child) => child instanceof HTMLElement
|
|
5676
6188
|
);
|
|
5677
|
-
const lastChild = children.length > 0 ?
|
|
6189
|
+
const lastChild = children.length > 0 ? ensureParentNodeId(children[children.length - 1]) : null;
|
|
5678
6190
|
return createPlacement(
|
|
5679
6191
|
ensureParentNodeId(parentEl),
|
|
5680
6192
|
laneContainerId,
|
|
@@ -5729,6 +6241,58 @@ function createDesignModeCollisionDetection(blockedContainerIds) {
|
|
|
5729
6241
|
};
|
|
5730
6242
|
}
|
|
5731
6243
|
|
|
6244
|
+
// src/client/dnd/design-mode/arrange-session.ts
|
|
6245
|
+
function serializeArrangeChange(event, index) {
|
|
6246
|
+
return {
|
|
6247
|
+
id: `${event.kind}:${event.operationId}:${index}`,
|
|
6248
|
+
kind: event.kind,
|
|
6249
|
+
operationId: event.operationId,
|
|
6250
|
+
commandType: event.kind === "move" ? event.commandType : void 0,
|
|
6251
|
+
nodeKey: event.nodeKey,
|
|
6252
|
+
nodeId: event.nodeId,
|
|
6253
|
+
inspectorRef: event.inspectorRef,
|
|
6254
|
+
selectedNodeKey: event.kind === "move" ? event.selectedNodeKey : void 0,
|
|
6255
|
+
selectedNodeId: event.kind === "move" ? event.selectedNodeId : void 0,
|
|
6256
|
+
selectedInspectorRef: event.kind === "move" ? event.selectedInspectorRef : void 0,
|
|
6257
|
+
anchorNodeKey: event.kind === "move" ? event.anchorNodeKey : void 0,
|
|
6258
|
+
anchorNodeId: event.kind === "move" ? event.anchorNodeId : void 0,
|
|
6259
|
+
container: event.kind === "move" ? event.container : void 0,
|
|
6260
|
+
beforeSibling: event.kind === "move" ? event.beforeSibling : void 0,
|
|
6261
|
+
afterSibling: event.kind === "move" ? event.afterSibling : void 0,
|
|
6262
|
+
sourceContainer: event.kind === "move" ? event.sourceContainer : void 0,
|
|
6263
|
+
sourceBeforeSibling: event.kind === "move" ? event.sourceBeforeSibling : void 0,
|
|
6264
|
+
sourceAfterSibling: event.kind === "move" ? event.sourceAfterSibling : void 0,
|
|
6265
|
+
diagnostics: event.kind === "move" ? event.diagnostics : void 0,
|
|
6266
|
+
// Insert-specific fields
|
|
6267
|
+
displayName: event.kind === "insert" ? event.displayName : void 0,
|
|
6268
|
+
html: event.kind === "insert" ? event.html : void 0,
|
|
6269
|
+
containerId: event.kind === "insert" ? event.containerId : void 0,
|
|
6270
|
+
containerNodeKey: event.kind === "insert" ? event.containerNodeKey : void 0,
|
|
6271
|
+
insertIndex: event.kind === "insert" ? event.index : void 0,
|
|
6272
|
+
summary: summarizeEditorChangeEvent(event),
|
|
6273
|
+
at: event.at
|
|
6274
|
+
};
|
|
6275
|
+
}
|
|
6276
|
+
function buildArrangeSessionSnapshot(params) {
|
|
6277
|
+
const { enabled, aiOutput } = params;
|
|
6278
|
+
const coalescedChanges = coalesceOrderedChanges(params.orderedChanges);
|
|
6279
|
+
const updatedAt = typeof params.updatedAt === "number" ? params.updatedAt : Date.now();
|
|
6280
|
+
return {
|
|
6281
|
+
mode: enabled ? "arrange" : "off",
|
|
6282
|
+
canUndo: params.orderedChanges.length > 0,
|
|
6283
|
+
changes: coalescedChanges.map(
|
|
6284
|
+
(event, index) => serializeArrangeChange(event, index)
|
|
6285
|
+
),
|
|
6286
|
+
diagnostics: aiOutput.diagnostics ?? null,
|
|
6287
|
+
output: {
|
|
6288
|
+
json: aiOutput,
|
|
6289
|
+
prompt: aiOutput.aiPromptContext,
|
|
6290
|
+
summary: coalescedChanges.length === 0 ? "No arrange changes recorded yet." : `${coalescedChanges.length} arrange change${coalescedChanges.length === 1 ? "" : "s"} recorded.`
|
|
6291
|
+
},
|
|
6292
|
+
updatedAt
|
|
6293
|
+
};
|
|
6294
|
+
}
|
|
6295
|
+
|
|
5732
6296
|
// src/client/dnd/design-mode/useDesignModeAutoScroll.ts
|
|
5733
6297
|
import { useEffect as useEffect7 } from "react";
|
|
5734
6298
|
function edgeVelocity(distanceToEdge, zone, maxSpeed) {
|
|
@@ -5744,11 +6308,16 @@ function findScrollableAtPoint(x, y) {
|
|
|
5744
6308
|
return resolveScrollableFromHitStack(stack) ?? (document.scrollingElement ?? document.documentElement);
|
|
5745
6309
|
}
|
|
5746
6310
|
function getScrollerKey(el) {
|
|
5747
|
-
return el.getAttribute?.(
|
|
6311
|
+
return el.getAttribute?.(DND_NODE_ID_ATTR) ?? el.id ?? el.tagName.toLowerCase();
|
|
5748
6312
|
}
|
|
5749
6313
|
function pointerInsideRect(x, y, rect, inset = 0) {
|
|
5750
6314
|
return x >= rect.left - inset && x <= rect.right + inset && y >= rect.top - inset && y <= rect.bottom + inset;
|
|
5751
6315
|
}
|
|
6316
|
+
var AUTO_SCROLL_ZONE_PX = 120;
|
|
6317
|
+
var AUTO_SCROLL_MAX_SPEED = 24;
|
|
6318
|
+
var SCROLLER_RECT_RECALC_INTERVAL = 8;
|
|
6319
|
+
var SCROLLER_SWITCH_FRAMES = 3;
|
|
6320
|
+
var SCROLLER_EDGE_HYSTERESIS_PX = 18;
|
|
5752
6321
|
function useDesignModeAutoScroll({
|
|
5753
6322
|
active,
|
|
5754
6323
|
mouseRef,
|
|
@@ -5757,11 +6326,6 @@ function useDesignModeAutoScroll({
|
|
|
5757
6326
|
}) {
|
|
5758
6327
|
useEffect7(() => {
|
|
5759
6328
|
if (!active) return;
|
|
5760
|
-
const ZONE = 120;
|
|
5761
|
-
const MAX = 24;
|
|
5762
|
-
const RECALC_INTERVAL = 8;
|
|
5763
|
-
const SWITCH_FRAMES = 3;
|
|
5764
|
-
const SCROLLER_EDGE_HYSTERESIS = 18;
|
|
5765
6329
|
let scrollEl = findScrollableAtPoint(mouseRef.current.x, mouseRef.current.y);
|
|
5766
6330
|
let cachedRect = scrollEl.getBoundingClientRect();
|
|
5767
6331
|
let rectAge = 0;
|
|
@@ -5769,17 +6333,17 @@ function useDesignModeAutoScroll({
|
|
|
5769
6333
|
const tick = () => {
|
|
5770
6334
|
const { x, y } = mouseRef.current;
|
|
5771
6335
|
const safeZone = safeZoneRef.current;
|
|
5772
|
-
if (++rectAge >
|
|
6336
|
+
if (++rectAge > SCROLLER_RECT_RECALC_INTERVAL) {
|
|
5773
6337
|
const candidate = findScrollableAtPoint(x, y);
|
|
5774
6338
|
const candidateKey = getScrollerKey(candidate);
|
|
5775
6339
|
const currentKey = getScrollerKey(scrollEl);
|
|
5776
6340
|
const currentRect = scrollEl.getBoundingClientRect();
|
|
5777
|
-
if (candidateKey !== currentKey && !pointerInsideRect(x, y, currentRect,
|
|
6341
|
+
if (candidateKey !== currentKey && !pointerInsideRect(x, y, currentRect, SCROLLER_EDGE_HYSTERESIS_PX)) {
|
|
5778
6342
|
const pendingKey = autoScrollStateRef.current.pendingScrollerKey;
|
|
5779
6343
|
const pendingFrames = pendingKey === candidateKey ? (autoScrollStateRef.current.pendingFrames ?? 0) + 1 : 1;
|
|
5780
6344
|
autoScrollStateRef.current.pendingScrollerKey = candidateKey;
|
|
5781
6345
|
autoScrollStateRef.current.pendingFrames = pendingFrames;
|
|
5782
|
-
if (pendingFrames >=
|
|
6346
|
+
if (pendingFrames >= SCROLLER_SWITCH_FRAMES) {
|
|
5783
6347
|
scrollEl = candidate;
|
|
5784
6348
|
cachedRect = candidate.getBoundingClientRect();
|
|
5785
6349
|
autoScrollStateRef.current.pendingScrollerKey = null;
|
|
@@ -5800,86 +6364,40 @@ function useDesignModeAutoScroll({
|
|
|
5800
6364
|
vx: 0,
|
|
5801
6365
|
vy: 0
|
|
5802
6366
|
};
|
|
5803
|
-
raf = requestAnimationFrame(tick);
|
|
5804
|
-
return;
|
|
5805
|
-
}
|
|
5806
|
-
const dTop = y - cachedRect.top;
|
|
5807
|
-
const dBot = cachedRect.bottom - y;
|
|
5808
|
-
let dy = 0;
|
|
5809
|
-
if (dTop > 0 && dTop <
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
if (
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
}
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
constructor(limit = DEFAULT_LIMIT) {
|
|
5838
|
-
this.limit = limit;
|
|
5839
|
-
__publicField(this, "undoStack", []);
|
|
5840
|
-
__publicField(this, "redoStack", []);
|
|
5841
|
-
}
|
|
5842
|
-
execute(command, apply) {
|
|
5843
|
-
const applied = apply(command, "redo");
|
|
5844
|
-
if (!applied) return false;
|
|
5845
|
-
this.undoStack.push(command);
|
|
5846
|
-
if (this.undoStack.length > this.limit) this.undoStack.shift();
|
|
5847
|
-
this.redoStack = [];
|
|
5848
|
-
return true;
|
|
5849
|
-
}
|
|
5850
|
-
undo(apply) {
|
|
5851
|
-
const command = this.undoStack.pop();
|
|
5852
|
-
if (!command) return false;
|
|
5853
|
-
const applied = apply(command, "undo");
|
|
5854
|
-
if (!applied) {
|
|
5855
|
-
this.undoStack.push(command);
|
|
5856
|
-
return false;
|
|
5857
|
-
}
|
|
5858
|
-
this.redoStack.push(command);
|
|
5859
|
-
return true;
|
|
5860
|
-
}
|
|
5861
|
-
redo(apply) {
|
|
5862
|
-
const command = this.redoStack.pop();
|
|
5863
|
-
if (!command) return false;
|
|
5864
|
-
const applied = apply(command, "redo");
|
|
5865
|
-
if (!applied) {
|
|
5866
|
-
this.redoStack.push(command);
|
|
5867
|
-
return false;
|
|
5868
|
-
}
|
|
5869
|
-
this.undoStack.push(command);
|
|
5870
|
-
return true;
|
|
5871
|
-
}
|
|
5872
|
-
clear() {
|
|
5873
|
-
this.undoStack = [];
|
|
5874
|
-
this.redoStack = [];
|
|
5875
|
-
}
|
|
5876
|
-
canUndo() {
|
|
5877
|
-
return this.undoStack.length > 0;
|
|
5878
|
-
}
|
|
5879
|
-
canRedo() {
|
|
5880
|
-
return this.redoStack.length > 0;
|
|
5881
|
-
}
|
|
5882
|
-
};
|
|
6367
|
+
raf = requestAnimationFrame(tick);
|
|
6368
|
+
return;
|
|
6369
|
+
}
|
|
6370
|
+
const dTop = y - cachedRect.top;
|
|
6371
|
+
const dBot = cachedRect.bottom - y;
|
|
6372
|
+
let dy = 0;
|
|
6373
|
+
if (dTop > 0 && dTop < AUTO_SCROLL_ZONE_PX)
|
|
6374
|
+
dy = -edgeVelocity(dTop, AUTO_SCROLL_ZONE_PX, AUTO_SCROLL_MAX_SPEED);
|
|
6375
|
+
else if (dBot > 0 && dBot < AUTO_SCROLL_ZONE_PX)
|
|
6376
|
+
dy = edgeVelocity(dBot, AUTO_SCROLL_ZONE_PX, AUTO_SCROLL_MAX_SPEED);
|
|
6377
|
+
const dLeft = x - cachedRect.left;
|
|
6378
|
+
const dRight = cachedRect.right - x;
|
|
6379
|
+
let dx = 0;
|
|
6380
|
+
if (dLeft > 0 && dLeft < AUTO_SCROLL_ZONE_PX)
|
|
6381
|
+
dx = -edgeVelocity(dLeft, AUTO_SCROLL_ZONE_PX, AUTO_SCROLL_MAX_SPEED);
|
|
6382
|
+
else if (dRight > 0 && dRight < AUTO_SCROLL_ZONE_PX)
|
|
6383
|
+
dx = edgeVelocity(dRight, AUTO_SCROLL_ZONE_PX, AUTO_SCROLL_MAX_SPEED);
|
|
6384
|
+
if (dy !== 0) scrollEl.scrollTop += dy;
|
|
6385
|
+
if (dx !== 0) scrollEl.scrollLeft += dx;
|
|
6386
|
+
autoScrollStateRef.current = {
|
|
6387
|
+
...autoScrollStateRef.current,
|
|
6388
|
+
scrollerKey: getScrollerKey(scrollEl),
|
|
6389
|
+
vx: dx,
|
|
6390
|
+
vy: dy
|
|
6391
|
+
};
|
|
6392
|
+
raf = requestAnimationFrame(tick);
|
|
6393
|
+
};
|
|
6394
|
+
raf = requestAnimationFrame(tick);
|
|
6395
|
+
return () => cancelAnimationFrame(raf);
|
|
6396
|
+
}, [active, autoScrollStateRef, mouseRef, safeZoneRef]);
|
|
6397
|
+
}
|
|
6398
|
+
|
|
6399
|
+
// src/client/dnd/design-mode/useOverlayRefs.ts
|
|
6400
|
+
import { useRef as useRef8, useMemo as useMemo5 } from "react";
|
|
5883
6401
|
|
|
5884
6402
|
// src/client/dnd/design-mode/sortable-preview-animator.ts
|
|
5885
6403
|
function isIdentityTransform(transform) {
|
|
@@ -6093,43 +6611,6 @@ function useHoverDetection({
|
|
|
6093
6611
|
// src/client/dnd/design-mode/useDragVisualLoop.ts
|
|
6094
6612
|
import { useEffect as useEffect9 } from "react";
|
|
6095
6613
|
|
|
6096
|
-
// src/client/dnd/design-mode/drag-lock.ts
|
|
6097
|
-
var MIN_UNLOCK_MARGIN = 32;
|
|
6098
|
-
var MAX_UNLOCK_MARGIN = 72;
|
|
6099
|
-
var MIN_UNLOCK_DWELL_MS = 170;
|
|
6100
|
-
var MAX_UNLOCK_DWELL_MS = 300;
|
|
6101
|
-
function clamp2(value, min, max) {
|
|
6102
|
-
return Math.min(max, Math.max(min, value));
|
|
6103
|
-
}
|
|
6104
|
-
function resolveCrossUnlockParams(rect) {
|
|
6105
|
-
const width = Math.max(0, rect.right - rect.left);
|
|
6106
|
-
const height = Math.max(0, rect.bottom - rect.top);
|
|
6107
|
-
const shortest = Math.max(1, Math.min(width, height));
|
|
6108
|
-
const margin = Math.round(clamp2(shortest * 0.14, MIN_UNLOCK_MARGIN, MAX_UNLOCK_MARGIN));
|
|
6109
|
-
const dwellMs = Math.round(clamp2(shortest * 0.7, MIN_UNLOCK_DWELL_MS, MAX_UNLOCK_DWELL_MS));
|
|
6110
|
-
return { margin, dwellMs };
|
|
6111
|
-
}
|
|
6112
|
-
function checkCrossUnlock(lock, adjustedPx, adjustedPy, lockRect, now, margin, dwellMs, graceMs) {
|
|
6113
|
-
if (lock.isCrossUnlocked) {
|
|
6114
|
-
if (lock.unlockUntilTs !== null && now < lock.unlockUntilTs) return lock;
|
|
6115
|
-
return lock;
|
|
6116
|
-
}
|
|
6117
|
-
const outsideBand = adjustedPx < lockRect.left - margin || adjustedPx > lockRect.right + margin || adjustedPy < lockRect.top - margin || adjustedPy > lockRect.bottom + margin;
|
|
6118
|
-
if (!outsideBand) {
|
|
6119
|
-
return lock.exitSince !== null ? { ...lock, exitSince: null } : lock;
|
|
6120
|
-
}
|
|
6121
|
-
const exitSince = lock.exitSince ?? now;
|
|
6122
|
-
if (now - exitSince >= dwellMs) {
|
|
6123
|
-
return {
|
|
6124
|
-
containerId: lock.containerId,
|
|
6125
|
-
isCrossUnlocked: true,
|
|
6126
|
-
exitSince,
|
|
6127
|
-
unlockUntilTs: now + graceMs
|
|
6128
|
-
};
|
|
6129
|
-
}
|
|
6130
|
-
return lock.exitSince !== null ? lock : { ...lock, exitSince: now };
|
|
6131
|
-
}
|
|
6132
|
-
|
|
6133
6614
|
// src/client/dnd/design-mode/quadtree.ts
|
|
6134
6615
|
function overlaps(a, b) {
|
|
6135
6616
|
return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
|
|
@@ -6139,6 +6620,7 @@ function fitsInside(outer, inner) {
|
|
|
6139
6620
|
}
|
|
6140
6621
|
var CAPACITY = 8;
|
|
6141
6622
|
var MAX_DEPTH2 = 8;
|
|
6623
|
+
var ROOT_BOUNDS_PAD = 100;
|
|
6142
6624
|
var QTNode = class _QTNode {
|
|
6143
6625
|
constructor(bounds, depth = 0) {
|
|
6144
6626
|
__publicField(this, "bounds");
|
|
@@ -6236,12 +6718,11 @@ var Quadtree = class _Quadtree {
|
|
|
6236
6718
|
if (r.right > maxX) maxX = r.right;
|
|
6237
6719
|
if (r.bottom > maxY) maxY = r.bottom;
|
|
6238
6720
|
}
|
|
6239
|
-
const PAD = 100;
|
|
6240
6721
|
const root = new QTNode({
|
|
6241
|
-
x: minX -
|
|
6242
|
-
y: minY -
|
|
6243
|
-
w: maxX - minX + 2 *
|
|
6244
|
-
h: maxY - minY + 2 *
|
|
6722
|
+
x: minX - ROOT_BOUNDS_PAD,
|
|
6723
|
+
y: minY - ROOT_BOUNDS_PAD,
|
|
6724
|
+
w: maxX - minX + 2 * ROOT_BOUNDS_PAD,
|
|
6725
|
+
h: maxY - minY + 2 * ROOT_BOUNDS_PAD
|
|
6245
6726
|
});
|
|
6246
6727
|
for (const [id, r] of rects) {
|
|
6247
6728
|
if (r.width > 0 && r.height > 0) {
|
|
@@ -6343,9 +6824,9 @@ function getSlotQueryRadius() {
|
|
|
6343
6824
|
const vh = window.innerHeight;
|
|
6344
6825
|
return Math.min(500, Math.max(100, Math.max(vw, vh) * 0.2));
|
|
6345
6826
|
}
|
|
6346
|
-
var NEST_EDGE_FRACTION = 0.
|
|
6347
|
-
var ORIGIN_RETURN_BAND_PX =
|
|
6348
|
-
var LAST_CONTAINER_REENTRY_SLOP_PX =
|
|
6827
|
+
var NEST_EDGE_FRACTION = 0.2;
|
|
6828
|
+
var ORIGIN_RETURN_BAND_PX = 24;
|
|
6829
|
+
var LAST_CONTAINER_REENTRY_SLOP_PX = 16;
|
|
6349
6830
|
var ROW_OVERLAP_TOLERANCE_PX = 1;
|
|
6350
6831
|
function normalizeContainerSet(containers) {
|
|
6351
6832
|
if (!containers) return null;
|
|
@@ -6423,8 +6904,6 @@ var DragIntentEngine = class {
|
|
|
6423
6904
|
// Pre-computed set of slot IDs that would produce no movement for the active item.
|
|
6424
6905
|
__publicField(this, "noOpSlotIds");
|
|
6425
6906
|
__publicField(this, "originContainerId");
|
|
6426
|
-
__publicField(this, "laneRootContainerId");
|
|
6427
|
-
__publicField(this, "laneParentId");
|
|
6428
6907
|
__publicField(this, "laneAllowedContainers");
|
|
6429
6908
|
__publicField(this, "blockedContainerIds");
|
|
6430
6909
|
__publicField(this, "targetPlane", "lane");
|
|
@@ -6436,8 +6915,6 @@ var DragIntentEngine = class {
|
|
|
6436
6915
|
this.queryRadius = getSlotQueryRadius();
|
|
6437
6916
|
this.slotsByContainer = buildSlotsByContainer(projection.slotIndex);
|
|
6438
6917
|
this.originContainerId = this.getNodeParentContainerId(activeId);
|
|
6439
|
-
this.laneRootContainerId = options?.laneRootContainerId ?? this.originContainerId;
|
|
6440
|
-
this.laneParentId = this.laneRootContainerId ? this.containerIndex.get(this.laneRootContainerId)?.parentId ?? null : null;
|
|
6441
6918
|
this.laneAllowedContainers = normalizeContainerSet(options?.laneAllowedContainers);
|
|
6442
6919
|
this.blockedContainerIds = normalizeContainerSet(options?.blockedContainerIds);
|
|
6443
6920
|
const noOpSlotIds = /* @__PURE__ */ new Set();
|
|
@@ -6469,10 +6946,6 @@ var DragIntentEngine = class {
|
|
|
6469
6946
|
setTargetPlane(plane) {
|
|
6470
6947
|
this.targetPlane = plane;
|
|
6471
6948
|
}
|
|
6472
|
-
setLaneAllowedContainers(containers) {
|
|
6473
|
-
this.laneAllowedContainers = normalizeContainerSet(containers);
|
|
6474
|
-
this.reset();
|
|
6475
|
-
}
|
|
6476
6949
|
getTargetPlane() {
|
|
6477
6950
|
return this.targetPlane;
|
|
6478
6951
|
}
|
|
@@ -6571,7 +7044,7 @@ var DragIntentEngine = class {
|
|
|
6571
7044
|
if (this.isInvalidDropContainer(pointerContainerId)) return null;
|
|
6572
7045
|
for (const childId of container.children) {
|
|
6573
7046
|
if (childId === this.activeId) continue;
|
|
6574
|
-
if (this.
|
|
7047
|
+
if (this.isInvalidDropContainer(childId)) continue;
|
|
6575
7048
|
const childNode = this.getNodeRect(childId);
|
|
6576
7049
|
if (!childNode) continue;
|
|
6577
7050
|
if (!pointInRect(px, py, childNode)) continue;
|
|
@@ -6596,9 +7069,16 @@ var DragIntentEngine = class {
|
|
|
6596
7069
|
if (laneLock && !this.isLaneContainer(id)) continue;
|
|
6597
7070
|
if (!pointInRect(px, py, node.rect)) continue;
|
|
6598
7071
|
if (this.hasUsableSlots(id) === false) continue;
|
|
6599
|
-
|
|
7072
|
+
const r = node.rect;
|
|
7073
|
+
const edgeDistX = Math.min(px - r.left, r.right - px);
|
|
7074
|
+
const edgeDistY = Math.min(py - r.top, r.bottom - py);
|
|
7075
|
+
const minDim = Math.min(r.width, r.height);
|
|
7076
|
+
const edgeFraction = Math.min(edgeDistX, edgeDistY) / Math.max(1, minDim);
|
|
7077
|
+
const edgePenalty = edgeFraction < 0.1 ? 1 : 0;
|
|
7078
|
+
pointerContainers.push({ id, depth: node.depth, area: node.rect.width * node.rect.height, edgePenalty });
|
|
6600
7079
|
}
|
|
6601
7080
|
pointerContainers.sort((a, b) => {
|
|
7081
|
+
if (a.edgePenalty !== b.edgePenalty) return a.edgePenalty - b.edgePenalty;
|
|
6602
7082
|
if (b.depth !== a.depth) return b.depth - a.depth;
|
|
6603
7083
|
return a.area - b.area;
|
|
6604
7084
|
});
|
|
@@ -6611,7 +7091,9 @@ var DragIntentEngine = class {
|
|
|
6611
7091
|
}
|
|
6612
7092
|
if (this.originContainerId && this.originContainerId !== pointerContainerId && !this.isInvalidDropContainer(this.originContainerId) && !this.isBlockedContainer(this.originContainerId) && (!laneLock || this.isLaneContainer(this.originContainerId)) && this.hasUsableSlots(this.originContainerId)) {
|
|
6613
7093
|
const originNode = this.containerIndex.get(this.originContainerId);
|
|
6614
|
-
|
|
7094
|
+
const currentNode = pointerContainerId ? this.containerIndex.get(pointerContainerId) : null;
|
|
7095
|
+
const originIsDeeper = originNode && (!currentNode || originNode.depth >= currentNode.depth);
|
|
7096
|
+
if (originIsDeeper && originNode && pointInExpandedRect(px, py, originNode.rect, ORIGIN_RETURN_BAND_PX)) {
|
|
6615
7097
|
pointerContainerId = this.originContainerId;
|
|
6616
7098
|
}
|
|
6617
7099
|
}
|
|
@@ -6678,8 +7160,13 @@ var DragIntentEngine = class {
|
|
|
6678
7160
|
}
|
|
6679
7161
|
return n;
|
|
6680
7162
|
}
|
|
7163
|
+
const SLOT_ZONE_PX = 30;
|
|
6681
7164
|
for (const child of childRects) {
|
|
6682
7165
|
const midY = child.rect.top + child.rect.height / 2;
|
|
7166
|
+
const clampedTop = Math.max(child.rect.top, midY - SLOT_ZONE_PX);
|
|
7167
|
+
if (py < clampedTop) return child.index;
|
|
7168
|
+
const clampedBottom = Math.min(child.rect.bottom, midY + SLOT_ZONE_PX);
|
|
7169
|
+
if (py > clampedBottom) continue;
|
|
6683
7170
|
if (py < midY) return child.index;
|
|
6684
7171
|
}
|
|
6685
7172
|
return n;
|
|
@@ -6748,15 +7235,14 @@ var DragIntentEngine = class {
|
|
|
6748
7235
|
hasUsableSlots(containerId) {
|
|
6749
7236
|
const filtered = this.getContainerSlots(containerId);
|
|
6750
7237
|
if (filtered.length > 0) return true;
|
|
6751
|
-
|
|
7238
|
+
const raw = this.slotsByContainer.get(containerId) ?? [];
|
|
7239
|
+
if (raw.length === 0) return false;
|
|
7240
|
+
const container = this.containerIndex.get(containerId);
|
|
7241
|
+
return !container || container.children.length <= 1;
|
|
6752
7242
|
}
|
|
6753
7243
|
isLaneContainer(containerId) {
|
|
6754
7244
|
if (this.laneAllowedContainers) return this.laneAllowedContainers.has(containerId);
|
|
6755
|
-
|
|
6756
|
-
if (containerId === this.laneRootContainerId) return true;
|
|
6757
|
-
const node = this.containerIndex.get(containerId);
|
|
6758
|
-
if (!node) return false;
|
|
6759
|
-
return node.parentId === this.laneParentId;
|
|
7245
|
+
return true;
|
|
6760
7246
|
}
|
|
6761
7247
|
isBlockedContainer(containerId) {
|
|
6762
7248
|
return this.blockedContainerIds?.has(containerId) ?? false;
|
|
@@ -6767,9 +7253,11 @@ function createDragIntentEngine(projection, activeId, options) {
|
|
|
6767
7253
|
}
|
|
6768
7254
|
|
|
6769
7255
|
// src/client/dnd/design-mode/drop-line.ts
|
|
7256
|
+
var DROP_LINE_THICKNESS_PX = 2;
|
|
7257
|
+
var DROP_LINE_MIN_LENGTH_PX = 40;
|
|
6770
7258
|
function slotDropLine(slot, container, projectionTree, sdx, sdy) {
|
|
6771
|
-
const T =
|
|
6772
|
-
const MIN =
|
|
7259
|
+
const T = DROP_LINE_THICKNESS_PX;
|
|
7260
|
+
const MIN = DROP_LINE_MIN_LENGTH_PX;
|
|
6773
7261
|
const bp = computeSlotBoundaryPoint(slot, container, projectionTree);
|
|
6774
7262
|
if (container.strategy === "horizontal") {
|
|
6775
7263
|
return {
|
|
@@ -6809,9 +7297,6 @@ function slotDropLine(slot, container, projectionTree, sdx, sdy) {
|
|
|
6809
7297
|
}
|
|
6810
7298
|
|
|
6811
7299
|
// src/client/dnd/design-mode/preview-layout.ts
|
|
6812
|
-
function clampIndex(index, min, max) {
|
|
6813
|
-
return Math.max(min, Math.min(index, max));
|
|
6814
|
-
}
|
|
6815
7300
|
function getContainerChildren(projection, containerId) {
|
|
6816
7301
|
return [...projection.containerIndex.get(containerId)?.children ?? []];
|
|
6817
7302
|
}
|
|
@@ -6931,7 +7416,7 @@ function buildPreviewLayout({
|
|
|
6931
7416
|
const originalItemIds = getContainerChildren(projection, containerId);
|
|
6932
7417
|
const activeIndex = originalItemIds.indexOf(anchorNodeId);
|
|
6933
7418
|
if (activeIndex < 0) return null;
|
|
6934
|
-
const overIndex =
|
|
7419
|
+
const overIndex = clamp(targetIndex, 0, Math.max(0, originalItemIds.length - 1));
|
|
6935
7420
|
const previewItemIds = arrayMove(originalItemIds, activeIndex, overIndex);
|
|
6936
7421
|
const strategy = getContainerStrategy(projection, containerId);
|
|
6937
7422
|
containers.set(containerId, {
|
|
@@ -6971,7 +7456,7 @@ function buildPreviewLayout({
|
|
|
6971
7456
|
activePreviewContainers.add(sourceContainerId);
|
|
6972
7457
|
const targetOriginalItemIds = targetChildren.filter((id) => id !== anchorNodeId);
|
|
6973
7458
|
const targetPreviewItemIds = [...targetOriginalItemIds];
|
|
6974
|
-
const targetOverIndex =
|
|
7459
|
+
const targetOverIndex = clamp(targetIndex, 0, targetOriginalItemIds.length);
|
|
6975
7460
|
targetPreviewItemIds.splice(targetOverIndex, 0, anchorNodeId);
|
|
6976
7461
|
const targetStrategy = getContainerStrategy(projection, targetContainerId);
|
|
6977
7462
|
const targetStrategyItemIds = [...targetOriginalItemIds, anchorNodeId];
|
|
@@ -7003,9 +7488,8 @@ function buildPreviewLayout({
|
|
|
7003
7488
|
}
|
|
7004
7489
|
|
|
7005
7490
|
// src/client/dnd/design-mode/useDragVisualLoop.ts
|
|
7006
|
-
var CONTAINER_SWITCH_HYSTERESIS =
|
|
7007
|
-
var
|
|
7008
|
-
var SMALL_NODE_NEST_DWELL_MS = 180;
|
|
7491
|
+
var CONTAINER_SWITCH_HYSTERESIS = 12;
|
|
7492
|
+
var SMALL_NODE_NEST_DWELL_MS = 100;
|
|
7009
7493
|
var LARGE_NODE_NEST_DWELL_MS = 320;
|
|
7010
7494
|
function useDragVisualLoop({
|
|
7011
7495
|
activeDrag,
|
|
@@ -7054,33 +7538,12 @@ function useDragVisualLoop({
|
|
|
7054
7538
|
const scrollDy = window.scrollY - snapshot.scroll.y;
|
|
7055
7539
|
const adjustedPx = px + scrollDx;
|
|
7056
7540
|
const adjustedPy = py + scrollDy;
|
|
7057
|
-
const lockContainer = refs.lockRef.current.containerId ? refs.projectionRef.current.containerIndex.get(refs.lockRef.current.containerId) ?? null : null;
|
|
7058
|
-
if (!refs.lockRef.current.isCrossUnlocked && lockContainer) {
|
|
7059
|
-
const { margin, dwellMs } = resolveCrossUnlockParams(lockContainer.rect);
|
|
7060
|
-
const nextLock = checkCrossUnlock(
|
|
7061
|
-
refs.lockRef.current,
|
|
7062
|
-
adjustedPx,
|
|
7063
|
-
adjustedPy,
|
|
7064
|
-
lockContainer.rect,
|
|
7065
|
-
performance.now(),
|
|
7066
|
-
margin,
|
|
7067
|
-
dwellMs,
|
|
7068
|
-
CROSS_UNLOCK_GRACE_MS
|
|
7069
|
-
);
|
|
7070
|
-
if (nextLock !== refs.lockRef.current) {
|
|
7071
|
-
refs.lockRef.current = nextLock;
|
|
7072
|
-
if (nextLock.isCrossUnlocked) engine.setLaneAllowedContainers(null);
|
|
7073
|
-
}
|
|
7074
|
-
}
|
|
7075
7541
|
const nestPreview = engine.peekNestIntent(px, py, window.scrollX, window.scrollY, snapshot.scroll.x, snapshot.scroll.y);
|
|
7076
7542
|
const isLargeDraggedNode = shouldBlockNestForLargeNode(activeDrag.w, activeDrag.h);
|
|
7077
7543
|
let allowNest = false;
|
|
7078
7544
|
if (nestPreview) {
|
|
7079
7545
|
const childId = nestPreview.slotId.split("::nest::")[1] ?? "";
|
|
7080
|
-
|
|
7081
|
-
const isSiblingList = siblings.length >= 2;
|
|
7082
|
-
const isSourceContainerNest = nestPreview.containerId === refs.sourceContainerIdRef.current;
|
|
7083
|
-
allowNest = !!childId && !isSiblingList && !isLargeDraggedNode && !isSourceContainerNest;
|
|
7546
|
+
allowNest = !!childId && !isLargeDraggedNode;
|
|
7084
7547
|
}
|
|
7085
7548
|
if (!allowNest) {
|
|
7086
7549
|
refs.nestCandidateRef.current = null;
|
|
@@ -7090,7 +7553,12 @@ function useDragVisualLoop({
|
|
|
7090
7553
|
const now = performance.now();
|
|
7091
7554
|
if (!refs.nestCandidateRef.current || refs.nestCandidateRef.current.id !== nestPreview.slotId) {
|
|
7092
7555
|
refs.nestCandidateRef.current = { id: nestPreview.slotId, since: now };
|
|
7093
|
-
|
|
7556
|
+
const hintTargetId = nestPreview.slotId.split("::nest::")[1] ?? "";
|
|
7557
|
+
refs.nestTargetRef.current = refs.nodeMapRef.current.get(hintTargetId) ?? null;
|
|
7558
|
+
const hintRect = snapshot.rects.get(hintTargetId);
|
|
7559
|
+
if (hintRect) {
|
|
7560
|
+
setInsideRect(new DOMRect(hintRect.left - scrollDx, hintRect.top - scrollDy, hintRect.width, hintRect.height));
|
|
7561
|
+
}
|
|
7094
7562
|
setTargetPlane("lane");
|
|
7095
7563
|
} else if (now - refs.nestCandidateRef.current.since >= (isLargeDraggedNode ? LARGE_NODE_NEST_DWELL_MS : SMALL_NODE_NEST_DWELL_MS)) {
|
|
7096
7564
|
setTargetPlane("nest");
|
|
@@ -7149,18 +7617,6 @@ function useDragVisualLoop({
|
|
|
7149
7617
|
setTargetPlane("lane");
|
|
7150
7618
|
refs.nestCandidateRef.current = null;
|
|
7151
7619
|
refs.nestTargetRef.current = null;
|
|
7152
|
-
const lockedParentId = refs.lockRef.current.containerId;
|
|
7153
|
-
if (!refs.lockRef.current.isCrossUnlocked && (!lockedParentId || intent.containerId !== lockedParentId)) {
|
|
7154
|
-
refs.lastIntentRef.current = null;
|
|
7155
|
-
refs.visualIntentRef.current = null;
|
|
7156
|
-
refs.resolvedReorderRef.current = null;
|
|
7157
|
-
refs.finalVisualKeyRef.current = null;
|
|
7158
|
-
setInsideRect(null);
|
|
7159
|
-
setDropLine(null);
|
|
7160
|
-
animator.clear();
|
|
7161
|
-
rafId = requestAnimationFrame(tick);
|
|
7162
|
-
return;
|
|
7163
|
-
}
|
|
7164
7620
|
refs.lastIntentRef.current = intent;
|
|
7165
7621
|
refs.visualIntentRef.current = intent;
|
|
7166
7622
|
setInsideRect(null);
|
|
@@ -7192,19 +7648,43 @@ function useDragVisualLoop({
|
|
|
7192
7648
|
}
|
|
7193
7649
|
const sourceCommitLane = refs.sourceCommitLaneRef.current ?? getCommitLaneSnapshot(sourceContainerId);
|
|
7194
7650
|
const targetCommitLane = getCommitLaneSnapshot(intent.containerId);
|
|
7195
|
-
|
|
7651
|
+
let commitPlacement = resolveCommitPlacementFromVisualTarget(
|
|
7196
7652
|
targetCommitLane,
|
|
7197
7653
|
sourceCommitLane,
|
|
7198
7654
|
activeDrag.anchorNodeId,
|
|
7199
7655
|
targetInsertIndex
|
|
7200
7656
|
);
|
|
7201
7657
|
if (!commitPlacement) {
|
|
7202
|
-
refs.
|
|
7203
|
-
refs.
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7658
|
+
const containerEl = refs.nodeMapRef.current.get(intent.containerId)?.element;
|
|
7659
|
+
const sourceCommitNodeId = refs.sourceCommitLaneRef.current?.commitNodeByAnchorId.get(activeDrag.anchorNodeId) ?? activeDrag.anchorNodeId;
|
|
7660
|
+
if (!containerEl) {
|
|
7661
|
+
refs.resolvedReorderRef.current = null;
|
|
7662
|
+
refs.finalVisualKeyRef.current = null;
|
|
7663
|
+
setDropLine(null);
|
|
7664
|
+
animator.clear();
|
|
7665
|
+
rafId = requestAnimationFrame(tick);
|
|
7666
|
+
return;
|
|
7667
|
+
}
|
|
7668
|
+
const targetChildren = Array.from(containerEl.children).filter(
|
|
7669
|
+
(child) => child instanceof HTMLElement
|
|
7670
|
+
);
|
|
7671
|
+
const filteredChildren = targetChildren.filter(
|
|
7672
|
+
(child) => child.getAttribute(DND_NODE_ID_ATTR) !== sourceCommitNodeId
|
|
7673
|
+
);
|
|
7674
|
+
const idx = Math.max(0, Math.min(targetInsertIndex, filteredChildren.length));
|
|
7675
|
+
const beforeChild = filteredChildren[idx] ?? null;
|
|
7676
|
+
const afterChild = idx > 0 ? filteredChildren[idx - 1] ?? null : null;
|
|
7677
|
+
const parentNodeId = ensureParentNodeId(containerEl);
|
|
7678
|
+
const beforeNodeId = beforeChild?.getAttribute(DND_NODE_ID_ATTR) ? ensureParentNodeId(beforeChild) : null;
|
|
7679
|
+
const afterNodeId = afterChild?.getAttribute(DND_NODE_ID_ATTR) ? ensureParentNodeId(afterChild) : null;
|
|
7680
|
+
commitPlacement = {
|
|
7681
|
+
parentNodeId,
|
|
7682
|
+
laneContainerId: intent.containerId,
|
|
7683
|
+
commitNodeId: sourceCommitNodeId,
|
|
7684
|
+
beforeNodeId,
|
|
7685
|
+
afterNodeId,
|
|
7686
|
+
index: idx
|
|
7687
|
+
};
|
|
7208
7688
|
}
|
|
7209
7689
|
setDropLine(slotDropLine(slot, container, proj.projectionTree, scrollDx, scrollDy));
|
|
7210
7690
|
refs.resolvedReorderRef.current = {
|
|
@@ -7249,56 +7729,175 @@ function useDragVisualLoop({
|
|
|
7249
7729
|
|
|
7250
7730
|
// src/client/dnd/design-mode/useDragLifecycle.ts
|
|
7251
7731
|
import { useState as useState5, useCallback as useCallback6 } from "react";
|
|
7252
|
-
|
|
7253
|
-
// src/client/dnd/design-mode/node-key.ts
|
|
7254
|
-
var _runtimeKeyMap = /* @__PURE__ */ new WeakMap();
|
|
7255
|
-
var _runtimeKeyCounter = 0;
|
|
7256
|
-
function getRuntimeKey(el) {
|
|
7257
|
-
const existing = _runtimeKeyMap.get(el);
|
|
7258
|
-
if (existing) return existing;
|
|
7259
|
-
const key2 = `runtime:${++_runtimeKeyCounter}`;
|
|
7260
|
-
_runtimeKeyMap.set(el, key2);
|
|
7261
|
-
return key2;
|
|
7262
|
-
}
|
|
7263
|
-
function getNodeKeyFromElement(el) {
|
|
7264
|
-
const resolved = resolveDragSurface(el);
|
|
7265
|
-
const host = resolved.host ?? el;
|
|
7266
|
-
const meta = getEditorMeta(host);
|
|
7267
|
-
if (meta) return meta.nodeId;
|
|
7268
|
-
const persisted = host.getAttribute("data-dnd-node-id");
|
|
7269
|
-
if (persisted) return persisted;
|
|
7270
|
-
return getRuntimeKey(host);
|
|
7271
|
-
}
|
|
7272
|
-
function getInspectorRefFromElement(el) {
|
|
7273
|
-
const resolved = resolveDragSurface(el);
|
|
7274
|
-
const host = resolved.host ?? el;
|
|
7275
|
-
const meta = getEditorMeta(host);
|
|
7276
|
-
if (!meta) return null;
|
|
7277
|
-
return { relativePath: meta.file, line: meta.line, column: meta.col };
|
|
7278
|
-
}
|
|
7279
|
-
|
|
7280
|
-
// src/client/dnd/design-mode/useDragLifecycle.ts
|
|
7281
|
-
var SOFT_COMMIT_DISTANCE_PX = 14;
|
|
7282
|
-
function isLaneCommitValid(intent, resolvedReorder, lockedParentId) {
|
|
7283
|
-
if (!intent || intent.mode !== "reorder") return false;
|
|
7284
|
-
if (!resolvedReorder) return false;
|
|
7285
|
-
if (!lockedParentId) return false;
|
|
7286
|
-
if (intent.containerId !== lockedParentId) return false;
|
|
7287
|
-
if (resolvedReorder.visual.slotId !== intent.slotId) return false;
|
|
7288
|
-
if (resolvedReorder.visual.parentNodeId !== intent.containerId) return false;
|
|
7289
|
-
return true;
|
|
7290
|
-
}
|
|
7291
|
-
function buildRuntimeIdentityLocal(nodeId, resolveNodeElement) {
|
|
7732
|
+
function buildRuntimeIdentityLocal(nodeId, resolveNodeElement, effectiveLaneSnapshot) {
|
|
7292
7733
|
if (!nodeId) return null;
|
|
7293
7734
|
const element = resolveNodeElement(nodeId);
|
|
7294
7735
|
if (!element) return null;
|
|
7736
|
+
const nodeKey = getNodeKeyFromElement(element);
|
|
7737
|
+
const inspectorRef = getInspectorRefFromElement(element);
|
|
7738
|
+
const laneEffectiveNodeId = effectiveLaneSnapshot?.effectiveNodeIdsByCommitNodeId.get(nodeId) ?? null;
|
|
7739
|
+
const effectiveNodeId = typeof laneEffectiveNodeId === "string" && laneEffectiveNodeId.length > 0 ? laneEffectiveNodeId : isSourceBackedRuntimeNodeKey(nodeKey) ? nodeKey : null;
|
|
7740
|
+
const shouldExposeEffectiveIdentity = typeof effectiveNodeId === "string" && effectiveNodeId.length > 0;
|
|
7295
7741
|
return {
|
|
7296
7742
|
nodeId,
|
|
7297
|
-
nodeKey
|
|
7298
|
-
selector: element.getAttribute(
|
|
7299
|
-
inspectorRef
|
|
7743
|
+
nodeKey,
|
|
7744
|
+
selector: element.getAttribute(DND_NODE_ID_ATTR) ? buildNodeSelector(nodeId) : null,
|
|
7745
|
+
inspectorRef,
|
|
7746
|
+
...shouldExposeEffectiveIdentity ? {
|
|
7747
|
+
effectiveNodeId,
|
|
7748
|
+
effectiveNodeKey: effectiveNodeId,
|
|
7749
|
+
effectiveInspectorRef: inspectorRef
|
|
7750
|
+
} : {}
|
|
7751
|
+
};
|
|
7752
|
+
}
|
|
7753
|
+
function buildMovePayloadWithEffectiveIdentities(params) {
|
|
7754
|
+
const {
|
|
7755
|
+
placement,
|
|
7756
|
+
containerIdentity,
|
|
7757
|
+
beforeSiblingIdentity,
|
|
7758
|
+
afterSiblingIdentity,
|
|
7759
|
+
commitIdentity
|
|
7760
|
+
} = params;
|
|
7761
|
+
return {
|
|
7762
|
+
...placement,
|
|
7763
|
+
effectiveContainerNodeId: containerIdentity?.effectiveNodeId ?? null,
|
|
7764
|
+
effectiveContainerNodeKey: containerIdentity?.effectiveNodeKey ?? null,
|
|
7765
|
+
effectiveContainerInspectorRef: containerIdentity?.effectiveInspectorRef ?? null,
|
|
7766
|
+
effectiveBeforeNodeId: beforeSiblingIdentity?.effectiveNodeId ?? null,
|
|
7767
|
+
effectiveBeforeNodeKey: beforeSiblingIdentity?.effectiveNodeKey ?? null,
|
|
7768
|
+
effectiveBeforeInspectorRef: beforeSiblingIdentity?.effectiveInspectorRef ?? null,
|
|
7769
|
+
effectiveAfterNodeId: afterSiblingIdentity?.effectiveNodeId ?? null,
|
|
7770
|
+
effectiveAfterNodeKey: afterSiblingIdentity?.effectiveNodeKey ?? null,
|
|
7771
|
+
effectiveAfterInspectorRef: afterSiblingIdentity?.effectiveInspectorRef ?? null,
|
|
7772
|
+
effectiveCommitNodeId: commitIdentity?.effectiveNodeId ?? null,
|
|
7773
|
+
effectiveCommitNodeKey: commitIdentity?.effectiveNodeKey ?? null,
|
|
7774
|
+
effectiveCommitInspectorRef: commitIdentity?.effectiveInspectorRef ?? null
|
|
7300
7775
|
};
|
|
7301
7776
|
}
|
|
7777
|
+
function recordCommittedMove(args) {
|
|
7778
|
+
const {
|
|
7779
|
+
command,
|
|
7780
|
+
anchorNodeId,
|
|
7781
|
+
selectedNodeId,
|
|
7782
|
+
anchorEl,
|
|
7783
|
+
selectedEl,
|
|
7784
|
+
nodeKey,
|
|
7785
|
+
inspRef,
|
|
7786
|
+
selectedNodeKey,
|
|
7787
|
+
selectedInspectorRef,
|
|
7788
|
+
sourceLaneSnapshot,
|
|
7789
|
+
targetLaneSnapshot,
|
|
7790
|
+
resolveNodeElement,
|
|
7791
|
+
recordMoveChange
|
|
7792
|
+
} = args;
|
|
7793
|
+
const sourceLaneIdentitySnapshot = captureCommitLaneIdentitySnapshot(
|
|
7794
|
+
sourceLaneSnapshot,
|
|
7795
|
+
resolveNodeElement
|
|
7796
|
+
);
|
|
7797
|
+
const targetLaneIdentitySnapshot = command.to.laneContainerId === command.from.laneContainerId ? sourceLaneIdentitySnapshot : captureCommitLaneIdentitySnapshot(targetLaneSnapshot, resolveNodeElement);
|
|
7798
|
+
const targetLaneAfterIdentitySnapshot = targetLaneIdentitySnapshot ? createCommitLaneIdentitySnapshot({
|
|
7799
|
+
laneContainerId: targetLaneIdentitySnapshot.laneContainerId,
|
|
7800
|
+
commitNodeIds: projectCommitNodeIdsForMove({
|
|
7801
|
+
commitNodeIds: targetLaneIdentitySnapshot.commitNodeIds,
|
|
7802
|
+
movedCommitNodeId: command.commitNodeId,
|
|
7803
|
+
targetIndex: command.to.index
|
|
7804
|
+
}),
|
|
7805
|
+
nodeKeysByCommitNodeId: new Map([
|
|
7806
|
+
...targetLaneIdentitySnapshot.nodeKeysByCommitNodeId,
|
|
7807
|
+
...sourceLaneIdentitySnapshot?.nodeKeysByCommitNodeId.has(command.commitNodeId) ? [[command.commitNodeId, sourceLaneIdentitySnapshot.nodeKeysByCommitNodeId.get(command.commitNodeId) ?? null]] : []
|
|
7808
|
+
])
|
|
7809
|
+
}) : null;
|
|
7810
|
+
const sourceContainerIdentity = buildRuntimeIdentityLocal(
|
|
7811
|
+
command.from.parentNodeId ?? command.from.laneContainerId,
|
|
7812
|
+
resolveNodeElement,
|
|
7813
|
+
sourceLaneIdentitySnapshot
|
|
7814
|
+
);
|
|
7815
|
+
const sourceBeforeSiblingIdentity = buildRuntimeIdentityLocal(command.from.beforeNodeId, resolveNodeElement, sourceLaneIdentitySnapshot);
|
|
7816
|
+
const sourceAfterSiblingIdentity = buildRuntimeIdentityLocal(command.from.afterNodeId, resolveNodeElement, sourceLaneIdentitySnapshot);
|
|
7817
|
+
const sourceCommitIdentity = buildRuntimeIdentityLocal(command.commitNodeId, resolveNodeElement, sourceLaneIdentitySnapshot);
|
|
7818
|
+
const containerIdentity = buildRuntimeIdentityLocal(
|
|
7819
|
+
command.to.parentNodeId ?? command.to.laneContainerId,
|
|
7820
|
+
resolveNodeElement,
|
|
7821
|
+
targetLaneAfterIdentitySnapshot
|
|
7822
|
+
);
|
|
7823
|
+
const beforeSiblingIdentity = buildRuntimeIdentityLocal(command.to.beforeNodeId, resolveNodeElement, targetLaneAfterIdentitySnapshot);
|
|
7824
|
+
const afterSiblingIdentity = buildRuntimeIdentityLocal(command.to.afterNodeId, resolveNodeElement, targetLaneAfterIdentitySnapshot);
|
|
7825
|
+
const targetCommitIdentity = buildRuntimeIdentityLocal(command.commitNodeId, resolveNodeElement, targetLaneAfterIdentitySnapshot);
|
|
7826
|
+
const diagnostics = buildMovePersistenceDiagnostics({
|
|
7827
|
+
sourceLane: sourceLaneIdentitySnapshot,
|
|
7828
|
+
targetLane: targetLaneIdentitySnapshot,
|
|
7829
|
+
movedCommitNodeId: command.commitNodeId,
|
|
7830
|
+
sourceLaneContainerId: command.from.laneContainerId,
|
|
7831
|
+
targetLaneContainerId: command.to.laneContainerId,
|
|
7832
|
+
targetIndex: command.to.index
|
|
7833
|
+
});
|
|
7834
|
+
const componentName = getEditorMeta(anchorEl)?.componentName;
|
|
7835
|
+
const operationId = `${command.at}:${anchorNodeId}`;
|
|
7836
|
+
const beforePayload = buildMovePayloadWithEffectiveIdentities({
|
|
7837
|
+
placement: command.from,
|
|
7838
|
+
containerIdentity: sourceContainerIdentity,
|
|
7839
|
+
beforeSiblingIdentity: sourceBeforeSiblingIdentity,
|
|
7840
|
+
afterSiblingIdentity: sourceAfterSiblingIdentity,
|
|
7841
|
+
commitIdentity: sourceCommitIdentity
|
|
7842
|
+
});
|
|
7843
|
+
const afterPayload = buildMovePayloadWithEffectiveIdentities({
|
|
7844
|
+
placement: command.to,
|
|
7845
|
+
containerIdentity,
|
|
7846
|
+
beforeSiblingIdentity,
|
|
7847
|
+
afterSiblingIdentity,
|
|
7848
|
+
commitIdentity: targetCommitIdentity
|
|
7849
|
+
});
|
|
7850
|
+
recordMoveChange({
|
|
7851
|
+
operationId,
|
|
7852
|
+
commandType: command.type,
|
|
7853
|
+
nodeKey,
|
|
7854
|
+
nodeId: anchorNodeId,
|
|
7855
|
+
inspectorRef: inspRef,
|
|
7856
|
+
selectedNodeKey,
|
|
7857
|
+
selectedNodeId,
|
|
7858
|
+
selectedInspectorRef,
|
|
7859
|
+
anchorNodeKey: nodeKey,
|
|
7860
|
+
anchorNodeId,
|
|
7861
|
+
sourceContainer: sourceContainerIdentity,
|
|
7862
|
+
sourceBeforeSibling: sourceBeforeSiblingIdentity,
|
|
7863
|
+
sourceAfterSibling: sourceAfterSiblingIdentity,
|
|
7864
|
+
diagnostics,
|
|
7865
|
+
container: containerIdentity,
|
|
7866
|
+
beforeSibling: beforeSiblingIdentity,
|
|
7867
|
+
afterSibling: afterSiblingIdentity,
|
|
7868
|
+
componentName,
|
|
7869
|
+
before: beforePayload,
|
|
7870
|
+
after: afterPayload
|
|
7871
|
+
});
|
|
7872
|
+
dispatchRuntimeEvent({
|
|
7873
|
+
type: "element-moved",
|
|
7874
|
+
operationId,
|
|
7875
|
+
commandType: command.type,
|
|
7876
|
+
selected: {
|
|
7877
|
+
nodeId: selectedNodeId,
|
|
7878
|
+
nodeKey: selectedNodeKey,
|
|
7879
|
+
selector: selectedEl.getAttribute(DND_NODE_ID_ATTR) ? buildNodeSelector(selectedNodeId) : null,
|
|
7880
|
+
inspectorRef: selectedInspectorRef,
|
|
7881
|
+
...targetCommitIdentity?.effectiveNodeId ? { effectiveNodeId: targetCommitIdentity.effectiveNodeId, effectiveNodeKey: targetCommitIdentity.effectiveNodeKey } : {}
|
|
7882
|
+
},
|
|
7883
|
+
anchor: {
|
|
7884
|
+
nodeId: anchorNodeId,
|
|
7885
|
+
nodeKey,
|
|
7886
|
+
selector: anchorEl.getAttribute(DND_NODE_ID_ATTR) ? buildNodeSelector(anchorNodeId) : null,
|
|
7887
|
+
inspectorRef: inspRef,
|
|
7888
|
+
...targetCommitIdentity?.effectiveNodeId ? { effectiveNodeId: targetCommitIdentity.effectiveNodeId, effectiveNodeKey: targetCommitIdentity.effectiveNodeKey } : {}
|
|
7889
|
+
},
|
|
7890
|
+
sourceContainer: sourceContainerIdentity,
|
|
7891
|
+
sourceBeforeSibling: sourceBeforeSiblingIdentity,
|
|
7892
|
+
sourceAfterSibling: sourceAfterSiblingIdentity,
|
|
7893
|
+
container: containerIdentity,
|
|
7894
|
+
beforeSibling: beforeSiblingIdentity,
|
|
7895
|
+
afterSibling: afterSiblingIdentity,
|
|
7896
|
+
diagnostics,
|
|
7897
|
+
before: beforePayload,
|
|
7898
|
+
after: afterPayload
|
|
7899
|
+
});
|
|
7900
|
+
}
|
|
7302
7901
|
function useDragLifecycle({
|
|
7303
7902
|
refs,
|
|
7304
7903
|
pause,
|
|
@@ -7380,12 +7979,10 @@ function useDragLifecycle({
|
|
|
7380
7979
|
const srcContainerId = anchor.sourceContainerId;
|
|
7381
7980
|
const sourceCommitLane = getCommitLaneSnapshot(srcContainerId);
|
|
7382
7981
|
const startPosition = getCurrentCommitPlacement(anchorNodeId, srcContainerId);
|
|
7383
|
-
const laneAllowed = srcContainerId ? /* @__PURE__ */ new Set([srcContainerId]) : null;
|
|
7384
7982
|
const blockedContainerIds = buildBlockedStructuralContainerIds(proj, refs.nodeMapRef.current, srcContainerId);
|
|
7385
7983
|
refs.blockedContainerIdsRef.current = blockedContainerIds;
|
|
7386
7984
|
refs.engineRef.current = createDragIntentEngine(proj, anchorNodeId, {
|
|
7387
7985
|
laneRootContainerId: srcContainerId,
|
|
7388
|
-
laneAllowedContainers: laneAllowed,
|
|
7389
7986
|
blockedContainerIds
|
|
7390
7987
|
});
|
|
7391
7988
|
refs.engineRef.current.setTargetPlane("lane");
|
|
@@ -7393,7 +7990,7 @@ function useDragLifecycle({
|
|
|
7393
7990
|
refs.sourceContainerIdRef.current = srcContainerId;
|
|
7394
7991
|
refs.sourceCommitLaneRef.current = sourceCommitLane;
|
|
7395
7992
|
refs.dragSessionAnchorRef.current = anchor;
|
|
7396
|
-
refs.lockRef.current = { containerId: srcContainerId, isCrossUnlocked:
|
|
7993
|
+
refs.lockRef.current = { containerId: srcContainerId, isCrossUnlocked: true, exitSince: null, unlockUntilTs: null };
|
|
7397
7994
|
refs.dragStartPositionRef.current = startPosition;
|
|
7398
7995
|
refs.safeZoneRef.current = resolveSafeZoneRect();
|
|
7399
7996
|
pause();
|
|
@@ -7419,22 +8016,34 @@ function useDragLifecycle({
|
|
|
7419
8016
|
const pointer = refs.mouseRef.current;
|
|
7420
8017
|
const safeZone = refs.safeZoneRef.current;
|
|
7421
8018
|
const blockedByGuard = isPointerOutsideSafeZone(pointer.x, pointer.y, safeZone) || isPointerInHardPageEdgeZone(pointer.x, pointer.y);
|
|
7422
|
-
|
|
8019
|
+
let sourcePos = refs.dragStartPositionRef.current ?? getCurrentCommitPlacement(anchorNodeId, refs.sourceContainerIdRef.current);
|
|
8020
|
+
if (!sourcePos && activeEl && activeEl.parentElement) {
|
|
8021
|
+
const parentEl = activeEl.parentElement;
|
|
8022
|
+
const commitNodeId = activeEl.getAttribute(DND_NODE_ID_ATTR) ?? anchorNodeId;
|
|
8023
|
+
const siblings = Array.from(parentEl.children).filter((c) => c instanceof HTMLElement);
|
|
8024
|
+
const idx = siblings.indexOf(activeEl);
|
|
8025
|
+
if (idx >= 0) {
|
|
8026
|
+
sourcePos = {
|
|
8027
|
+
parentNodeId: ensureParentNodeId(parentEl),
|
|
8028
|
+
laneContainerId: refs.sourceContainerIdRef.current,
|
|
8029
|
+
commitNodeId,
|
|
8030
|
+
beforeNodeId: idx < siblings.length - 1 ? siblings[idx + 1].getAttribute(DND_NODE_ID_ATTR) ?? ensureParentNodeId(siblings[idx + 1]) : null,
|
|
8031
|
+
afterNodeId: idx > 0 ? siblings[idx - 1].getAttribute(DND_NODE_ID_ATTR) ?? ensureParentNodeId(siblings[idx - 1]) : null,
|
|
8032
|
+
index: idx
|
|
8033
|
+
};
|
|
8034
|
+
}
|
|
8035
|
+
}
|
|
7423
8036
|
const intent = refs.visualIntentRef.current;
|
|
7424
|
-
const finalVisual = refs.finalVisualKeyRef.current;
|
|
7425
8037
|
const nestTarget = refs.nestTargetRef.current;
|
|
7426
8038
|
const intentHasContainer = intent ? refs.projectionRef.current.containerIndex.has(intent.containerId) : false;
|
|
7427
8039
|
const intentHasSlot = intent ? intent.mode === "nest" || refs.projectionRef.current.slotIndex.has(intent.slotId) : false;
|
|
7428
8040
|
const intentValid = !!intent && intentHasContainer && intentHasSlot;
|
|
7429
8041
|
if (!blockedByGuard && activeEl && sourcePos && (nestTarget || intentValid)) {
|
|
7430
8042
|
const dropTargetId = nestTarget?.nodeId ?? intent?.containerId;
|
|
7431
|
-
if (dropTargetId) {
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
syncHistoryAvailability();
|
|
7436
|
-
return;
|
|
7437
|
-
}
|
|
8043
|
+
if (dropTargetId && !refs.nodeMapRef.current.has(dropTargetId)) {
|
|
8044
|
+
clearDragState();
|
|
8045
|
+
syncHistoryAvailability();
|
|
8046
|
+
return;
|
|
7438
8047
|
}
|
|
7439
8048
|
let command = null;
|
|
7440
8049
|
if (nestTarget) {
|
|
@@ -7454,46 +8063,8 @@ function useDragLifecycle({
|
|
|
7454
8063
|
};
|
|
7455
8064
|
}
|
|
7456
8065
|
} else if (intentValid && intent.mode === "reorder") {
|
|
7457
|
-
const lockedParentId = refs.lockRef.current.containerId;
|
|
7458
|
-
if (!refs.lockRef.current.isCrossUnlocked && (!lockedParentId || intent.containerId !== lockedParentId)) {
|
|
7459
|
-
clearDragState();
|
|
7460
|
-
syncHistoryAvailability();
|
|
7461
|
-
return;
|
|
7462
|
-
}
|
|
7463
|
-
const snapshot = refs.snapshotRef.current;
|
|
7464
|
-
const targetContainer = refs.projectionRef.current.containerIndex.get(intent.containerId);
|
|
7465
|
-
if (targetContainer) {
|
|
7466
|
-
const scrollDx = window.scrollX - (snapshot?.scroll.x ?? 0);
|
|
7467
|
-
const scrollDy = window.scrollY - (snapshot?.scroll.y ?? 0);
|
|
7468
|
-
const adjPx = pointer.x + scrollDx;
|
|
7469
|
-
const adjPy = pointer.y + scrollDy;
|
|
7470
|
-
const r = targetContainer.rect;
|
|
7471
|
-
const inContainer = adjPx >= r.left - 40 && adjPx <= r.right + 40 && adjPy >= r.top - 40 && adjPy <= r.bottom + 40;
|
|
7472
|
-
if (!inContainer) {
|
|
7473
|
-
const softFallbackAllowed = finalVisual?.mode === "reorder" && finalVisual.containerId === intent.containerId && (() => {
|
|
7474
|
-
const slot = refs.projectionRef.current.slotIndex.get(finalVisual.slotId);
|
|
7475
|
-
const container = refs.projectionRef.current.containerIndex.get(finalVisual.containerId);
|
|
7476
|
-
if (!slot || !container) return false;
|
|
7477
|
-
const bp = computeSlotBoundaryPoint(slot, container, refs.projectionRef.current.projectionTree);
|
|
7478
|
-
const dist = Math.hypot(adjPx - bp.x, adjPy - bp.y);
|
|
7479
|
-
return dist <= SOFT_COMMIT_DISTANCE_PX;
|
|
7480
|
-
})();
|
|
7481
|
-
if (!softFallbackAllowed) {
|
|
7482
|
-
clearDragState();
|
|
7483
|
-
syncHistoryAvailability();
|
|
7484
|
-
return;
|
|
7485
|
-
}
|
|
7486
|
-
}
|
|
7487
|
-
}
|
|
7488
8066
|
const resolvedReorder = refs.resolvedReorderRef.current;
|
|
7489
|
-
|
|
7490
|
-
if (!laneValid) {
|
|
7491
|
-
clearDragState();
|
|
7492
|
-
syncHistoryAvailability();
|
|
7493
|
-
return;
|
|
7494
|
-
}
|
|
7495
|
-
const visualParity = !!finalVisual && finalVisual.mode === "reorder" && finalVisual.anchorNodeId === anchorNodeId && finalVisual.slotId === resolvedReorder.visual.slotId && finalVisual.parentNodeId === resolvedReorder.visual.parentNodeId && finalVisual.index === resolvedReorder.visual.index && finalVisual.containerId === intent.containerId;
|
|
7496
|
-
if (!visualParity) {
|
|
8067
|
+
if (!resolvedReorder || resolvedReorder.visual.containerId !== intent.containerId) {
|
|
7497
8068
|
clearDragState();
|
|
7498
8069
|
syncHistoryAvailability();
|
|
7499
8070
|
return;
|
|
@@ -7509,56 +8080,32 @@ function useDragLifecycle({
|
|
|
7509
8080
|
};
|
|
7510
8081
|
}
|
|
7511
8082
|
if (command) {
|
|
8083
|
+
const anchorEl = resolveNodeElement(anchorNodeId) ?? activeEl;
|
|
8084
|
+
const selectedEl = resolveNodeElement(selectedNodeId) ?? anchorEl;
|
|
8085
|
+
const nodeKey = getNodeKeyFromElement(anchorEl);
|
|
8086
|
+
const inspRef = getInspectorRefFromElement(anchorEl);
|
|
8087
|
+
const selectedNodeKey = getNodeKeyFromElement(selectedEl);
|
|
8088
|
+
const selectedInspectorRef = getInspectorRefFromElement(selectedEl);
|
|
8089
|
+
const sourceLaneSnapshot = refs.sourceCommitLaneRef.current ?? getCommitLaneSnapshot(command.from.laneContainerId);
|
|
8090
|
+
const targetLaneSnapshot = command.to.laneContainerId === command.from.laneContainerId ? sourceLaneSnapshot : getCommitLaneSnapshot(command.to.laneContainerId);
|
|
7512
8091
|
const committed = refs.historyRef.current.execute(command, applyCommand);
|
|
7513
8092
|
if (committed) {
|
|
7514
8093
|
activeEl.setAttribute("data-dnd-moved", "");
|
|
7515
8094
|
selectElement(null);
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
recordMoveChange({
|
|
8095
|
+
recordCommittedMove({
|
|
8096
|
+
command,
|
|
8097
|
+
anchorNodeId,
|
|
8098
|
+
selectedNodeId,
|
|
8099
|
+
anchorEl,
|
|
8100
|
+
selectedEl,
|
|
7523
8101
|
nodeKey,
|
|
7524
|
-
|
|
7525
|
-
inspectorRef: inspRef,
|
|
8102
|
+
inspRef,
|
|
7526
8103
|
selectedNodeKey,
|
|
7527
|
-
selectedNodeId,
|
|
7528
8104
|
selectedInspectorRef,
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
after: command.to
|
|
7534
|
-
});
|
|
7535
|
-
const containerIdentity = buildRuntimeIdentityLocal(
|
|
7536
|
-
command.to.laneContainerId ?? command.to.parentNodeId,
|
|
7537
|
-
resolveNodeElement
|
|
7538
|
-
);
|
|
7539
|
-
const beforeSiblingIdentity = buildRuntimeIdentityLocal(command.to.beforeNodeId, resolveNodeElement);
|
|
7540
|
-
const afterSiblingIdentity = buildRuntimeIdentityLocal(command.to.afterNodeId, resolveNodeElement);
|
|
7541
|
-
dispatchRuntimeEvent({
|
|
7542
|
-
type: "element-moved",
|
|
7543
|
-
operationId: `${command.at}:${anchorNodeId}`,
|
|
7544
|
-
commandType: command.type,
|
|
7545
|
-
selected: {
|
|
7546
|
-
nodeId: selectedNodeId,
|
|
7547
|
-
nodeKey: selectedNodeKey,
|
|
7548
|
-
selector: selectedEl.getAttribute(DND_NODE_ID_ATTR2) ? `[${DND_NODE_ID_ATTR2}="${escapeCssAttrValue(selectedNodeId)}"]` : null,
|
|
7549
|
-
inspectorRef: selectedInspectorRef
|
|
7550
|
-
},
|
|
7551
|
-
anchor: {
|
|
7552
|
-
nodeId: anchorNodeId,
|
|
7553
|
-
nodeKey,
|
|
7554
|
-
selector: anchorEl.getAttribute(DND_NODE_ID_ATTR2) ? `[${DND_NODE_ID_ATTR2}="${escapeCssAttrValue(anchorNodeId)}"]` : null,
|
|
7555
|
-
inspectorRef: inspRef
|
|
7556
|
-
},
|
|
7557
|
-
container: containerIdentity,
|
|
7558
|
-
beforeSibling: beforeSiblingIdentity,
|
|
7559
|
-
afterSibling: afterSiblingIdentity,
|
|
7560
|
-
before: command.from,
|
|
7561
|
-
after: command.to
|
|
8105
|
+
sourceLaneSnapshot,
|
|
8106
|
+
targetLaneSnapshot,
|
|
8107
|
+
resolveNodeElement,
|
|
8108
|
+
recordMoveChange
|
|
7562
8109
|
});
|
|
7563
8110
|
}
|
|
7564
8111
|
}
|
|
@@ -7597,7 +8144,7 @@ function useResizeSession({
|
|
|
7597
8144
|
const rect = resizeTargetEl.getBoundingClientRect();
|
|
7598
8145
|
const selectedNodeKey = getNodeKeyFromElement(selectedEl);
|
|
7599
8146
|
const targetNodeKey = getNodeKeyFromElement(resizeTargetEl);
|
|
7600
|
-
const targetNodeId =
|
|
8147
|
+
const targetNodeId = ensureParentNodeId(resizeTargetEl);
|
|
7601
8148
|
const startColSpan = parseSpanValue(getComputedStyle(resizeTargetEl).gridColumnEnd || resizeTargetEl.style.gridColumnEnd);
|
|
7602
8149
|
const startRowSpan = parseSpanValue(getComputedStyle(resizeTargetEl).gridRowEnd || resizeTargetEl.style.gridRowEnd);
|
|
7603
8150
|
const columnTrackCount = getGridTrackCount(resizeTargetParent ? getComputedStyle(resizeTargetParent).gridTemplateColumns : null);
|
|
@@ -7705,8 +8252,10 @@ function useResizeSession({
|
|
|
7705
8252
|
after: { width: afterW, height: afterH, colSpan: afterColSpan, rowSpan: afterRowSpan, styles: afterStyles },
|
|
7706
8253
|
at: Date.now()
|
|
7707
8254
|
};
|
|
8255
|
+
const operationId = `${resizeCmd.at}:${selectedNodeId}`;
|
|
7708
8256
|
refs.historyRef.current.execute(resizeCmd, applyCommand);
|
|
7709
8257
|
recordResizeChange({
|
|
8258
|
+
operationId,
|
|
7710
8259
|
nodeKey: selectedNodeKey,
|
|
7711
8260
|
nodeId: selectedNodeId,
|
|
7712
8261
|
inspectorRef,
|
|
@@ -7716,17 +8265,17 @@ function useResizeSession({
|
|
|
7716
8265
|
});
|
|
7717
8266
|
dispatchRuntimeEvent({
|
|
7718
8267
|
type: "element-resized",
|
|
7719
|
-
operationId
|
|
8268
|
+
operationId,
|
|
7720
8269
|
selected: {
|
|
7721
8270
|
nodeId: selectedNodeId,
|
|
7722
8271
|
nodeKey: selectedNodeKey,
|
|
7723
|
-
selector:
|
|
8272
|
+
selector: buildNodeSelector(selectedNodeId),
|
|
7724
8273
|
inspectorRef
|
|
7725
8274
|
},
|
|
7726
8275
|
anchor: {
|
|
7727
8276
|
nodeId: targetNodeId,
|
|
7728
8277
|
nodeKey: targetNodeKey,
|
|
7729
|
-
selector:
|
|
8278
|
+
selector: buildNodeSelector(targetNodeId),
|
|
7730
8279
|
inspectorRef: getInspectorRefFromElement(element)
|
|
7731
8280
|
},
|
|
7732
8281
|
resize: {
|
|
@@ -7767,6 +8316,169 @@ function useResizeSession({
|
|
|
7767
8316
|
return { handleResizeHandlePointerDown };
|
|
7768
8317
|
}
|
|
7769
8318
|
|
|
8319
|
+
// src/client/dnd/design-mode/useExternalDragLoop.ts
|
|
8320
|
+
import { useEffect as useEffect11, useRef as useRef9 } from "react";
|
|
8321
|
+
var EXTERNAL_DRAG_ACTIVE_ID = "__external-drag-placeholder__";
|
|
8322
|
+
function useExternalDragLoop({
|
|
8323
|
+
externalDrag,
|
|
8324
|
+
refs,
|
|
8325
|
+
elements,
|
|
8326
|
+
resolveNodeElement,
|
|
8327
|
+
setDropLine
|
|
8328
|
+
}) {
|
|
8329
|
+
const engineRef = useRef9(null);
|
|
8330
|
+
const lastIntentRef = useRef9(null);
|
|
8331
|
+
useEffect11(() => {
|
|
8332
|
+
if (!externalDrag) {
|
|
8333
|
+
engineRef.current = null;
|
|
8334
|
+
lastIntentRef.current = null;
|
|
8335
|
+
setDropLine(null);
|
|
8336
|
+
externalDragMouse.commitRequested = false;
|
|
8337
|
+
return;
|
|
8338
|
+
}
|
|
8339
|
+
const projection = buildProjection(elements);
|
|
8340
|
+
if (projection.containerIndex.size === 0) return;
|
|
8341
|
+
const rects = /* @__PURE__ */ new Map();
|
|
8342
|
+
for (const el of elements) {
|
|
8343
|
+
rects.set(el.nodeId, el.rect);
|
|
8344
|
+
}
|
|
8345
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
8346
|
+
for (const el of elements) nodeMap.set(el.nodeId, el);
|
|
8347
|
+
const blockedContainerIds = buildBlockedStructuralContainerIds(projection, nodeMap, null);
|
|
8348
|
+
const engine = new DragIntentEngine(projection, EXTERNAL_DRAG_ACTIVE_ID, {
|
|
8349
|
+
blockedContainerIds
|
|
8350
|
+
});
|
|
8351
|
+
engineRef.current = engine;
|
|
8352
|
+
const scrollX = window.scrollX;
|
|
8353
|
+
const scrollY = window.scrollY;
|
|
8354
|
+
let rafId;
|
|
8355
|
+
const tick = () => {
|
|
8356
|
+
if (externalDragMouse.commitRequested) {
|
|
8357
|
+
externalDragMouse.commitRequested = false;
|
|
8358
|
+
commitExternalDrop(
|
|
8359
|
+
externalDragMouse.x,
|
|
8360
|
+
externalDragMouse.y,
|
|
8361
|
+
engine,
|
|
8362
|
+
externalDrag,
|
|
8363
|
+
refs,
|
|
8364
|
+
resolveNodeElement,
|
|
8365
|
+
setDropLine
|
|
8366
|
+
);
|
|
8367
|
+
return;
|
|
8368
|
+
}
|
|
8369
|
+
const intent = engine.resolve(
|
|
8370
|
+
externalDragMouse.x,
|
|
8371
|
+
externalDragMouse.y,
|
|
8372
|
+
window.scrollX,
|
|
8373
|
+
window.scrollY,
|
|
8374
|
+
scrollX,
|
|
8375
|
+
scrollY
|
|
8376
|
+
);
|
|
8377
|
+
if (intent && intent.mode === "reorder") {
|
|
8378
|
+
lastIntentRef.current = intent;
|
|
8379
|
+
const slot = projection.slotIndex.get(intent.slotId);
|
|
8380
|
+
const container = projection.containerIndex.get(intent.containerId);
|
|
8381
|
+
if (slot && container) {
|
|
8382
|
+
const scrollDx = window.scrollX - scrollX;
|
|
8383
|
+
const scrollDy = window.scrollY - scrollY;
|
|
8384
|
+
setDropLine(slotDropLine(slot, container, projection.projectionTree, scrollDx, scrollDy));
|
|
8385
|
+
} else {
|
|
8386
|
+
setDropLine(null);
|
|
8387
|
+
}
|
|
8388
|
+
} else {
|
|
8389
|
+
lastIntentRef.current = null;
|
|
8390
|
+
setDropLine(null);
|
|
8391
|
+
}
|
|
8392
|
+
rafId = requestAnimationFrame(tick);
|
|
8393
|
+
};
|
|
8394
|
+
rafId = requestAnimationFrame(tick);
|
|
8395
|
+
return () => cancelAnimationFrame(rafId);
|
|
8396
|
+
}, [externalDrag, elements, refs, resolveNodeElement, setDropLine]);
|
|
8397
|
+
}
|
|
8398
|
+
function commitExternalDrop(x, y, engine, component, refs, resolveNodeElement, setDropLine) {
|
|
8399
|
+
const scrollX = window.scrollX;
|
|
8400
|
+
const scrollY = window.scrollY;
|
|
8401
|
+
const intent = engine.resolve(x, y, scrollX, scrollY, scrollX, scrollY);
|
|
8402
|
+
if (!intent || intent.mode !== "reorder") {
|
|
8403
|
+
useDesignModeStore.getState().cancelExternalDrag();
|
|
8404
|
+
setDropLine(null);
|
|
8405
|
+
return;
|
|
8406
|
+
}
|
|
8407
|
+
const containerEl = resolveNodeElement(intent.containerId);
|
|
8408
|
+
if (!containerEl) {
|
|
8409
|
+
useDesignModeStore.getState().cancelExternalDrag();
|
|
8410
|
+
setDropLine(null);
|
|
8411
|
+
return;
|
|
8412
|
+
}
|
|
8413
|
+
const template = document.createElement("template");
|
|
8414
|
+
template.innerHTML = component.html.trim();
|
|
8415
|
+
const newElement = template.content.firstElementChild;
|
|
8416
|
+
if (!newElement) {
|
|
8417
|
+
useDesignModeStore.getState().cancelExternalDrag();
|
|
8418
|
+
setDropLine(null);
|
|
8419
|
+
return;
|
|
8420
|
+
}
|
|
8421
|
+
const nodeId = `insert:${Date.now()}`;
|
|
8422
|
+
newElement.setAttribute(DND_NODE_ID_ATTR, nodeId);
|
|
8423
|
+
newElement.setAttribute("data-dnd-force-allow", "");
|
|
8424
|
+
newElement.setAttribute("data-dnd-moved", "");
|
|
8425
|
+
const children = Array.from(containerEl.children).filter(
|
|
8426
|
+
(child) => child instanceof HTMLElement
|
|
8427
|
+
);
|
|
8428
|
+
const beforeChild = children[intent.index] ?? null;
|
|
8429
|
+
containerEl.insertBefore(newElement, beforeChild);
|
|
8430
|
+
const insertCommand = {
|
|
8431
|
+
type: "insert",
|
|
8432
|
+
nodeId,
|
|
8433
|
+
html: component.html,
|
|
8434
|
+
containerId: intent.containerId,
|
|
8435
|
+
index: intent.index,
|
|
8436
|
+
at: Date.now()
|
|
8437
|
+
};
|
|
8438
|
+
const applied = refs.historyRef.current.execute(insertCommand, (command, direction) => {
|
|
8439
|
+
if (command.type !== "insert") return false;
|
|
8440
|
+
return applyInsertCommand(command, direction, resolveNodeElement);
|
|
8441
|
+
});
|
|
8442
|
+
if (applied) {
|
|
8443
|
+
const nodeKey = getNodeKeyFromElement(newElement);
|
|
8444
|
+
const inspectorRef = getInspectorRefFromElement(newElement);
|
|
8445
|
+
const containerNodeKey = getNodeKeyFromElement(containerEl);
|
|
8446
|
+
let containerIdentity = null;
|
|
8447
|
+
if (containerEl) {
|
|
8448
|
+
containerIdentity = {
|
|
8449
|
+
nodeId: intent.containerId,
|
|
8450
|
+
nodeKey: getNodeKeyFromElement(containerEl),
|
|
8451
|
+
selector: containerEl.getAttribute(DND_NODE_ID_ATTR) ? buildNodeSelector(intent.containerId) : null,
|
|
8452
|
+
inspectorRef: getInspectorRefFromElement(containerEl)
|
|
8453
|
+
};
|
|
8454
|
+
}
|
|
8455
|
+
const operationId = `${insertCommand.at}:${nodeId}`;
|
|
8456
|
+
useDesignModeStore.getState().recordInsertChange({
|
|
8457
|
+
operationId,
|
|
8458
|
+
nodeKey,
|
|
8459
|
+
nodeId,
|
|
8460
|
+
inspectorRef,
|
|
8461
|
+
displayName: component.displayName,
|
|
8462
|
+
html: component.html,
|
|
8463
|
+
containerId: intent.containerId,
|
|
8464
|
+
containerNodeKey,
|
|
8465
|
+
index: intent.index
|
|
8466
|
+
});
|
|
8467
|
+
dispatchRuntimeEvent({
|
|
8468
|
+
type: "element-inserted",
|
|
8469
|
+
operationId,
|
|
8470
|
+
nodeId,
|
|
8471
|
+
nodeKey,
|
|
8472
|
+
displayName: component.displayName,
|
|
8473
|
+
html: component.html,
|
|
8474
|
+
container: containerIdentity,
|
|
8475
|
+
index: intent.index
|
|
8476
|
+
});
|
|
8477
|
+
}
|
|
8478
|
+
setDropLine(null);
|
|
8479
|
+
useDesignModeStore.getState().cancelExternalDrag();
|
|
8480
|
+
}
|
|
8481
|
+
|
|
7770
8482
|
// src/client/dnd/design-mode/DesignModeOverlay.tsx
|
|
7771
8483
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
7772
8484
|
var DROP_ANIMATION = { duration: DURATION_MS, easing: EASING };
|
|
@@ -7812,7 +8524,7 @@ function HitArea({
|
|
|
7812
8524
|
onResizeHandlePointerDown
|
|
7813
8525
|
}) {
|
|
7814
8526
|
const { setNodeRef, listeners, attributes, isDragging } = useDraggable({ id: scanned.nodeId });
|
|
7815
|
-
|
|
8527
|
+
useEffect12(() => {
|
|
7816
8528
|
setNodeRef(scanned.element);
|
|
7817
8529
|
return () => setNodeRef(null);
|
|
7818
8530
|
}, [setNodeRef, scanned.element]);
|
|
@@ -7824,16 +8536,16 @@ function HitArea({
|
|
|
7824
8536
|
el.style.removeProperty("opacity");
|
|
7825
8537
|
}
|
|
7826
8538
|
}, [isDragging, scanned.element]);
|
|
7827
|
-
|
|
8539
|
+
useEffect12(() => () => {
|
|
7828
8540
|
scanned.element.style.removeProperty("opacity");
|
|
7829
8541
|
scanned.element.style.removeProperty("transform");
|
|
7830
8542
|
scanned.element.style.removeProperty("transition");
|
|
7831
8543
|
scanned.element.style.removeProperty("will-change");
|
|
7832
8544
|
}, [scanned.element]);
|
|
7833
|
-
const divRef =
|
|
8545
|
+
const divRef = useRef10(null);
|
|
7834
8546
|
const [hoverResizeHandle, setHoverResizeHandle] = useState6(null);
|
|
7835
8547
|
const isSelected = scanned.nodeId === selectedNodeId;
|
|
7836
|
-
|
|
8548
|
+
useEffect12(() => {
|
|
7837
8549
|
const div = divRef.current;
|
|
7838
8550
|
if (!div) return;
|
|
7839
8551
|
const onWheel = (e) => {
|
|
@@ -7904,6 +8616,7 @@ function DesignModeOverlay() {
|
|
|
7904
8616
|
const redoLastChange = useDesignModeStore((s) => s.redoLastChange);
|
|
7905
8617
|
const orderedChanges = useDesignModeStore((s) => s.orderedChanges);
|
|
7906
8618
|
const exportChangesForAI = useDesignModeStore((s) => s.exportChangesForAI);
|
|
8619
|
+
const externalDrag = useDesignModeStore((s) => s.externalDrag);
|
|
7907
8620
|
const { elements, pause, resume } = useElementScanner(enabled);
|
|
7908
8621
|
const nodeMap = useMemo6(() => {
|
|
7909
8622
|
const m = /* @__PURE__ */ new Map();
|
|
@@ -7918,7 +8631,7 @@ function DesignModeOverlay() {
|
|
|
7918
8631
|
const refs = useOverlayRefs(undoRequestId, redoRequestId);
|
|
7919
8632
|
refs.elementsRef.current = elements;
|
|
7920
8633
|
refs.nodeMapRef.current = nodeMap;
|
|
7921
|
-
|
|
8634
|
+
useEffect12(() => {
|
|
7922
8635
|
const m = /* @__PURE__ */ new Map();
|
|
7923
8636
|
for (const el of draggableElements) m.set(el.nodeId, el);
|
|
7924
8637
|
refs.draggableNodeMapRef.current = m;
|
|
@@ -7926,7 +8639,7 @@ function DesignModeOverlay() {
|
|
|
7926
8639
|
const resolveNodeElement = useCallback8((nodeId) => {
|
|
7927
8640
|
const known = refs.nodeMapRef.current.get(nodeId)?.element;
|
|
7928
8641
|
if (known) return known;
|
|
7929
|
-
return document.querySelector(
|
|
8642
|
+
return document.querySelector(buildNodeSelector(nodeId));
|
|
7930
8643
|
}, []);
|
|
7931
8644
|
const clearCommitLaneCache = useCallback8(() => {
|
|
7932
8645
|
refs.commitLaneCacheRef.current.clear();
|
|
@@ -7949,7 +8662,7 @@ function DesignModeOverlay() {
|
|
|
7949
8662
|
if (command.type === "resize") {
|
|
7950
8663
|
const resCmd = command;
|
|
7951
8664
|
const targetId = resCmd.targetNodeId ?? resCmd.nodeId;
|
|
7952
|
-
const targetEl = document.querySelector(
|
|
8665
|
+
const targetEl = document.querySelector(buildNodeSelector(targetId));
|
|
7953
8666
|
if (!targetEl) return false;
|
|
7954
8667
|
const snapshot = direction === "redo" ? resCmd.after : resCmd.before;
|
|
7955
8668
|
if (snapshot.styles) {
|
|
@@ -7960,6 +8673,9 @@ function DesignModeOverlay() {
|
|
|
7960
8673
|
}
|
|
7961
8674
|
return true;
|
|
7962
8675
|
}
|
|
8676
|
+
if (command.type === "insert") {
|
|
8677
|
+
return applyInsertCommand(command, direction, resolveNodeElement);
|
|
8678
|
+
}
|
|
7963
8679
|
const applied = applyDndCommandPlacement(command, direction, resolveNodeElement);
|
|
7964
8680
|
if (!applied) return false;
|
|
7965
8681
|
clearCommitLaneCache();
|
|
@@ -7972,6 +8688,10 @@ function DesignModeOverlay() {
|
|
|
7972
8688
|
}
|
|
7973
8689
|
setHistoryAvailability(refs.historyRef.current.canUndo(), refs.historyRef.current.canRedo());
|
|
7974
8690
|
}, [historyMode, setHistoryAvailability]);
|
|
8691
|
+
useEffect12(() => {
|
|
8692
|
+
refs.historyRef.current.clear();
|
|
8693
|
+
syncHistoryAvailability();
|
|
8694
|
+
}, [enabled, syncHistoryAvailability]);
|
|
7975
8695
|
const sensors = useSensors(
|
|
7976
8696
|
useSensor(PointerSensor, { activationConstraint: { distance: 8 } })
|
|
7977
8697
|
);
|
|
@@ -7982,7 +8702,7 @@ function DesignModeOverlay() {
|
|
|
7982
8702
|
);
|
|
7983
8703
|
const [insideRect, setInsideRect] = useState6(null);
|
|
7984
8704
|
const [dropLine, setDropLine] = useState6(null);
|
|
7985
|
-
|
|
8705
|
+
useEffect12(() => {
|
|
7986
8706
|
if (!enabled) return;
|
|
7987
8707
|
const fn = (e) => {
|
|
7988
8708
|
refs.mouseRef.current = { x: e.clientX, y: e.clientY };
|
|
@@ -7990,7 +8710,7 @@ function DesignModeOverlay() {
|
|
|
7990
8710
|
window.addEventListener("pointermove", fn, { capture: true, passive: true });
|
|
7991
8711
|
return () => window.removeEventListener("pointermove", fn, { capture: true });
|
|
7992
8712
|
}, [enabled]);
|
|
7993
|
-
|
|
8713
|
+
useEffect12(() => {
|
|
7994
8714
|
if (!enabled) return;
|
|
7995
8715
|
if (historyMode !== "local") return;
|
|
7996
8716
|
const onKeyDown = (e) => {
|
|
@@ -8019,7 +8739,7 @@ function DesignModeOverlay() {
|
|
|
8019
8739
|
setInsideRect,
|
|
8020
8740
|
setDropLine
|
|
8021
8741
|
});
|
|
8022
|
-
|
|
8742
|
+
useEffect12(() => {
|
|
8023
8743
|
if (!enabled || activeDrag) return;
|
|
8024
8744
|
if (undoRequestId === refs.processedUndoIdRef.current) return;
|
|
8025
8745
|
refs.processedUndoIdRef.current = undoRequestId;
|
|
@@ -8036,7 +8756,7 @@ function DesignModeOverlay() {
|
|
|
8036
8756
|
});
|
|
8037
8757
|
syncHistoryAvailability();
|
|
8038
8758
|
}, [undoRequestId, enabled, activeDrag, applyCommand, resume, selectElement, syncHistoryAvailability, undoLastChange]);
|
|
8039
|
-
|
|
8759
|
+
useEffect12(() => {
|
|
8040
8760
|
if (!enabled || activeDrag || historyMode !== "local") return;
|
|
8041
8761
|
if (redoRequestId === refs.processedRedoIdRef.current) return;
|
|
8042
8762
|
refs.processedRedoIdRef.current = redoRequestId;
|
|
@@ -8048,42 +8768,29 @@ function DesignModeOverlay() {
|
|
|
8048
8768
|
}
|
|
8049
8769
|
syncHistoryAvailability();
|
|
8050
8770
|
}, [redoRequestId, enabled, activeDrag, applyCommand, historyMode, resume, selectElement, syncHistoryAvailability, redoLastChange]);
|
|
8051
|
-
|
|
8771
|
+
useEffect12(() => {
|
|
8052
8772
|
syncHistoryAvailability();
|
|
8053
8773
|
}, [syncHistoryAvailability]);
|
|
8054
|
-
|
|
8774
|
+
useEffect12(() => {
|
|
8055
8775
|
dispatchRuntimeEvent({
|
|
8056
8776
|
type: "design-mode-changed",
|
|
8057
8777
|
mode: enabled ? "design" : "off"
|
|
8058
8778
|
});
|
|
8059
8779
|
}, [enabled]);
|
|
8060
|
-
|
|
8780
|
+
useEffect12(() => {
|
|
8061
8781
|
const aiOutput = exportChangesForAI();
|
|
8062
8782
|
const updatedAt = orderedChanges.length > 0 ? orderedChanges[orderedChanges.length - 1]?.at ?? Date.now() : Date.now();
|
|
8063
8783
|
dispatchRuntimeEvent({
|
|
8064
8784
|
type: "arrange-session-changed",
|
|
8065
|
-
session: {
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
id: `${event.kind}:${event.nodeKey}:${event.at}:${index}`,
|
|
8070
|
-
kind: event.kind,
|
|
8071
|
-
nodeKey: event.nodeKey,
|
|
8072
|
-
nodeId: event.nodeId,
|
|
8073
|
-
inspectorRef: event.inspectorRef,
|
|
8074
|
-
summary: summarizeEditorChangeEvent(event),
|
|
8075
|
-
at: event.at
|
|
8076
|
-
})),
|
|
8077
|
-
output: {
|
|
8078
|
-
json: aiOutput,
|
|
8079
|
-
prompt: aiOutput.aiPromptContext,
|
|
8080
|
-
summary: orderedChanges.length === 0 ? "No arrange changes recorded yet." : `${orderedChanges.length} arrange change${orderedChanges.length === 1 ? "" : "s"} recorded.`
|
|
8081
|
-
},
|
|
8785
|
+
session: buildArrangeSessionSnapshot({
|
|
8786
|
+
enabled,
|
|
8787
|
+
orderedChanges,
|
|
8788
|
+
aiOutput,
|
|
8082
8789
|
updatedAt
|
|
8083
|
-
}
|
|
8790
|
+
})
|
|
8084
8791
|
});
|
|
8085
8792
|
}, [enabled, exportChangesForAI, orderedChanges]);
|
|
8086
|
-
|
|
8793
|
+
useEffect12(() => {
|
|
8087
8794
|
if (!enabled) return;
|
|
8088
8795
|
document.body.classList.add("design-mode-active");
|
|
8089
8796
|
return () => document.body.classList.remove("design-mode-active");
|
|
@@ -8096,6 +8803,13 @@ function DesignModeOverlay() {
|
|
|
8096
8803
|
autoScrollStateRef: refs.autoScrollStateRef
|
|
8097
8804
|
});
|
|
8098
8805
|
useDragVisualLoop({ activeDrag, refs, getCommitLaneSnapshot, setInsideRect, setDropLine });
|
|
8806
|
+
useExternalDragLoop({
|
|
8807
|
+
externalDrag,
|
|
8808
|
+
refs,
|
|
8809
|
+
elements,
|
|
8810
|
+
resolveNodeElement,
|
|
8811
|
+
setDropLine
|
|
8812
|
+
});
|
|
8099
8813
|
const { handleResizeHandlePointerDown } = useResizeSession({
|
|
8100
8814
|
enabled,
|
|
8101
8815
|
refs,
|
|
@@ -8151,7 +8865,7 @@ function DesignModeOverlay() {
|
|
|
8151
8865
|
}
|
|
8152
8866
|
}
|
|
8153
8867
|
),
|
|
8154
|
-
dropLine && activeDrag && /* @__PURE__ */ jsx4(
|
|
8868
|
+
dropLine && (activeDrag ?? externalDrag) && /* @__PURE__ */ jsx4(
|
|
8155
8869
|
"div",
|
|
8156
8870
|
{
|
|
8157
8871
|
"data-design-mode-ui": true,
|
|
@@ -8210,35 +8924,40 @@ var combinedCollisionDetection = (args) => {
|
|
|
8210
8924
|
|
|
8211
8925
|
// src/client/dnd/DndProvider.tsx
|
|
8212
8926
|
import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
8927
|
+
var externalDragMouse = { x: 0, y: 0, commitRequested: false };
|
|
8213
8928
|
function DndProvider({ children }) {
|
|
8214
8929
|
const setActive = useDndStore((s) => s.setActive);
|
|
8215
8930
|
const dragEndHandlers = useDndStore((s) => s.dragEndHandlers);
|
|
8216
|
-
const toggle = useDesignModeStore((s) => s.toggle);
|
|
8217
8931
|
const designModeEnabled = useDesignModeStore((s) => s.enabled);
|
|
8218
|
-
const setDesignModeEnabled = useDesignModeStore((s) => s.setEnabled);
|
|
8219
8932
|
const setHistoryMode = useDesignModeStore((s) => s.setHistoryMode);
|
|
8220
8933
|
const setInspectorTheme = useDesignModeStore((s) => s.setInspectorTheme);
|
|
8221
8934
|
const requestUndo = useDesignModeStore((s) => s.requestUndo);
|
|
8222
|
-
|
|
8935
|
+
const transitionDesignMode = useCallback9((nextEnabled) => {
|
|
8936
|
+
const designModeState = useDesignModeStore.getState();
|
|
8937
|
+
if (designModeState.enabled === nextEnabled) return;
|
|
8938
|
+
designModeState.resetArrangeSession();
|
|
8939
|
+
designModeState.setEnabled(nextEnabled);
|
|
8940
|
+
}, []);
|
|
8941
|
+
useEffect13(() => {
|
|
8223
8942
|
const handleKeyDown = (e) => {
|
|
8224
8943
|
if (e.ctrlKey && e.shiftKey && e.key === "D") {
|
|
8225
8944
|
e.preventDefault();
|
|
8226
|
-
|
|
8945
|
+
transitionDesignMode(!useDesignModeStore.getState().enabled);
|
|
8227
8946
|
}
|
|
8228
8947
|
};
|
|
8229
8948
|
window.addEventListener("keydown", handleKeyDown);
|
|
8230
8949
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
8231
|
-
}, [
|
|
8232
|
-
|
|
8950
|
+
}, [transitionDesignMode]);
|
|
8951
|
+
useEffect13(() => {
|
|
8233
8952
|
const handleHostCommand = (event) => {
|
|
8234
8953
|
const detail = event.detail;
|
|
8235
8954
|
if (!detail || typeof detail !== "object") return;
|
|
8236
8955
|
switch (detail.type) {
|
|
8237
8956
|
case "sync-editor-state":
|
|
8238
8957
|
if (detail.mode === "design") {
|
|
8239
|
-
|
|
8958
|
+
transitionDesignMode(true);
|
|
8240
8959
|
} else if (detail.mode === "inspect" || detail.mode === "off") {
|
|
8241
|
-
|
|
8960
|
+
transitionDesignMode(false);
|
|
8242
8961
|
}
|
|
8243
8962
|
if (detail.historyMode) {
|
|
8244
8963
|
setHistoryMode(detail.historyMode);
|
|
@@ -8248,7 +8967,7 @@ function DndProvider({ children }) {
|
|
|
8248
8967
|
}
|
|
8249
8968
|
break;
|
|
8250
8969
|
case "set-design-mode":
|
|
8251
|
-
|
|
8970
|
+
transitionDesignMode(detail.enabled);
|
|
8252
8971
|
break;
|
|
8253
8972
|
case "set-history-mode":
|
|
8254
8973
|
setHistoryMode(detail.historyMode);
|
|
@@ -8259,6 +8978,21 @@ function DndProvider({ children }) {
|
|
|
8259
8978
|
case "request-undo":
|
|
8260
8979
|
requestUndo();
|
|
8261
8980
|
break;
|
|
8981
|
+
case "external-drag-start":
|
|
8982
|
+
useDesignModeStore.getState().startExternalDrag(detail.component);
|
|
8983
|
+
break;
|
|
8984
|
+
case "external-drag-move":
|
|
8985
|
+
externalDragMouse.x = detail.clientX;
|
|
8986
|
+
externalDragMouse.y = detail.clientY;
|
|
8987
|
+
break;
|
|
8988
|
+
case "external-drag-end":
|
|
8989
|
+
externalDragMouse.x = detail.clientX;
|
|
8990
|
+
externalDragMouse.y = detail.clientY;
|
|
8991
|
+
externalDragMouse.commitRequested = true;
|
|
8992
|
+
break;
|
|
8993
|
+
case "external-drag-cancel":
|
|
8994
|
+
useDesignModeStore.getState().cancelExternalDrag();
|
|
8995
|
+
break;
|
|
8262
8996
|
default:
|
|
8263
8997
|
break;
|
|
8264
8998
|
}
|
|
@@ -8268,7 +9002,7 @@ function DndProvider({ children }) {
|
|
|
8268
9002
|
window.removeEventListener(HOST_COMMAND_EVENT, handleHostCommand);
|
|
8269
9003
|
};
|
|
8270
9004
|
}, [
|
|
8271
|
-
|
|
9005
|
+
transitionDesignMode,
|
|
8272
9006
|
setHistoryMode,
|
|
8273
9007
|
setInspectorTheme,
|
|
8274
9008
|
requestUndo
|
|
@@ -8303,6 +9037,7 @@ function DndProvider({ children }) {
|
|
|
8303
9037
|
const handleDesignModeCrash = useCallback9(() => {
|
|
8304
9038
|
const designModeState = useDesignModeStore.getState();
|
|
8305
9039
|
designModeState.resetAll();
|
|
9040
|
+
designModeState.resetArrangeSession();
|
|
8306
9041
|
designModeState.setEnabled(false);
|
|
8307
9042
|
}, []);
|
|
8308
9043
|
if (designModeEnabled) {
|
|
@@ -8343,7 +9078,7 @@ function DndProvider({ children }) {
|
|
|
8343
9078
|
}
|
|
8344
9079
|
|
|
8345
9080
|
// src/client/useArchieDevToolsRuntime.ts
|
|
8346
|
-
import { useEffect as
|
|
9081
|
+
import { useEffect as useEffect14, useRef as useRef11 } from "react";
|
|
8347
9082
|
|
|
8348
9083
|
// src/client/inject-inspector/archieOrigins.ts
|
|
8349
9084
|
var ARCHIE_HOST_ORIGINS = [
|
|
@@ -8449,10 +9184,9 @@ function injectInspector(opts = {}) {
|
|
|
8449
9184
|
document.head.appendChild(script);
|
|
8450
9185
|
return null;
|
|
8451
9186
|
}
|
|
8452
|
-
const localInspectorModulePath = "./inspector.js";
|
|
8453
9187
|
return import(
|
|
8454
9188
|
/* @vite-ignore */
|
|
8455
|
-
|
|
9189
|
+
"./inspector.js"
|
|
8456
9190
|
).then((m) => {
|
|
8457
9191
|
try {
|
|
8458
9192
|
m.default();
|
|
@@ -8568,18 +9302,26 @@ installDomMetaSanitizer();
|
|
|
8568
9302
|
installDomMetaSanitizer();
|
|
8569
9303
|
function useArchieDevToolsRuntime({
|
|
8570
9304
|
router,
|
|
8571
|
-
inspector
|
|
9305
|
+
inspector,
|
|
9306
|
+
inspectorOptions
|
|
8572
9307
|
}) {
|
|
8573
|
-
|
|
9308
|
+
const stableOptionsKey = JSON.stringify(inspectorOptions ?? null);
|
|
9309
|
+
const inspectorOptionsRef = useRef11(inspectorOptions);
|
|
9310
|
+
inspectorOptionsRef.current = inspectorOptions;
|
|
9311
|
+
useEffect14(() => {
|
|
8574
9312
|
if (process.env.NODE_ENV !== "development") {
|
|
8575
9313
|
return;
|
|
8576
9314
|
}
|
|
8577
9315
|
const unsubscribe = setupArchieRouteListener(router);
|
|
8578
|
-
if (inspector) {
|
|
8579
|
-
|
|
9316
|
+
if (inspector !== false) {
|
|
9317
|
+
const isLocalHost = typeof window !== "undefined" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1");
|
|
9318
|
+
injectInspector({
|
|
9319
|
+
scriptSource: isLocalHost ? "local" : "remote",
|
|
9320
|
+
...inspectorOptionsRef.current
|
|
9321
|
+
});
|
|
8580
9322
|
}
|
|
8581
9323
|
return unsubscribe;
|
|
8582
|
-
}, [router, inspector]);
|
|
9324
|
+
}, [router, inspector, stableOptionsKey]);
|
|
8583
9325
|
}
|
|
8584
9326
|
|
|
8585
9327
|
// src/client/ArchieDevToolProvider.tsx
|
|
@@ -8587,9 +9329,10 @@ import { jsx as jsx6 } from "react/jsx-runtime";
|
|
|
8587
9329
|
function ArchieDevToolProvider({
|
|
8588
9330
|
children,
|
|
8589
9331
|
router,
|
|
8590
|
-
inspector
|
|
9332
|
+
inspector,
|
|
9333
|
+
inspectorOptions
|
|
8591
9334
|
}) {
|
|
8592
|
-
useArchieDevToolsRuntime({ router, inspector });
|
|
9335
|
+
useArchieDevToolsRuntime({ router, inspector, inspectorOptions });
|
|
8593
9336
|
return /* @__PURE__ */ jsx6(DndProvider, { children });
|
|
8594
9337
|
}
|
|
8595
9338
|
export {
|