@archie/devtools 0.0.12-beta → 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 +1443 -727
- package/dist/client/client.mjs +1405 -689
- package/dist/index.js +9 -3
- package/dist/index.mjs +9 -3
- package/package.json +2 -2
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,19 +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
|
-
const
|
|
4079
|
-
|
|
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}`;
|
|
4080
4553
|
}
|
|
4081
4554
|
if (event.payload.after.mode === "grid-span") {
|
|
4082
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}`;
|
|
4083
4556
|
}
|
|
4084
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}`;
|
|
4085
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
|
+
}
|
|
4086
4565
|
function rebuildNodeEdit(orderedChanges, nodeKey) {
|
|
4087
4566
|
let latestMove;
|
|
4088
4567
|
let latestResize;
|
|
@@ -4097,11 +4576,36 @@ function rebuildNodeEdit(orderedChanges, nodeKey) {
|
|
|
4097
4576
|
nodeId = ev.nodeId;
|
|
4098
4577
|
inspectorRef = ev.inspectorRef;
|
|
4099
4578
|
if (ev.kind === "move") latestMove = ev.payload;
|
|
4100
|
-
else latestResize = ev.payload;
|
|
4579
|
+
else if (ev.kind === "resize") latestResize = ev.payload;
|
|
4101
4580
|
}
|
|
4102
4581
|
if (count === 0) return null;
|
|
4103
4582
|
return { nodeKey, nodeId, inspectorRef, latestMove, latestResize, changeCount: count, lastUpdatedAt: lastAt };
|
|
4104
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
|
+
}
|
|
4105
4609
|
var useDesignModeStore = create((set, get) => ({
|
|
4106
4610
|
// UI state
|
|
4107
4611
|
enabled: false,
|
|
@@ -4117,8 +4621,10 @@ var useDesignModeStore = create((set, get) => ({
|
|
|
4117
4621
|
nodeEditsByKey: {},
|
|
4118
4622
|
orderedChanges: [],
|
|
4119
4623
|
undoneChanges: [],
|
|
4624
|
+
// External drag
|
|
4625
|
+
externalDrag: null,
|
|
4120
4626
|
// UI actions
|
|
4121
|
-
toggle: () => set((s) => ({ enabled: !s.enabled })),
|
|
4627
|
+
toggle: () => set((s) => ({ enabled: !s.enabled, ...buildEmptyArrangeSessionState() })),
|
|
4122
4628
|
setEnabled: (enabled) => set({ enabled }),
|
|
4123
4629
|
setHovered: (selector) => set({ hoveredSelector: selector }),
|
|
4124
4630
|
setSelected: (selector) => set({ selectedSelector: selector }),
|
|
@@ -4133,6 +4639,7 @@ var useDesignModeStore = create((set, get) => ({
|
|
|
4133
4639
|
canUndo: false,
|
|
4134
4640
|
canRedo: false
|
|
4135
4641
|
}),
|
|
4642
|
+
resetArrangeSession: () => set(buildEmptyArrangeSessionState()),
|
|
4136
4643
|
// Change tracking actions
|
|
4137
4644
|
recordMoveChange: ({
|
|
4138
4645
|
operationId,
|
|
@@ -4145,18 +4652,24 @@ var useDesignModeStore = create((set, get) => ({
|
|
|
4145
4652
|
selectedInspectorRef,
|
|
4146
4653
|
anchorNodeKey,
|
|
4147
4654
|
anchorNodeId,
|
|
4655
|
+
sourceContainer,
|
|
4656
|
+
sourceBeforeSibling,
|
|
4657
|
+
sourceAfterSibling,
|
|
4148
4658
|
container,
|
|
4149
4659
|
beforeSibling,
|
|
4150
4660
|
afterSibling,
|
|
4661
|
+
diagnostics,
|
|
4151
4662
|
componentName,
|
|
4152
4663
|
before,
|
|
4153
4664
|
after
|
|
4154
4665
|
}) => {
|
|
4155
4666
|
const now = Date.now();
|
|
4667
|
+
const normalizedOperationId = operationId ?? buildFallbackOperationId(now, nodeId);
|
|
4668
|
+
const normalizedCommandType = commandType ?? inferMoveCommandType(before, after);
|
|
4156
4669
|
const event = {
|
|
4157
4670
|
kind: "move",
|
|
4158
|
-
operationId:
|
|
4159
|
-
commandType:
|
|
4671
|
+
operationId: normalizedOperationId,
|
|
4672
|
+
commandType: normalizedCommandType,
|
|
4160
4673
|
nodeKey,
|
|
4161
4674
|
nodeId,
|
|
4162
4675
|
inspectorRef,
|
|
@@ -4165,9 +4678,13 @@ var useDesignModeStore = create((set, get) => ({
|
|
|
4165
4678
|
selectedInspectorRef,
|
|
4166
4679
|
anchorNodeKey,
|
|
4167
4680
|
anchorNodeId,
|
|
4681
|
+
sourceContainer: sourceContainer ?? null,
|
|
4682
|
+
sourceBeforeSibling: sourceBeforeSibling ?? null,
|
|
4683
|
+
sourceAfterSibling: sourceAfterSibling ?? null,
|
|
4168
4684
|
container: container ?? null,
|
|
4169
4685
|
beforeSibling: beforeSibling ?? null,
|
|
4170
4686
|
afterSibling: afterSibling ?? null,
|
|
4687
|
+
diagnostics: diagnostics ?? null,
|
|
4171
4688
|
payload: { before, after },
|
|
4172
4689
|
at: now
|
|
4173
4690
|
};
|
|
@@ -4190,11 +4707,20 @@ var useDesignModeStore = create((set, get) => ({
|
|
|
4190
4707
|
};
|
|
4191
4708
|
});
|
|
4192
4709
|
},
|
|
4193
|
-
recordResizeChange: ({
|
|
4710
|
+
recordResizeChange: ({
|
|
4711
|
+
operationId,
|
|
4712
|
+
nodeKey,
|
|
4713
|
+
nodeId,
|
|
4714
|
+
inspectorRef,
|
|
4715
|
+
componentName,
|
|
4716
|
+
before,
|
|
4717
|
+
after
|
|
4718
|
+
}) => {
|
|
4194
4719
|
const now = Date.now();
|
|
4720
|
+
const normalizedOperationId = operationId ?? buildFallbackOperationId(now, nodeId);
|
|
4195
4721
|
const event = {
|
|
4196
4722
|
kind: "resize",
|
|
4197
|
-
operationId:
|
|
4723
|
+
operationId: normalizedOperationId,
|
|
4198
4724
|
nodeKey,
|
|
4199
4725
|
nodeId,
|
|
4200
4726
|
inspectorRef,
|
|
@@ -4220,6 +4746,52 @@ var useDesignModeStore = create((set, get) => ({
|
|
|
4220
4746
|
};
|
|
4221
4747
|
});
|
|
4222
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 }),
|
|
4223
4795
|
undoLastChange: () => {
|
|
4224
4796
|
const { orderedChanges, undoneChanges } = get();
|
|
4225
4797
|
if (orderedChanges.length === 0) return null;
|
|
@@ -4245,7 +4817,8 @@ var useDesignModeStore = create((set, get) => ({
|
|
|
4245
4817
|
},
|
|
4246
4818
|
clearChanges: () => set({ orderedChanges: [], undoneChanges: [], nodeEditsByKey: {} }),
|
|
4247
4819
|
exportChangesForAI: () => {
|
|
4248
|
-
const { orderedChanges, nodeEditsByKey } = get();
|
|
4820
|
+
const { orderedChanges: rawChanges, nodeEditsByKey } = get();
|
|
4821
|
+
const orderedChanges = coalesceOrderedChanges(rawChanges);
|
|
4249
4822
|
const changesByFile = {};
|
|
4250
4823
|
for (const event of orderedChanges) {
|
|
4251
4824
|
const fileKey = event.inspectorRef?.relativePath ?? "__runtime__";
|
|
@@ -4259,180 +4832,127 @@ var useDesignModeStore = create((set, get) => ({
|
|
|
4259
4832
|
lines.push(` ${summarizeEditorChangeEvent(ev)}`);
|
|
4260
4833
|
}
|
|
4261
4834
|
}
|
|
4835
|
+
const diagnostics = mergeRuntimeArrangeDiagnostics(
|
|
4836
|
+
orderedChanges.map(
|
|
4837
|
+
(event) => event.kind === "move" ? event.diagnostics : null
|
|
4838
|
+
)
|
|
4839
|
+
);
|
|
4262
4840
|
return {
|
|
4263
4841
|
changesByFile,
|
|
4264
4842
|
finalSnapshot: { ...nodeEditsByKey },
|
|
4265
|
-
aiPromptContext: lines.join("\n")
|
|
4843
|
+
aiPromptContext: lines.join("\n"),
|
|
4844
|
+
...diagnostics ? { diagnostics } : {}
|
|
4266
4845
|
};
|
|
4267
4846
|
}
|
|
4268
4847
|
}));
|
|
4269
4848
|
|
|
4270
4849
|
// src/client/dnd/design-mode/DesignModeOverlay.tsx
|
|
4271
|
-
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";
|
|
4272
4851
|
|
|
4273
4852
|
// src/client/dnd/design-mode/useElementScanner.ts
|
|
4274
4853
|
import { useState as useState4, useEffect as useEffect4, useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
4275
4854
|
|
|
4276
|
-
// src/client/dnd/design-mode/
|
|
4277
|
-
var
|
|
4278
|
-
var
|
|
4279
|
-
|
|
4280
|
-
|
|
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));
|
|
4281
4862
|
}
|
|
4282
|
-
function
|
|
4283
|
-
|
|
4284
|
-
const tag = el.tagName.toLowerCase();
|
|
4285
|
-
if (STRUCTURAL_TAGS.has(tag)) return true;
|
|
4286
|
-
if (el.id === "root") return true;
|
|
4287
|
-
return tag === "main" && (el.parentElement === document.body || el.parentElement?.id === "root");
|
|
4863
|
+
function buildNodeSelector(nodeId) {
|
|
4864
|
+
return `[${DND_NODE_ID_ATTR}="${escapeCssAttrValue(nodeId)}"]`;
|
|
4288
4865
|
}
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
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";
|
|
4293
4872
|
}
|
|
4294
|
-
function
|
|
4295
|
-
return
|
|
4873
|
+
function isPointerInEdgeZone(px, py, guard) {
|
|
4874
|
+
return px <= guard || py <= guard || px >= window.innerWidth - guard || py >= window.innerHeight - guard;
|
|
4296
4875
|
}
|
|
4297
|
-
function
|
|
4298
|
-
|
|
4299
|
-
if (!container) return false;
|
|
4300
|
-
if (container.children.length < 2) return false;
|
|
4301
|
-
return !isStructuralContainer(containerId, nodeMap);
|
|
4876
|
+
function isPointerInPageEdgeZone(px, py) {
|
|
4877
|
+
return isPointerInEdgeZone(px, py, PAGE_EDGE_GUARD);
|
|
4302
4878
|
}
|
|
4303
|
-
function
|
|
4304
|
-
|
|
4305
|
-
for (const containerId of projection.containerIndex.keys()) {
|
|
4306
|
-
if (containerId === sourceContainerId) continue;
|
|
4307
|
-
if (isStructuralContainer(containerId, nodeMap)) blocked.add(containerId);
|
|
4308
|
-
}
|
|
4309
|
-
return blocked;
|
|
4879
|
+
function isPointerInHardPageEdgeZone(px, py) {
|
|
4880
|
+
return isPointerInEdgeZone(px, py, HARD_PAGE_EDGE_GUARD);
|
|
4310
4881
|
}
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
const
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
}
|
|
4322
|
-
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 };
|
|
4323
4892
|
}
|
|
4324
|
-
function
|
|
4325
|
-
|
|
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;
|
|
4326
4896
|
}
|
|
4327
|
-
function
|
|
4328
|
-
return
|
|
4897
|
+
function isPointerOutsideSafeZone(px, py, safeZone) {
|
|
4898
|
+
return !isPointerInSafeZone(px, py, safeZone);
|
|
4329
4899
|
}
|
|
4330
|
-
function
|
|
4331
|
-
|
|
4332
|
-
return meta.componentName[0] !== meta.componentName[0].toLowerCase();
|
|
4900
|
+
function isPointerBlocked(px, py, safeZone) {
|
|
4901
|
+
return isPointerInHardPageEdgeZone(px, py) || isPointerOutsideSafeZone(px, py, safeZone);
|
|
4333
4902
|
}
|
|
4334
|
-
function
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
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;
|
|
4338
4913
|
}
|
|
4339
|
-
return
|
|
4914
|
+
return null;
|
|
4340
4915
|
}
|
|
4341
|
-
function
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
if (child.hasAttribute("data-design-mode-ui")) return false;
|
|
4345
|
-
if (child.hasAttribute("data-design-mode-allow")) return false;
|
|
4346
|
-
return !shouldIgnoreAsVirtualRuntime(child);
|
|
4347
|
-
});
|
|
4916
|
+
function escapeCssAttrValue(value) {
|
|
4917
|
+
const esc = globalThis.CSS?.escape;
|
|
4918
|
+
return esc ? esc(value) : value.replace(/["\\]/g, "\\$&");
|
|
4348
4919
|
}
|
|
4349
|
-
function
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
if (tag === "script" || tag === "style" || tag === "noscript") return true;
|
|
4353
|
-
if (el instanceof SVGElement && !isRootSvg(el)) return true;
|
|
4354
|
-
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]");
|
|
4355
4923
|
}
|
|
4356
|
-
function
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
const
|
|
4360
|
-
const
|
|
4361
|
-
|
|
4362
|
-
directMetaCache.set(el, resolved);
|
|
4363
|
-
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;
|
|
4364
4930
|
}
|
|
4365
|
-
function
|
|
4366
|
-
const
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
metaCache.set(el, meta);
|
|
4373
|
-
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;
|
|
4374
4938
|
}
|
|
4375
|
-
fiber = fiber.return;
|
|
4376
4939
|
}
|
|
4377
|
-
metaCache.set(el, null);
|
|
4378
4940
|
return null;
|
|
4379
4941
|
}
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
}
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
const ownerMeta = directMeta ?? getEditorMeta(el);
|
|
4392
|
-
if (isPassthroughMeta(ownerMeta)) {
|
|
4393
|
-
const children = getCandidateChildren(el);
|
|
4394
|
-
if (children.length !== 1) return null;
|
|
4395
|
-
return children[0];
|
|
4396
|
-
}
|
|
4397
|
-
if (directMeta && !isWrapperMeta(directMeta)) return el;
|
|
4398
|
-
const MAX_STEPS = 10;
|
|
4399
|
-
const ownerNodeId = ownerMeta?.nodeId ?? null;
|
|
4400
|
-
let outermost = el;
|
|
4401
|
-
let parent = el.parentElement;
|
|
4402
|
-
for (let i = 0; i < MAX_STEPS; i += 1) {
|
|
4403
|
-
if (!parent || parent === document.body) break;
|
|
4404
|
-
if (shouldIgnoreAsVirtualRuntime(parent)) break;
|
|
4405
|
-
const parentMeta = getEditorMeta(parent);
|
|
4406
|
-
if (!parentMeta) break;
|
|
4407
|
-
if (ownerNodeId && parentMeta.nodeId !== ownerNodeId) break;
|
|
4408
|
-
if (isStructuralComponentName(parentMeta.componentName)) break;
|
|
4409
|
-
if (!isWrapperMeta(parentMeta)) break;
|
|
4410
|
-
if (isPassthroughMeta(parentMeta)) break;
|
|
4411
|
-
if (hasOwnTextContent(parent)) break;
|
|
4412
|
-
const children = getCandidateChildren(parent);
|
|
4413
|
-
if (children.length !== 1) break;
|
|
4414
|
-
outermost = parent;
|
|
4415
|
-
parent = parent.parentElement;
|
|
4416
|
-
}
|
|
4417
|
-
return outermost;
|
|
4418
|
-
}
|
|
4419
|
-
function resolveDragSurface(el) {
|
|
4420
|
-
const host = resolveHostElementFromFiberChain(el);
|
|
4421
|
-
if (!host) {
|
|
4422
|
-
return { host: null, metaOwner: null, kind: "direct-host" };
|
|
4423
|
-
}
|
|
4424
|
-
const metaOwner = findMetaOwnerElement(host) ?? findMetaOwnerElement(el);
|
|
4425
|
-
const ownerMeta = metaOwner ? getEditorMeta(metaOwner) : null;
|
|
4426
|
-
const kind = isPassthroughMeta(ownerMeta) ? "passthrough-slot" : metaOwner && metaOwner !== host && isWrapperMeta(ownerMeta) ? "wrapper-to-host" : "direct-host";
|
|
4427
|
-
return {
|
|
4428
|
-
host,
|
|
4429
|
-
metaOwner,
|
|
4430
|
-
kind
|
|
4431
|
-
};
|
|
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;
|
|
4432
4953
|
}
|
|
4433
4954
|
|
|
4434
4955
|
// src/client/dnd/design-mode/useElementScanner.ts
|
|
4435
|
-
var DND_NODE_ID_ATTR = "data-dnd-node-id";
|
|
4436
4956
|
function resolveContainerStrategy(el) {
|
|
4437
4957
|
const cs = getComputedStyle(el);
|
|
4438
4958
|
const d = cs.display;
|
|
@@ -4463,7 +4983,7 @@ function getRuntimeNodeId(el) {
|
|
|
4463
4983
|
}
|
|
4464
4984
|
function getEditorMetaNodeId(el) {
|
|
4465
4985
|
if (!(el instanceof HTMLElement)) return null;
|
|
4466
|
-
return
|
|
4986
|
+
return getDirectEditorMeta(el)?.nodeId ?? null;
|
|
4467
4987
|
}
|
|
4468
4988
|
function isInspectorEligible(el) {
|
|
4469
4989
|
if (getEditorMetaNodeId(el) !== null) return "editor-meta";
|
|
@@ -4487,7 +5007,7 @@ function inferEligibilityFromSortableParent(el) {
|
|
|
4487
5007
|
if (child.hasAttribute("data-design-mode-ui")) return false;
|
|
4488
5008
|
return !isRuntimeVisualInternal(child);
|
|
4489
5009
|
});
|
|
4490
|
-
if (siblingElements.length <
|
|
5010
|
+
if (siblingElements.length < 1) return null;
|
|
4491
5011
|
return "inferred-sortable-descendant";
|
|
4492
5012
|
}
|
|
4493
5013
|
function ensureUniqueNodeId(base, used) {
|
|
@@ -5041,7 +5561,15 @@ function buildProjection(elements, rectOverrides) {
|
|
|
5041
5561
|
}
|
|
5042
5562
|
const containerIndex = /* @__PURE__ */ new Map();
|
|
5043
5563
|
for (const node of nodeMap.values()) {
|
|
5044
|
-
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
|
+
}
|
|
5045
5573
|
node.sortable = true;
|
|
5046
5574
|
const firstChild = elementIndex.get(node.children[0]);
|
|
5047
5575
|
node.strategy = firstChild?.containerStrategy ?? "vertical";
|
|
@@ -5112,31 +5640,25 @@ function buildVerticalSlotRect(children, nodeMap, cRect, i, n) {
|
|
|
5112
5640
|
const top = i === 0 ? cRect.top : nodeMap.get(children[i - 1])?.rect.bottom ?? cRect.top;
|
|
5113
5641
|
const bottom = i === n ? cRect.bottom : nodeMap.get(children[i])?.rect.top ?? cRect.bottom;
|
|
5114
5642
|
const height = Math.max(0, bottom - top);
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
width: cRect.width,
|
|
5123
|
-
height
|
|
5124
|
-
};
|
|
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 };
|
|
5125
5650
|
}
|
|
5126
5651
|
function buildHorizontalSlotRect(children, nodeMap, cRect, i, n) {
|
|
5127
5652
|
const left = i === 0 ? cRect.left : nodeMap.get(children[i - 1])?.rect.right ?? cRect.left;
|
|
5128
5653
|
const right = i === n ? cRect.right : nodeMap.get(children[i])?.rect.left ?? cRect.right;
|
|
5129
5654
|
const width = Math.max(0, right - left);
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
width,
|
|
5138
|
-
height: cRect.height
|
|
5139
|
-
};
|
|
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 };
|
|
5140
5662
|
}
|
|
5141
5663
|
function buildRectSlotRect(children, nodeMap, cRect, i, n, rows) {
|
|
5142
5664
|
const findRow = (childIdx) => rows.find((r) => childIdx >= r.startIdx && childIdx <= r.endIdx) ?? null;
|
|
@@ -5200,6 +5722,79 @@ function buildRectSlotRect(children, nodeMap, cRect, i, n, rows) {
|
|
|
5200
5722
|
};
|
|
5201
5723
|
}
|
|
5202
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
|
+
|
|
5203
5798
|
// src/client/dnd/design-mode/resize.ts
|
|
5204
5799
|
function splitTopLevelSpaceSeparated(value) {
|
|
5205
5800
|
const tokens = [];
|
|
@@ -5231,9 +5826,6 @@ function parseSpanValue(value) {
|
|
|
5231
5826
|
const parsed = Number.parseInt(match[1], 10);
|
|
5232
5827
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : 1;
|
|
5233
5828
|
}
|
|
5234
|
-
function clamp(value, min, max) {
|
|
5235
|
-
return Math.max(min, Math.min(max, value));
|
|
5236
|
-
}
|
|
5237
5829
|
function resolveResizeTargetElement(selectedEl) {
|
|
5238
5830
|
const elementChildren = Array.from(selectedEl.children).filter(
|
|
5239
5831
|
(child2) => child2 instanceof HTMLElement
|
|
@@ -5314,100 +5906,6 @@ function applyStyleSnapshot(el, snapshot) {
|
|
|
5314
5906
|
}
|
|
5315
5907
|
}
|
|
5316
5908
|
|
|
5317
|
-
// src/client/dnd/design-mode/helpers.ts
|
|
5318
|
-
var DND_NODE_ID_ATTR2 = "data-dnd-node-id";
|
|
5319
|
-
var PAGE_EDGE_GUARD = 10;
|
|
5320
|
-
var HARD_PAGE_EDGE_GUARD = 4;
|
|
5321
|
-
var SAFE_ZONE_INSET = 8;
|
|
5322
|
-
var LARGE_NODE_NEST_BLOCK_MIN_WIDTH = 520;
|
|
5323
|
-
var LARGE_NODE_NEST_BLOCK_MIN_HEIGHT = 260;
|
|
5324
|
-
var LARGE_NODE_NEST_BLOCK_MIN_AREA = 18e4;
|
|
5325
|
-
function isEligibleForDrag(sc) {
|
|
5326
|
-
const source = sc.eligibilitySource ?? "editor-meta";
|
|
5327
|
-
return source === "editor-meta" || source === "force-allow" || source === "inferred-sortable-descendant";
|
|
5328
|
-
}
|
|
5329
|
-
function isPointerInPageEdgeZone(px, py) {
|
|
5330
|
-
return px <= PAGE_EDGE_GUARD || py <= PAGE_EDGE_GUARD || px >= window.innerWidth - PAGE_EDGE_GUARD || py >= window.innerHeight - PAGE_EDGE_GUARD;
|
|
5331
|
-
}
|
|
5332
|
-
function isPointerInHardPageEdgeZone(px, py) {
|
|
5333
|
-
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;
|
|
5334
|
-
}
|
|
5335
|
-
function resolveSafeZoneRect() {
|
|
5336
|
-
const root = document.getElementById("root");
|
|
5337
|
-
if (!root) return null;
|
|
5338
|
-
const rect = root.getBoundingClientRect();
|
|
5339
|
-
const left = rect.left + SAFE_ZONE_INSET;
|
|
5340
|
-
const top = rect.top + SAFE_ZONE_INSET;
|
|
5341
|
-
const right = rect.right - SAFE_ZONE_INSET;
|
|
5342
|
-
const bottom = rect.bottom - SAFE_ZONE_INSET;
|
|
5343
|
-
if (right <= left || bottom <= top) return null;
|
|
5344
|
-
return { left, top, right, bottom };
|
|
5345
|
-
}
|
|
5346
|
-
function isPointerInSafeZone(px, py, safeZone) {
|
|
5347
|
-
if (!safeZone) return true;
|
|
5348
|
-
return px >= safeZone.left && px <= safeZone.right && py >= safeZone.top && py <= safeZone.bottom;
|
|
5349
|
-
}
|
|
5350
|
-
function isPointerOutsideSafeZone(px, py, safeZone) {
|
|
5351
|
-
return !isPointerInSafeZone(px, py, safeZone);
|
|
5352
|
-
}
|
|
5353
|
-
function isPointerBlocked(px, py, safeZone) {
|
|
5354
|
-
return isPointerInHardPageEdgeZone(px, py) || isPointerOutsideSafeZone(px, py, safeZone);
|
|
5355
|
-
}
|
|
5356
|
-
function shouldBlockNestForLargeNode(w, h) {
|
|
5357
|
-
return w >= LARGE_NODE_NEST_BLOCK_MIN_WIDTH || h >= LARGE_NODE_NEST_BLOCK_MIN_HEIGHT || w * h >= LARGE_NODE_NEST_BLOCK_MIN_AREA;
|
|
5358
|
-
}
|
|
5359
|
-
function findScrollableAncestor(start, dx, dy) {
|
|
5360
|
-
let el = start;
|
|
5361
|
-
while (el && el !== document.body) {
|
|
5362
|
-
const cs = getComputedStyle(el);
|
|
5363
|
-
if (dy !== 0 && (cs.overflowY === "auto" || cs.overflowY === "scroll") && el.scrollHeight > el.clientHeight + 1) return el;
|
|
5364
|
-
if (dx !== 0 && (cs.overflowX === "auto" || cs.overflowX === "scroll") && el.scrollWidth > el.clientWidth + 1) return el;
|
|
5365
|
-
el = el.parentElement;
|
|
5366
|
-
}
|
|
5367
|
-
return null;
|
|
5368
|
-
}
|
|
5369
|
-
function escapeCssAttrValue(value) {
|
|
5370
|
-
const esc = globalThis.CSS?.escape;
|
|
5371
|
-
return esc ? esc(value) : value.replace(/["\\]/g, "\\$&");
|
|
5372
|
-
}
|
|
5373
|
-
function isDesignModeUiElement(el) {
|
|
5374
|
-
if (!(el instanceof HTMLElement)) return false;
|
|
5375
|
-
return el.hasAttribute("data-design-mode-ui") || !!el.closest("[data-design-mode-ui]");
|
|
5376
|
-
}
|
|
5377
|
-
function isElementScrollable(el) {
|
|
5378
|
-
if (!(el instanceof HTMLElement)) return false;
|
|
5379
|
-
const cs = getComputedStyle(el);
|
|
5380
|
-
const overY = cs.overflowY;
|
|
5381
|
-
const overX = cs.overflowX;
|
|
5382
|
-
return (overY === "auto" || overY === "scroll") && el.scrollHeight > el.clientHeight || (overX === "auto" || overX === "scroll") && el.scrollWidth > el.clientWidth;
|
|
5383
|
-
}
|
|
5384
|
-
function resolveScrollableFromHitStack(stack) {
|
|
5385
|
-
for (const hit of stack) {
|
|
5386
|
-
if (isDesignModeUiElement(hit)) continue;
|
|
5387
|
-
let el = hit;
|
|
5388
|
-
while (el) {
|
|
5389
|
-
if (isElementScrollable(el)) return el;
|
|
5390
|
-
el = el.parentElement;
|
|
5391
|
-
}
|
|
5392
|
-
}
|
|
5393
|
-
return null;
|
|
5394
|
-
}
|
|
5395
|
-
var _undoParentNodeIdMap = /* @__PURE__ */ new WeakMap();
|
|
5396
|
-
var _undoParentNodeIdCounter = 0;
|
|
5397
|
-
function ensureParentNodeId(el) {
|
|
5398
|
-
const existing = el.getAttribute(DND_NODE_ID_ATTR2);
|
|
5399
|
-
if (existing) return existing;
|
|
5400
|
-
if (!_undoParentNodeIdMap.has(el)) {
|
|
5401
|
-
_undoParentNodeIdMap.set(el, `dnd-parent:${++_undoParentNodeIdCounter}`);
|
|
5402
|
-
}
|
|
5403
|
-
const id = _undoParentNodeIdMap.get(el);
|
|
5404
|
-
el.setAttribute(DND_NODE_ID_ATTR2, id);
|
|
5405
|
-
return id;
|
|
5406
|
-
}
|
|
5407
|
-
function ensureElementNodeId(el) {
|
|
5408
|
-
return ensureParentNodeId(el);
|
|
5409
|
-
}
|
|
5410
|
-
|
|
5411
5909
|
// src/client/dnd/design-mode/drag-preview.tsx
|
|
5412
5910
|
import { useRef as useRef7, useEffect as useEffect6 } from "react";
|
|
5413
5911
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
@@ -5508,7 +6006,7 @@ function findNearestContainerId(nodeId, projection, nodeMap, allowStructuralFall
|
|
|
5508
6006
|
const parentId = node?.parentId ?? null;
|
|
5509
6007
|
if (!parentId) break;
|
|
5510
6008
|
const parentContainer = projection.containerIndex.get(parentId);
|
|
5511
|
-
if (parentContainer && parentContainer.children.length >=
|
|
6009
|
+
if (parentContainer && parentContainer.children.length >= 1) {
|
|
5512
6010
|
if (isValidLaneContainer(parentId, projection, nodeMap)) return parentId;
|
|
5513
6011
|
if (allowStructuralFallback && !structuralFallback) structuralFallback = parentId;
|
|
5514
6012
|
}
|
|
@@ -5593,12 +6091,6 @@ function findDirectChildUnderParent(child, parent) {
|
|
|
5593
6091
|
}
|
|
5594
6092
|
return null;
|
|
5595
6093
|
}
|
|
5596
|
-
function isContiguous(indices) {
|
|
5597
|
-
if (indices.length === 0) return false;
|
|
5598
|
-
const min = Math.min(...indices);
|
|
5599
|
-
const max = Math.max(...indices);
|
|
5600
|
-
return max - min + 1 === indices.length;
|
|
5601
|
-
}
|
|
5602
6094
|
function createPlacement(parentNodeId, laneContainerId, commitNodeId, beforeNodeId, afterNodeId, index) {
|
|
5603
6095
|
return {
|
|
5604
6096
|
parentNodeId,
|
|
@@ -5612,7 +6104,16 @@ function createPlacement(parentNodeId, laneContainerId, commitNodeId, beforeNode
|
|
|
5612
6104
|
function buildCommitLaneSnapshot(laneContainerId, projection, nodeMap) {
|
|
5613
6105
|
const lane = projection.containerIndex.get(laneContainerId);
|
|
5614
6106
|
const laneElement = nodeMap.get(laneContainerId)?.element ?? null;
|
|
5615
|
-
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
|
+
}
|
|
5616
6117
|
const childElements = lane.children.map((childId) => nodeMap.get(childId)?.element ?? null).filter((el) => !!el);
|
|
5617
6118
|
if (childElements.length !== lane.children.length) return null;
|
|
5618
6119
|
const commitParent = findLowestCommonAncestor(childElements, laneElement);
|
|
@@ -5629,19 +6130,18 @@ function buildCommitLaneSnapshot(laneContainerId, projection, nodeMap) {
|
|
|
5629
6130
|
if (seenRoots.has(commitRoot)) return null;
|
|
5630
6131
|
seenRoots.add(commitRoot);
|
|
5631
6132
|
commitRoots.push(commitRoot);
|
|
5632
|
-
commitNodeByAnchorId.set(anchorNodeId,
|
|
6133
|
+
commitNodeByAnchorId.set(anchorNodeId, ensureParentNodeId(commitRoot));
|
|
5633
6134
|
}
|
|
5634
6135
|
const childList = Array.from(commitParent.children).filter(
|
|
5635
6136
|
(child) => child instanceof HTMLElement
|
|
5636
6137
|
);
|
|
5637
6138
|
const commitRootIndices = commitRoots.map((root) => childList.indexOf(root));
|
|
5638
6139
|
if (commitRootIndices.some((index) => index < 0)) return null;
|
|
5639
|
-
if (!isContiguous(commitRootIndices)) return null;
|
|
5640
6140
|
const domOrderedRoots = [...commitRoots].sort(
|
|
5641
6141
|
(a, b) => childList.indexOf(a) - childList.indexOf(b)
|
|
5642
6142
|
);
|
|
5643
|
-
const projectionOrderedIds = commitRoots.map((root) =>
|
|
5644
|
-
const domOrderedIds = domOrderedRoots.map((root) =>
|
|
6143
|
+
const projectionOrderedIds = commitRoots.map((root) => ensureParentNodeId(root));
|
|
6144
|
+
const domOrderedIds = domOrderedRoots.map((root) => ensureParentNodeId(root));
|
|
5645
6145
|
if (projectionOrderedIds.length !== domOrderedIds.length) return null;
|
|
5646
6146
|
if (projectionOrderedIds.some((id, index) => id !== domOrderedIds[index])) return null;
|
|
5647
6147
|
return {
|
|
@@ -5686,7 +6186,7 @@ function resolveAppendCommitPlacement(parentEl, commitNodeId, laneContainerId) {
|
|
|
5686
6186
|
const children = Array.from(parentEl.children).filter(
|
|
5687
6187
|
(child) => child instanceof HTMLElement
|
|
5688
6188
|
);
|
|
5689
|
-
const lastChild = children.length > 0 ?
|
|
6189
|
+
const lastChild = children.length > 0 ? ensureParentNodeId(children[children.length - 1]) : null;
|
|
5690
6190
|
return createPlacement(
|
|
5691
6191
|
ensureParentNodeId(parentEl),
|
|
5692
6192
|
laneContainerId,
|
|
@@ -5741,6 +6241,58 @@ function createDesignModeCollisionDetection(blockedContainerIds) {
|
|
|
5741
6241
|
};
|
|
5742
6242
|
}
|
|
5743
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
|
+
|
|
5744
6296
|
// src/client/dnd/design-mode/useDesignModeAutoScroll.ts
|
|
5745
6297
|
import { useEffect as useEffect7 } from "react";
|
|
5746
6298
|
function edgeVelocity(distanceToEdge, zone, maxSpeed) {
|
|
@@ -5756,11 +6308,16 @@ function findScrollableAtPoint(x, y) {
|
|
|
5756
6308
|
return resolveScrollableFromHitStack(stack) ?? (document.scrollingElement ?? document.documentElement);
|
|
5757
6309
|
}
|
|
5758
6310
|
function getScrollerKey(el) {
|
|
5759
|
-
return el.getAttribute?.(
|
|
6311
|
+
return el.getAttribute?.(DND_NODE_ID_ATTR) ?? el.id ?? el.tagName.toLowerCase();
|
|
5760
6312
|
}
|
|
5761
6313
|
function pointerInsideRect(x, y, rect, inset = 0) {
|
|
5762
6314
|
return x >= rect.left - inset && x <= rect.right + inset && y >= rect.top - inset && y <= rect.bottom + inset;
|
|
5763
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;
|
|
5764
6321
|
function useDesignModeAutoScroll({
|
|
5765
6322
|
active,
|
|
5766
6323
|
mouseRef,
|
|
@@ -5769,11 +6326,6 @@ function useDesignModeAutoScroll({
|
|
|
5769
6326
|
}) {
|
|
5770
6327
|
useEffect7(() => {
|
|
5771
6328
|
if (!active) return;
|
|
5772
|
-
const ZONE = 120;
|
|
5773
|
-
const MAX = 24;
|
|
5774
|
-
const RECALC_INTERVAL = 8;
|
|
5775
|
-
const SWITCH_FRAMES = 3;
|
|
5776
|
-
const SCROLLER_EDGE_HYSTERESIS = 18;
|
|
5777
6329
|
let scrollEl = findScrollableAtPoint(mouseRef.current.x, mouseRef.current.y);
|
|
5778
6330
|
let cachedRect = scrollEl.getBoundingClientRect();
|
|
5779
6331
|
let rectAge = 0;
|
|
@@ -5781,17 +6333,17 @@ function useDesignModeAutoScroll({
|
|
|
5781
6333
|
const tick = () => {
|
|
5782
6334
|
const { x, y } = mouseRef.current;
|
|
5783
6335
|
const safeZone = safeZoneRef.current;
|
|
5784
|
-
if (++rectAge >
|
|
6336
|
+
if (++rectAge > SCROLLER_RECT_RECALC_INTERVAL) {
|
|
5785
6337
|
const candidate = findScrollableAtPoint(x, y);
|
|
5786
6338
|
const candidateKey = getScrollerKey(candidate);
|
|
5787
6339
|
const currentKey = getScrollerKey(scrollEl);
|
|
5788
6340
|
const currentRect = scrollEl.getBoundingClientRect();
|
|
5789
|
-
if (candidateKey !== currentKey && !pointerInsideRect(x, y, currentRect,
|
|
6341
|
+
if (candidateKey !== currentKey && !pointerInsideRect(x, y, currentRect, SCROLLER_EDGE_HYSTERESIS_PX)) {
|
|
5790
6342
|
const pendingKey = autoScrollStateRef.current.pendingScrollerKey;
|
|
5791
6343
|
const pendingFrames = pendingKey === candidateKey ? (autoScrollStateRef.current.pendingFrames ?? 0) + 1 : 1;
|
|
5792
6344
|
autoScrollStateRef.current.pendingScrollerKey = candidateKey;
|
|
5793
6345
|
autoScrollStateRef.current.pendingFrames = pendingFrames;
|
|
5794
|
-
if (pendingFrames >=
|
|
6346
|
+
if (pendingFrames >= SCROLLER_SWITCH_FRAMES) {
|
|
5795
6347
|
scrollEl = candidate;
|
|
5796
6348
|
cachedRect = candidate.getBoundingClientRect();
|
|
5797
6349
|
autoScrollStateRef.current.pendingScrollerKey = null;
|
|
@@ -5816,82 +6368,36 @@ function useDesignModeAutoScroll({
|
|
|
5816
6368
|
return;
|
|
5817
6369
|
}
|
|
5818
6370
|
const dTop = y - cachedRect.top;
|
|
5819
|
-
const dBot = cachedRect.bottom - y;
|
|
5820
|
-
let dy = 0;
|
|
5821
|
-
if (dTop > 0 && dTop <
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
if (
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
}
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
constructor(limit = DEFAULT_LIMIT) {
|
|
5850
|
-
this.limit = limit;
|
|
5851
|
-
__publicField(this, "undoStack", []);
|
|
5852
|
-
__publicField(this, "redoStack", []);
|
|
5853
|
-
}
|
|
5854
|
-
execute(command, apply) {
|
|
5855
|
-
const applied = apply(command, "redo");
|
|
5856
|
-
if (!applied) return false;
|
|
5857
|
-
this.undoStack.push(command);
|
|
5858
|
-
if (this.undoStack.length > this.limit) this.undoStack.shift();
|
|
5859
|
-
this.redoStack = [];
|
|
5860
|
-
return true;
|
|
5861
|
-
}
|
|
5862
|
-
undo(apply) {
|
|
5863
|
-
const command = this.undoStack.pop();
|
|
5864
|
-
if (!command) return false;
|
|
5865
|
-
const applied = apply(command, "undo");
|
|
5866
|
-
if (!applied) {
|
|
5867
|
-
this.undoStack.push(command);
|
|
5868
|
-
return false;
|
|
5869
|
-
}
|
|
5870
|
-
this.redoStack.push(command);
|
|
5871
|
-
return true;
|
|
5872
|
-
}
|
|
5873
|
-
redo(apply) {
|
|
5874
|
-
const command = this.redoStack.pop();
|
|
5875
|
-
if (!command) return false;
|
|
5876
|
-
const applied = apply(command, "redo");
|
|
5877
|
-
if (!applied) {
|
|
5878
|
-
this.redoStack.push(command);
|
|
5879
|
-
return false;
|
|
5880
|
-
}
|
|
5881
|
-
this.undoStack.push(command);
|
|
5882
|
-
return true;
|
|
5883
|
-
}
|
|
5884
|
-
clear() {
|
|
5885
|
-
this.undoStack = [];
|
|
5886
|
-
this.redoStack = [];
|
|
5887
|
-
}
|
|
5888
|
-
canUndo() {
|
|
5889
|
-
return this.undoStack.length > 0;
|
|
5890
|
-
}
|
|
5891
|
-
canRedo() {
|
|
5892
|
-
return this.redoStack.length > 0;
|
|
5893
|
-
}
|
|
5894
|
-
};
|
|
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";
|
|
5895
6401
|
|
|
5896
6402
|
// src/client/dnd/design-mode/sortable-preview-animator.ts
|
|
5897
6403
|
function isIdentityTransform(transform) {
|
|
@@ -6105,43 +6611,6 @@ function useHoverDetection({
|
|
|
6105
6611
|
// src/client/dnd/design-mode/useDragVisualLoop.ts
|
|
6106
6612
|
import { useEffect as useEffect9 } from "react";
|
|
6107
6613
|
|
|
6108
|
-
// src/client/dnd/design-mode/drag-lock.ts
|
|
6109
|
-
var MIN_UNLOCK_MARGIN = 32;
|
|
6110
|
-
var MAX_UNLOCK_MARGIN = 72;
|
|
6111
|
-
var MIN_UNLOCK_DWELL_MS = 170;
|
|
6112
|
-
var MAX_UNLOCK_DWELL_MS = 300;
|
|
6113
|
-
function clamp2(value, min, max) {
|
|
6114
|
-
return Math.min(max, Math.max(min, value));
|
|
6115
|
-
}
|
|
6116
|
-
function resolveCrossUnlockParams(rect) {
|
|
6117
|
-
const width = Math.max(0, rect.right - rect.left);
|
|
6118
|
-
const height = Math.max(0, rect.bottom - rect.top);
|
|
6119
|
-
const shortest = Math.max(1, Math.min(width, height));
|
|
6120
|
-
const margin = Math.round(clamp2(shortest * 0.14, MIN_UNLOCK_MARGIN, MAX_UNLOCK_MARGIN));
|
|
6121
|
-
const dwellMs = Math.round(clamp2(shortest * 0.7, MIN_UNLOCK_DWELL_MS, MAX_UNLOCK_DWELL_MS));
|
|
6122
|
-
return { margin, dwellMs };
|
|
6123
|
-
}
|
|
6124
|
-
function checkCrossUnlock(lock, adjustedPx, adjustedPy, lockRect, now, margin, dwellMs, graceMs) {
|
|
6125
|
-
if (lock.isCrossUnlocked) {
|
|
6126
|
-
if (lock.unlockUntilTs !== null && now < lock.unlockUntilTs) return lock;
|
|
6127
|
-
return lock;
|
|
6128
|
-
}
|
|
6129
|
-
const outsideBand = adjustedPx < lockRect.left - margin || adjustedPx > lockRect.right + margin || adjustedPy < lockRect.top - margin || adjustedPy > lockRect.bottom + margin;
|
|
6130
|
-
if (!outsideBand) {
|
|
6131
|
-
return lock.exitSince !== null ? { ...lock, exitSince: null } : lock;
|
|
6132
|
-
}
|
|
6133
|
-
const exitSince = lock.exitSince ?? now;
|
|
6134
|
-
if (now - exitSince >= dwellMs) {
|
|
6135
|
-
return {
|
|
6136
|
-
containerId: lock.containerId,
|
|
6137
|
-
isCrossUnlocked: true,
|
|
6138
|
-
exitSince,
|
|
6139
|
-
unlockUntilTs: now + graceMs
|
|
6140
|
-
};
|
|
6141
|
-
}
|
|
6142
|
-
return lock.exitSince !== null ? lock : { ...lock, exitSince: now };
|
|
6143
|
-
}
|
|
6144
|
-
|
|
6145
6614
|
// src/client/dnd/design-mode/quadtree.ts
|
|
6146
6615
|
function overlaps(a, b) {
|
|
6147
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;
|
|
@@ -6151,6 +6620,7 @@ function fitsInside(outer, inner) {
|
|
|
6151
6620
|
}
|
|
6152
6621
|
var CAPACITY = 8;
|
|
6153
6622
|
var MAX_DEPTH2 = 8;
|
|
6623
|
+
var ROOT_BOUNDS_PAD = 100;
|
|
6154
6624
|
var QTNode = class _QTNode {
|
|
6155
6625
|
constructor(bounds, depth = 0) {
|
|
6156
6626
|
__publicField(this, "bounds");
|
|
@@ -6248,12 +6718,11 @@ var Quadtree = class _Quadtree {
|
|
|
6248
6718
|
if (r.right > maxX) maxX = r.right;
|
|
6249
6719
|
if (r.bottom > maxY) maxY = r.bottom;
|
|
6250
6720
|
}
|
|
6251
|
-
const PAD = 100;
|
|
6252
6721
|
const root = new QTNode({
|
|
6253
|
-
x: minX -
|
|
6254
|
-
y: minY -
|
|
6255
|
-
w: maxX - minX + 2 *
|
|
6256
|
-
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
|
|
6257
6726
|
});
|
|
6258
6727
|
for (const [id, r] of rects) {
|
|
6259
6728
|
if (r.width > 0 && r.height > 0) {
|
|
@@ -6355,9 +6824,9 @@ function getSlotQueryRadius() {
|
|
|
6355
6824
|
const vh = window.innerHeight;
|
|
6356
6825
|
return Math.min(500, Math.max(100, Math.max(vw, vh) * 0.2));
|
|
6357
6826
|
}
|
|
6358
|
-
var NEST_EDGE_FRACTION = 0.
|
|
6359
|
-
var ORIGIN_RETURN_BAND_PX =
|
|
6360
|
-
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;
|
|
6361
6830
|
var ROW_OVERLAP_TOLERANCE_PX = 1;
|
|
6362
6831
|
function normalizeContainerSet(containers) {
|
|
6363
6832
|
if (!containers) return null;
|
|
@@ -6435,8 +6904,6 @@ var DragIntentEngine = class {
|
|
|
6435
6904
|
// Pre-computed set of slot IDs that would produce no movement for the active item.
|
|
6436
6905
|
__publicField(this, "noOpSlotIds");
|
|
6437
6906
|
__publicField(this, "originContainerId");
|
|
6438
|
-
__publicField(this, "laneRootContainerId");
|
|
6439
|
-
__publicField(this, "laneParentId");
|
|
6440
6907
|
__publicField(this, "laneAllowedContainers");
|
|
6441
6908
|
__publicField(this, "blockedContainerIds");
|
|
6442
6909
|
__publicField(this, "targetPlane", "lane");
|
|
@@ -6448,8 +6915,6 @@ var DragIntentEngine = class {
|
|
|
6448
6915
|
this.queryRadius = getSlotQueryRadius();
|
|
6449
6916
|
this.slotsByContainer = buildSlotsByContainer(projection.slotIndex);
|
|
6450
6917
|
this.originContainerId = this.getNodeParentContainerId(activeId);
|
|
6451
|
-
this.laneRootContainerId = options?.laneRootContainerId ?? this.originContainerId;
|
|
6452
|
-
this.laneParentId = this.laneRootContainerId ? this.containerIndex.get(this.laneRootContainerId)?.parentId ?? null : null;
|
|
6453
6918
|
this.laneAllowedContainers = normalizeContainerSet(options?.laneAllowedContainers);
|
|
6454
6919
|
this.blockedContainerIds = normalizeContainerSet(options?.blockedContainerIds);
|
|
6455
6920
|
const noOpSlotIds = /* @__PURE__ */ new Set();
|
|
@@ -6481,10 +6946,6 @@ var DragIntentEngine = class {
|
|
|
6481
6946
|
setTargetPlane(plane) {
|
|
6482
6947
|
this.targetPlane = plane;
|
|
6483
6948
|
}
|
|
6484
|
-
setLaneAllowedContainers(containers) {
|
|
6485
|
-
this.laneAllowedContainers = normalizeContainerSet(containers);
|
|
6486
|
-
this.reset();
|
|
6487
|
-
}
|
|
6488
6949
|
getTargetPlane() {
|
|
6489
6950
|
return this.targetPlane;
|
|
6490
6951
|
}
|
|
@@ -6583,7 +7044,7 @@ var DragIntentEngine = class {
|
|
|
6583
7044
|
if (this.isInvalidDropContainer(pointerContainerId)) return null;
|
|
6584
7045
|
for (const childId of container.children) {
|
|
6585
7046
|
if (childId === this.activeId) continue;
|
|
6586
|
-
if (this.
|
|
7047
|
+
if (this.isInvalidDropContainer(childId)) continue;
|
|
6587
7048
|
const childNode = this.getNodeRect(childId);
|
|
6588
7049
|
if (!childNode) continue;
|
|
6589
7050
|
if (!pointInRect(px, py, childNode)) continue;
|
|
@@ -6608,9 +7069,16 @@ var DragIntentEngine = class {
|
|
|
6608
7069
|
if (laneLock && !this.isLaneContainer(id)) continue;
|
|
6609
7070
|
if (!pointInRect(px, py, node.rect)) continue;
|
|
6610
7071
|
if (this.hasUsableSlots(id) === false) continue;
|
|
6611
|
-
|
|
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 });
|
|
6612
7079
|
}
|
|
6613
7080
|
pointerContainers.sort((a, b) => {
|
|
7081
|
+
if (a.edgePenalty !== b.edgePenalty) return a.edgePenalty - b.edgePenalty;
|
|
6614
7082
|
if (b.depth !== a.depth) return b.depth - a.depth;
|
|
6615
7083
|
return a.area - b.area;
|
|
6616
7084
|
});
|
|
@@ -6623,7 +7091,9 @@ var DragIntentEngine = class {
|
|
|
6623
7091
|
}
|
|
6624
7092
|
if (this.originContainerId && this.originContainerId !== pointerContainerId && !this.isInvalidDropContainer(this.originContainerId) && !this.isBlockedContainer(this.originContainerId) && (!laneLock || this.isLaneContainer(this.originContainerId)) && this.hasUsableSlots(this.originContainerId)) {
|
|
6625
7093
|
const originNode = this.containerIndex.get(this.originContainerId);
|
|
6626
|
-
|
|
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)) {
|
|
6627
7097
|
pointerContainerId = this.originContainerId;
|
|
6628
7098
|
}
|
|
6629
7099
|
}
|
|
@@ -6690,8 +7160,13 @@ var DragIntentEngine = class {
|
|
|
6690
7160
|
}
|
|
6691
7161
|
return n;
|
|
6692
7162
|
}
|
|
7163
|
+
const SLOT_ZONE_PX = 30;
|
|
6693
7164
|
for (const child of childRects) {
|
|
6694
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;
|
|
6695
7170
|
if (py < midY) return child.index;
|
|
6696
7171
|
}
|
|
6697
7172
|
return n;
|
|
@@ -6760,15 +7235,14 @@ var DragIntentEngine = class {
|
|
|
6760
7235
|
hasUsableSlots(containerId) {
|
|
6761
7236
|
const filtered = this.getContainerSlots(containerId);
|
|
6762
7237
|
if (filtered.length > 0) return true;
|
|
6763
|
-
|
|
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;
|
|
6764
7242
|
}
|
|
6765
7243
|
isLaneContainer(containerId) {
|
|
6766
7244
|
if (this.laneAllowedContainers) return this.laneAllowedContainers.has(containerId);
|
|
6767
|
-
|
|
6768
|
-
if (containerId === this.laneRootContainerId) return true;
|
|
6769
|
-
const node = this.containerIndex.get(containerId);
|
|
6770
|
-
if (!node) return false;
|
|
6771
|
-
return node.parentId === this.laneParentId;
|
|
7245
|
+
return true;
|
|
6772
7246
|
}
|
|
6773
7247
|
isBlockedContainer(containerId) {
|
|
6774
7248
|
return this.blockedContainerIds?.has(containerId) ?? false;
|
|
@@ -6779,9 +7253,11 @@ function createDragIntentEngine(projection, activeId, options) {
|
|
|
6779
7253
|
}
|
|
6780
7254
|
|
|
6781
7255
|
// src/client/dnd/design-mode/drop-line.ts
|
|
7256
|
+
var DROP_LINE_THICKNESS_PX = 2;
|
|
7257
|
+
var DROP_LINE_MIN_LENGTH_PX = 40;
|
|
6782
7258
|
function slotDropLine(slot, container, projectionTree, sdx, sdy) {
|
|
6783
|
-
const T =
|
|
6784
|
-
const MIN =
|
|
7259
|
+
const T = DROP_LINE_THICKNESS_PX;
|
|
7260
|
+
const MIN = DROP_LINE_MIN_LENGTH_PX;
|
|
6785
7261
|
const bp = computeSlotBoundaryPoint(slot, container, projectionTree);
|
|
6786
7262
|
if (container.strategy === "horizontal") {
|
|
6787
7263
|
return {
|
|
@@ -6821,9 +7297,6 @@ function slotDropLine(slot, container, projectionTree, sdx, sdy) {
|
|
|
6821
7297
|
}
|
|
6822
7298
|
|
|
6823
7299
|
// src/client/dnd/design-mode/preview-layout.ts
|
|
6824
|
-
function clampIndex(index, min, max) {
|
|
6825
|
-
return Math.max(min, Math.min(index, max));
|
|
6826
|
-
}
|
|
6827
7300
|
function getContainerChildren(projection, containerId) {
|
|
6828
7301
|
return [...projection.containerIndex.get(containerId)?.children ?? []];
|
|
6829
7302
|
}
|
|
@@ -6943,7 +7416,7 @@ function buildPreviewLayout({
|
|
|
6943
7416
|
const originalItemIds = getContainerChildren(projection, containerId);
|
|
6944
7417
|
const activeIndex = originalItemIds.indexOf(anchorNodeId);
|
|
6945
7418
|
if (activeIndex < 0) return null;
|
|
6946
|
-
const overIndex =
|
|
7419
|
+
const overIndex = clamp(targetIndex, 0, Math.max(0, originalItemIds.length - 1));
|
|
6947
7420
|
const previewItemIds = arrayMove(originalItemIds, activeIndex, overIndex);
|
|
6948
7421
|
const strategy = getContainerStrategy(projection, containerId);
|
|
6949
7422
|
containers.set(containerId, {
|
|
@@ -6983,7 +7456,7 @@ function buildPreviewLayout({
|
|
|
6983
7456
|
activePreviewContainers.add(sourceContainerId);
|
|
6984
7457
|
const targetOriginalItemIds = targetChildren.filter((id) => id !== anchorNodeId);
|
|
6985
7458
|
const targetPreviewItemIds = [...targetOriginalItemIds];
|
|
6986
|
-
const targetOverIndex =
|
|
7459
|
+
const targetOverIndex = clamp(targetIndex, 0, targetOriginalItemIds.length);
|
|
6987
7460
|
targetPreviewItemIds.splice(targetOverIndex, 0, anchorNodeId);
|
|
6988
7461
|
const targetStrategy = getContainerStrategy(projection, targetContainerId);
|
|
6989
7462
|
const targetStrategyItemIds = [...targetOriginalItemIds, anchorNodeId];
|
|
@@ -7015,9 +7488,8 @@ function buildPreviewLayout({
|
|
|
7015
7488
|
}
|
|
7016
7489
|
|
|
7017
7490
|
// src/client/dnd/design-mode/useDragVisualLoop.ts
|
|
7018
|
-
var CONTAINER_SWITCH_HYSTERESIS =
|
|
7019
|
-
var
|
|
7020
|
-
var SMALL_NODE_NEST_DWELL_MS = 180;
|
|
7491
|
+
var CONTAINER_SWITCH_HYSTERESIS = 12;
|
|
7492
|
+
var SMALL_NODE_NEST_DWELL_MS = 100;
|
|
7021
7493
|
var LARGE_NODE_NEST_DWELL_MS = 320;
|
|
7022
7494
|
function useDragVisualLoop({
|
|
7023
7495
|
activeDrag,
|
|
@@ -7066,33 +7538,12 @@ function useDragVisualLoop({
|
|
|
7066
7538
|
const scrollDy = window.scrollY - snapshot.scroll.y;
|
|
7067
7539
|
const adjustedPx = px + scrollDx;
|
|
7068
7540
|
const adjustedPy = py + scrollDy;
|
|
7069
|
-
const lockContainer = refs.lockRef.current.containerId ? refs.projectionRef.current.containerIndex.get(refs.lockRef.current.containerId) ?? null : null;
|
|
7070
|
-
if (!refs.lockRef.current.isCrossUnlocked && lockContainer) {
|
|
7071
|
-
const { margin, dwellMs } = resolveCrossUnlockParams(lockContainer.rect);
|
|
7072
|
-
const nextLock = checkCrossUnlock(
|
|
7073
|
-
refs.lockRef.current,
|
|
7074
|
-
adjustedPx,
|
|
7075
|
-
adjustedPy,
|
|
7076
|
-
lockContainer.rect,
|
|
7077
|
-
performance.now(),
|
|
7078
|
-
margin,
|
|
7079
|
-
dwellMs,
|
|
7080
|
-
CROSS_UNLOCK_GRACE_MS
|
|
7081
|
-
);
|
|
7082
|
-
if (nextLock !== refs.lockRef.current) {
|
|
7083
|
-
refs.lockRef.current = nextLock;
|
|
7084
|
-
if (nextLock.isCrossUnlocked) engine.setLaneAllowedContainers(null);
|
|
7085
|
-
}
|
|
7086
|
-
}
|
|
7087
7541
|
const nestPreview = engine.peekNestIntent(px, py, window.scrollX, window.scrollY, snapshot.scroll.x, snapshot.scroll.y);
|
|
7088
7542
|
const isLargeDraggedNode = shouldBlockNestForLargeNode(activeDrag.w, activeDrag.h);
|
|
7089
7543
|
let allowNest = false;
|
|
7090
7544
|
if (nestPreview) {
|
|
7091
7545
|
const childId = nestPreview.slotId.split("::nest::")[1] ?? "";
|
|
7092
|
-
|
|
7093
|
-
const isSiblingList = siblings.length >= 2;
|
|
7094
|
-
const isSourceContainerNest = nestPreview.containerId === refs.sourceContainerIdRef.current;
|
|
7095
|
-
allowNest = !!childId && !isSiblingList && !isLargeDraggedNode && !isSourceContainerNest;
|
|
7546
|
+
allowNest = !!childId && !isLargeDraggedNode;
|
|
7096
7547
|
}
|
|
7097
7548
|
if (!allowNest) {
|
|
7098
7549
|
refs.nestCandidateRef.current = null;
|
|
@@ -7102,7 +7553,12 @@ function useDragVisualLoop({
|
|
|
7102
7553
|
const now = performance.now();
|
|
7103
7554
|
if (!refs.nestCandidateRef.current || refs.nestCandidateRef.current.id !== nestPreview.slotId) {
|
|
7104
7555
|
refs.nestCandidateRef.current = { id: nestPreview.slotId, since: now };
|
|
7105
|
-
|
|
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
|
+
}
|
|
7106
7562
|
setTargetPlane("lane");
|
|
7107
7563
|
} else if (now - refs.nestCandidateRef.current.since >= (isLargeDraggedNode ? LARGE_NODE_NEST_DWELL_MS : SMALL_NODE_NEST_DWELL_MS)) {
|
|
7108
7564
|
setTargetPlane("nest");
|
|
@@ -7161,18 +7617,6 @@ function useDragVisualLoop({
|
|
|
7161
7617
|
setTargetPlane("lane");
|
|
7162
7618
|
refs.nestCandidateRef.current = null;
|
|
7163
7619
|
refs.nestTargetRef.current = null;
|
|
7164
|
-
const lockedParentId = refs.lockRef.current.containerId;
|
|
7165
|
-
if (!refs.lockRef.current.isCrossUnlocked && (!lockedParentId || intent.containerId !== lockedParentId)) {
|
|
7166
|
-
refs.lastIntentRef.current = null;
|
|
7167
|
-
refs.visualIntentRef.current = null;
|
|
7168
|
-
refs.resolvedReorderRef.current = null;
|
|
7169
|
-
refs.finalVisualKeyRef.current = null;
|
|
7170
|
-
setInsideRect(null);
|
|
7171
|
-
setDropLine(null);
|
|
7172
|
-
animator.clear();
|
|
7173
|
-
rafId = requestAnimationFrame(tick);
|
|
7174
|
-
return;
|
|
7175
|
-
}
|
|
7176
7620
|
refs.lastIntentRef.current = intent;
|
|
7177
7621
|
refs.visualIntentRef.current = intent;
|
|
7178
7622
|
setInsideRect(null);
|
|
@@ -7204,19 +7648,43 @@ function useDragVisualLoop({
|
|
|
7204
7648
|
}
|
|
7205
7649
|
const sourceCommitLane = refs.sourceCommitLaneRef.current ?? getCommitLaneSnapshot(sourceContainerId);
|
|
7206
7650
|
const targetCommitLane = getCommitLaneSnapshot(intent.containerId);
|
|
7207
|
-
|
|
7651
|
+
let commitPlacement = resolveCommitPlacementFromVisualTarget(
|
|
7208
7652
|
targetCommitLane,
|
|
7209
7653
|
sourceCommitLane,
|
|
7210
7654
|
activeDrag.anchorNodeId,
|
|
7211
7655
|
targetInsertIndex
|
|
7212
7656
|
);
|
|
7213
7657
|
if (!commitPlacement) {
|
|
7214
|
-
refs.
|
|
7215
|
-
refs.
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
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
|
+
};
|
|
7220
7688
|
}
|
|
7221
7689
|
setDropLine(slotDropLine(slot, container, proj.projectionTree, scrollDx, scrollDy));
|
|
7222
7690
|
refs.resolvedReorderRef.current = {
|
|
@@ -7261,56 +7729,175 @@ function useDragVisualLoop({
|
|
|
7261
7729
|
|
|
7262
7730
|
// src/client/dnd/design-mode/useDragLifecycle.ts
|
|
7263
7731
|
import { useState as useState5, useCallback as useCallback6 } from "react";
|
|
7264
|
-
|
|
7265
|
-
// src/client/dnd/design-mode/node-key.ts
|
|
7266
|
-
var _runtimeKeyMap = /* @__PURE__ */ new WeakMap();
|
|
7267
|
-
var _runtimeKeyCounter = 0;
|
|
7268
|
-
function getRuntimeKey(el) {
|
|
7269
|
-
const existing = _runtimeKeyMap.get(el);
|
|
7270
|
-
if (existing) return existing;
|
|
7271
|
-
const key2 = `runtime:${++_runtimeKeyCounter}`;
|
|
7272
|
-
_runtimeKeyMap.set(el, key2);
|
|
7273
|
-
return key2;
|
|
7274
|
-
}
|
|
7275
|
-
function getNodeKeyFromElement(el) {
|
|
7276
|
-
const resolved = resolveDragSurface(el);
|
|
7277
|
-
const host = resolved.host ?? el;
|
|
7278
|
-
const meta = getEditorMeta(host);
|
|
7279
|
-
if (meta) return meta.nodeId;
|
|
7280
|
-
const persisted = host.getAttribute("data-dnd-node-id");
|
|
7281
|
-
if (persisted) return persisted;
|
|
7282
|
-
return getRuntimeKey(host);
|
|
7283
|
-
}
|
|
7284
|
-
function getInspectorRefFromElement(el) {
|
|
7285
|
-
const resolved = resolveDragSurface(el);
|
|
7286
|
-
const host = resolved.host ?? el;
|
|
7287
|
-
const meta = getEditorMeta(host);
|
|
7288
|
-
if (!meta) return null;
|
|
7289
|
-
return { relativePath: meta.file, line: meta.line, column: meta.col };
|
|
7290
|
-
}
|
|
7291
|
-
|
|
7292
|
-
// src/client/dnd/design-mode/useDragLifecycle.ts
|
|
7293
|
-
var SOFT_COMMIT_DISTANCE_PX = 14;
|
|
7294
|
-
function isLaneCommitValid(intent, resolvedReorder, lockedParentId) {
|
|
7295
|
-
if (!intent || intent.mode !== "reorder") return false;
|
|
7296
|
-
if (!resolvedReorder) return false;
|
|
7297
|
-
if (!lockedParentId) return false;
|
|
7298
|
-
if (intent.containerId !== lockedParentId) return false;
|
|
7299
|
-
if (resolvedReorder.visual.slotId !== intent.slotId) return false;
|
|
7300
|
-
if (resolvedReorder.visual.parentNodeId !== intent.containerId) return false;
|
|
7301
|
-
return true;
|
|
7302
|
-
}
|
|
7303
|
-
function buildRuntimeIdentityLocal(nodeId, resolveNodeElement) {
|
|
7732
|
+
function buildRuntimeIdentityLocal(nodeId, resolveNodeElement, effectiveLaneSnapshot) {
|
|
7304
7733
|
if (!nodeId) return null;
|
|
7305
7734
|
const element = resolveNodeElement(nodeId);
|
|
7306
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;
|
|
7307
7741
|
return {
|
|
7308
7742
|
nodeId,
|
|
7309
|
-
nodeKey
|
|
7310
|
-
selector: element.getAttribute(
|
|
7311
|
-
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
|
|
7312
7775
|
};
|
|
7313
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
|
+
}
|
|
7314
7901
|
function useDragLifecycle({
|
|
7315
7902
|
refs,
|
|
7316
7903
|
pause,
|
|
@@ -7392,12 +7979,10 @@ function useDragLifecycle({
|
|
|
7392
7979
|
const srcContainerId = anchor.sourceContainerId;
|
|
7393
7980
|
const sourceCommitLane = getCommitLaneSnapshot(srcContainerId);
|
|
7394
7981
|
const startPosition = getCurrentCommitPlacement(anchorNodeId, srcContainerId);
|
|
7395
|
-
const laneAllowed = srcContainerId ? /* @__PURE__ */ new Set([srcContainerId]) : null;
|
|
7396
7982
|
const blockedContainerIds = buildBlockedStructuralContainerIds(proj, refs.nodeMapRef.current, srcContainerId);
|
|
7397
7983
|
refs.blockedContainerIdsRef.current = blockedContainerIds;
|
|
7398
7984
|
refs.engineRef.current = createDragIntentEngine(proj, anchorNodeId, {
|
|
7399
7985
|
laneRootContainerId: srcContainerId,
|
|
7400
|
-
laneAllowedContainers: laneAllowed,
|
|
7401
7986
|
blockedContainerIds
|
|
7402
7987
|
});
|
|
7403
7988
|
refs.engineRef.current.setTargetPlane("lane");
|
|
@@ -7405,7 +7990,7 @@ function useDragLifecycle({
|
|
|
7405
7990
|
refs.sourceContainerIdRef.current = srcContainerId;
|
|
7406
7991
|
refs.sourceCommitLaneRef.current = sourceCommitLane;
|
|
7407
7992
|
refs.dragSessionAnchorRef.current = anchor;
|
|
7408
|
-
refs.lockRef.current = { containerId: srcContainerId, isCrossUnlocked:
|
|
7993
|
+
refs.lockRef.current = { containerId: srcContainerId, isCrossUnlocked: true, exitSince: null, unlockUntilTs: null };
|
|
7409
7994
|
refs.dragStartPositionRef.current = startPosition;
|
|
7410
7995
|
refs.safeZoneRef.current = resolveSafeZoneRect();
|
|
7411
7996
|
pause();
|
|
@@ -7431,22 +8016,34 @@ function useDragLifecycle({
|
|
|
7431
8016
|
const pointer = refs.mouseRef.current;
|
|
7432
8017
|
const safeZone = refs.safeZoneRef.current;
|
|
7433
8018
|
const blockedByGuard = isPointerOutsideSafeZone(pointer.x, pointer.y, safeZone) || isPointerInHardPageEdgeZone(pointer.x, pointer.y);
|
|
7434
|
-
|
|
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
|
+
}
|
|
7435
8036
|
const intent = refs.visualIntentRef.current;
|
|
7436
|
-
const finalVisual = refs.finalVisualKeyRef.current;
|
|
7437
8037
|
const nestTarget = refs.nestTargetRef.current;
|
|
7438
8038
|
const intentHasContainer = intent ? refs.projectionRef.current.containerIndex.has(intent.containerId) : false;
|
|
7439
8039
|
const intentHasSlot = intent ? intent.mode === "nest" || refs.projectionRef.current.slotIndex.has(intent.slotId) : false;
|
|
7440
8040
|
const intentValid = !!intent && intentHasContainer && intentHasSlot;
|
|
7441
8041
|
if (!blockedByGuard && activeEl && sourcePos && (nestTarget || intentValid)) {
|
|
7442
8042
|
const dropTargetId = nestTarget?.nodeId ?? intent?.containerId;
|
|
7443
|
-
if (dropTargetId) {
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
syncHistoryAvailability();
|
|
7448
|
-
return;
|
|
7449
|
-
}
|
|
8043
|
+
if (dropTargetId && !refs.nodeMapRef.current.has(dropTargetId)) {
|
|
8044
|
+
clearDragState();
|
|
8045
|
+
syncHistoryAvailability();
|
|
8046
|
+
return;
|
|
7450
8047
|
}
|
|
7451
8048
|
let command = null;
|
|
7452
8049
|
if (nestTarget) {
|
|
@@ -7466,46 +8063,8 @@ function useDragLifecycle({
|
|
|
7466
8063
|
};
|
|
7467
8064
|
}
|
|
7468
8065
|
} else if (intentValid && intent.mode === "reorder") {
|
|
7469
|
-
const lockedParentId = refs.lockRef.current.containerId;
|
|
7470
|
-
if (!refs.lockRef.current.isCrossUnlocked && (!lockedParentId || intent.containerId !== lockedParentId)) {
|
|
7471
|
-
clearDragState();
|
|
7472
|
-
syncHistoryAvailability();
|
|
7473
|
-
return;
|
|
7474
|
-
}
|
|
7475
|
-
const snapshot = refs.snapshotRef.current;
|
|
7476
|
-
const targetContainer = refs.projectionRef.current.containerIndex.get(intent.containerId);
|
|
7477
|
-
if (targetContainer) {
|
|
7478
|
-
const scrollDx = window.scrollX - (snapshot?.scroll.x ?? 0);
|
|
7479
|
-
const scrollDy = window.scrollY - (snapshot?.scroll.y ?? 0);
|
|
7480
|
-
const adjPx = pointer.x + scrollDx;
|
|
7481
|
-
const adjPy = pointer.y + scrollDy;
|
|
7482
|
-
const r = targetContainer.rect;
|
|
7483
|
-
const inContainer = adjPx >= r.left - 40 && adjPx <= r.right + 40 && adjPy >= r.top - 40 && adjPy <= r.bottom + 40;
|
|
7484
|
-
if (!inContainer) {
|
|
7485
|
-
const softFallbackAllowed = finalVisual?.mode === "reorder" && finalVisual.containerId === intent.containerId && (() => {
|
|
7486
|
-
const slot = refs.projectionRef.current.slotIndex.get(finalVisual.slotId);
|
|
7487
|
-
const container = refs.projectionRef.current.containerIndex.get(finalVisual.containerId);
|
|
7488
|
-
if (!slot || !container) return false;
|
|
7489
|
-
const bp = computeSlotBoundaryPoint(slot, container, refs.projectionRef.current.projectionTree);
|
|
7490
|
-
const dist = Math.hypot(adjPx - bp.x, adjPy - bp.y);
|
|
7491
|
-
return dist <= SOFT_COMMIT_DISTANCE_PX;
|
|
7492
|
-
})();
|
|
7493
|
-
if (!softFallbackAllowed) {
|
|
7494
|
-
clearDragState();
|
|
7495
|
-
syncHistoryAvailability();
|
|
7496
|
-
return;
|
|
7497
|
-
}
|
|
7498
|
-
}
|
|
7499
|
-
}
|
|
7500
8066
|
const resolvedReorder = refs.resolvedReorderRef.current;
|
|
7501
|
-
|
|
7502
|
-
if (!laneValid) {
|
|
7503
|
-
clearDragState();
|
|
7504
|
-
syncHistoryAvailability();
|
|
7505
|
-
return;
|
|
7506
|
-
}
|
|
7507
|
-
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;
|
|
7508
|
-
if (!visualParity) {
|
|
8067
|
+
if (!resolvedReorder || resolvedReorder.visual.containerId !== intent.containerId) {
|
|
7509
8068
|
clearDragState();
|
|
7510
8069
|
syncHistoryAvailability();
|
|
7511
8070
|
return;
|
|
@@ -7521,62 +8080,32 @@ function useDragLifecycle({
|
|
|
7521
8080
|
};
|
|
7522
8081
|
}
|
|
7523
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);
|
|
7524
8091
|
const committed = refs.historyRef.current.execute(command, applyCommand);
|
|
7525
8092
|
if (committed) {
|
|
7526
8093
|
activeEl.setAttribute("data-dnd-moved", "");
|
|
7527
8094
|
selectElement(null);
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
const containerIdentity = buildRuntimeIdentityLocal(
|
|
7535
|
-
command.to.parentNodeId ?? command.to.laneContainerId,
|
|
7536
|
-
resolveNodeElement
|
|
7537
|
-
);
|
|
7538
|
-
const beforeSiblingIdentity = buildRuntimeIdentityLocal(command.to.beforeNodeId, resolveNodeElement);
|
|
7539
|
-
const afterSiblingIdentity = buildRuntimeIdentityLocal(command.to.afterNodeId, resolveNodeElement);
|
|
7540
|
-
const operationId = `${command.at}:${anchorNodeId}`;
|
|
7541
|
-
recordMoveChange({
|
|
7542
|
-
operationId,
|
|
7543
|
-
commandType: command.type,
|
|
8095
|
+
recordCommittedMove({
|
|
8096
|
+
command,
|
|
8097
|
+
anchorNodeId,
|
|
8098
|
+
selectedNodeId,
|
|
8099
|
+
anchorEl,
|
|
8100
|
+
selectedEl,
|
|
7544
8101
|
nodeKey,
|
|
7545
|
-
|
|
7546
|
-
inspectorRef: inspRef,
|
|
8102
|
+
inspRef,
|
|
7547
8103
|
selectedNodeKey,
|
|
7548
|
-
selectedNodeId,
|
|
7549
8104
|
selectedInspectorRef,
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
afterSibling: afterSiblingIdentity,
|
|
7555
|
-
componentName: getEditorMeta(anchorEl)?.componentName,
|
|
7556
|
-
before: command.from,
|
|
7557
|
-
after: command.to
|
|
7558
|
-
});
|
|
7559
|
-
dispatchRuntimeEvent({
|
|
7560
|
-
type: "element-moved",
|
|
7561
|
-
operationId,
|
|
7562
|
-
commandType: command.type,
|
|
7563
|
-
selected: {
|
|
7564
|
-
nodeId: selectedNodeId,
|
|
7565
|
-
nodeKey: selectedNodeKey,
|
|
7566
|
-
selector: selectedEl.getAttribute(DND_NODE_ID_ATTR2) ? `[${DND_NODE_ID_ATTR2}="${escapeCssAttrValue(selectedNodeId)}"]` : null,
|
|
7567
|
-
inspectorRef: selectedInspectorRef
|
|
7568
|
-
},
|
|
7569
|
-
anchor: {
|
|
7570
|
-
nodeId: anchorNodeId,
|
|
7571
|
-
nodeKey,
|
|
7572
|
-
selector: anchorEl.getAttribute(DND_NODE_ID_ATTR2) ? `[${DND_NODE_ID_ATTR2}="${escapeCssAttrValue(anchorNodeId)}"]` : null,
|
|
7573
|
-
inspectorRef: inspRef
|
|
7574
|
-
},
|
|
7575
|
-
container: containerIdentity,
|
|
7576
|
-
beforeSibling: beforeSiblingIdentity,
|
|
7577
|
-
afterSibling: afterSiblingIdentity,
|
|
7578
|
-
before: command.from,
|
|
7579
|
-
after: command.to
|
|
8105
|
+
sourceLaneSnapshot,
|
|
8106
|
+
targetLaneSnapshot,
|
|
8107
|
+
resolveNodeElement,
|
|
8108
|
+
recordMoveChange
|
|
7580
8109
|
});
|
|
7581
8110
|
}
|
|
7582
8111
|
}
|
|
@@ -7615,7 +8144,7 @@ function useResizeSession({
|
|
|
7615
8144
|
const rect = resizeTargetEl.getBoundingClientRect();
|
|
7616
8145
|
const selectedNodeKey = getNodeKeyFromElement(selectedEl);
|
|
7617
8146
|
const targetNodeKey = getNodeKeyFromElement(resizeTargetEl);
|
|
7618
|
-
const targetNodeId =
|
|
8147
|
+
const targetNodeId = ensureParentNodeId(resizeTargetEl);
|
|
7619
8148
|
const startColSpan = parseSpanValue(getComputedStyle(resizeTargetEl).gridColumnEnd || resizeTargetEl.style.gridColumnEnd);
|
|
7620
8149
|
const startRowSpan = parseSpanValue(getComputedStyle(resizeTargetEl).gridRowEnd || resizeTargetEl.style.gridRowEnd);
|
|
7621
8150
|
const columnTrackCount = getGridTrackCount(resizeTargetParent ? getComputedStyle(resizeTargetParent).gridTemplateColumns : null);
|
|
@@ -7740,13 +8269,13 @@ function useResizeSession({
|
|
|
7740
8269
|
selected: {
|
|
7741
8270
|
nodeId: selectedNodeId,
|
|
7742
8271
|
nodeKey: selectedNodeKey,
|
|
7743
|
-
selector:
|
|
8272
|
+
selector: buildNodeSelector(selectedNodeId),
|
|
7744
8273
|
inspectorRef
|
|
7745
8274
|
},
|
|
7746
8275
|
anchor: {
|
|
7747
8276
|
nodeId: targetNodeId,
|
|
7748
8277
|
nodeKey: targetNodeKey,
|
|
7749
|
-
selector:
|
|
8278
|
+
selector: buildNodeSelector(targetNodeId),
|
|
7750
8279
|
inspectorRef: getInspectorRefFromElement(element)
|
|
7751
8280
|
},
|
|
7752
8281
|
resize: {
|
|
@@ -7787,6 +8316,169 @@ function useResizeSession({
|
|
|
7787
8316
|
return { handleResizeHandlePointerDown };
|
|
7788
8317
|
}
|
|
7789
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
|
+
|
|
7790
8482
|
// src/client/dnd/design-mode/DesignModeOverlay.tsx
|
|
7791
8483
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
7792
8484
|
var DROP_ANIMATION = { duration: DURATION_MS, easing: EASING };
|
|
@@ -7832,7 +8524,7 @@ function HitArea({
|
|
|
7832
8524
|
onResizeHandlePointerDown
|
|
7833
8525
|
}) {
|
|
7834
8526
|
const { setNodeRef, listeners, attributes, isDragging } = useDraggable({ id: scanned.nodeId });
|
|
7835
|
-
|
|
8527
|
+
useEffect12(() => {
|
|
7836
8528
|
setNodeRef(scanned.element);
|
|
7837
8529
|
return () => setNodeRef(null);
|
|
7838
8530
|
}, [setNodeRef, scanned.element]);
|
|
@@ -7844,16 +8536,16 @@ function HitArea({
|
|
|
7844
8536
|
el.style.removeProperty("opacity");
|
|
7845
8537
|
}
|
|
7846
8538
|
}, [isDragging, scanned.element]);
|
|
7847
|
-
|
|
8539
|
+
useEffect12(() => () => {
|
|
7848
8540
|
scanned.element.style.removeProperty("opacity");
|
|
7849
8541
|
scanned.element.style.removeProperty("transform");
|
|
7850
8542
|
scanned.element.style.removeProperty("transition");
|
|
7851
8543
|
scanned.element.style.removeProperty("will-change");
|
|
7852
8544
|
}, [scanned.element]);
|
|
7853
|
-
const divRef =
|
|
8545
|
+
const divRef = useRef10(null);
|
|
7854
8546
|
const [hoverResizeHandle, setHoverResizeHandle] = useState6(null);
|
|
7855
8547
|
const isSelected = scanned.nodeId === selectedNodeId;
|
|
7856
|
-
|
|
8548
|
+
useEffect12(() => {
|
|
7857
8549
|
const div = divRef.current;
|
|
7858
8550
|
if (!div) return;
|
|
7859
8551
|
const onWheel = (e) => {
|
|
@@ -7924,6 +8616,7 @@ function DesignModeOverlay() {
|
|
|
7924
8616
|
const redoLastChange = useDesignModeStore((s) => s.redoLastChange);
|
|
7925
8617
|
const orderedChanges = useDesignModeStore((s) => s.orderedChanges);
|
|
7926
8618
|
const exportChangesForAI = useDesignModeStore((s) => s.exportChangesForAI);
|
|
8619
|
+
const externalDrag = useDesignModeStore((s) => s.externalDrag);
|
|
7927
8620
|
const { elements, pause, resume } = useElementScanner(enabled);
|
|
7928
8621
|
const nodeMap = useMemo6(() => {
|
|
7929
8622
|
const m = /* @__PURE__ */ new Map();
|
|
@@ -7938,7 +8631,7 @@ function DesignModeOverlay() {
|
|
|
7938
8631
|
const refs = useOverlayRefs(undoRequestId, redoRequestId);
|
|
7939
8632
|
refs.elementsRef.current = elements;
|
|
7940
8633
|
refs.nodeMapRef.current = nodeMap;
|
|
7941
|
-
|
|
8634
|
+
useEffect12(() => {
|
|
7942
8635
|
const m = /* @__PURE__ */ new Map();
|
|
7943
8636
|
for (const el of draggableElements) m.set(el.nodeId, el);
|
|
7944
8637
|
refs.draggableNodeMapRef.current = m;
|
|
@@ -7946,7 +8639,7 @@ function DesignModeOverlay() {
|
|
|
7946
8639
|
const resolveNodeElement = useCallback8((nodeId) => {
|
|
7947
8640
|
const known = refs.nodeMapRef.current.get(nodeId)?.element;
|
|
7948
8641
|
if (known) return known;
|
|
7949
|
-
return document.querySelector(
|
|
8642
|
+
return document.querySelector(buildNodeSelector(nodeId));
|
|
7950
8643
|
}, []);
|
|
7951
8644
|
const clearCommitLaneCache = useCallback8(() => {
|
|
7952
8645
|
refs.commitLaneCacheRef.current.clear();
|
|
@@ -7969,7 +8662,7 @@ function DesignModeOverlay() {
|
|
|
7969
8662
|
if (command.type === "resize") {
|
|
7970
8663
|
const resCmd = command;
|
|
7971
8664
|
const targetId = resCmd.targetNodeId ?? resCmd.nodeId;
|
|
7972
|
-
const targetEl = document.querySelector(
|
|
8665
|
+
const targetEl = document.querySelector(buildNodeSelector(targetId));
|
|
7973
8666
|
if (!targetEl) return false;
|
|
7974
8667
|
const snapshot = direction === "redo" ? resCmd.after : resCmd.before;
|
|
7975
8668
|
if (snapshot.styles) {
|
|
@@ -7980,6 +8673,9 @@ function DesignModeOverlay() {
|
|
|
7980
8673
|
}
|
|
7981
8674
|
return true;
|
|
7982
8675
|
}
|
|
8676
|
+
if (command.type === "insert") {
|
|
8677
|
+
return applyInsertCommand(command, direction, resolveNodeElement);
|
|
8678
|
+
}
|
|
7983
8679
|
const applied = applyDndCommandPlacement(command, direction, resolveNodeElement);
|
|
7984
8680
|
if (!applied) return false;
|
|
7985
8681
|
clearCommitLaneCache();
|
|
@@ -7992,6 +8688,10 @@ function DesignModeOverlay() {
|
|
|
7992
8688
|
}
|
|
7993
8689
|
setHistoryAvailability(refs.historyRef.current.canUndo(), refs.historyRef.current.canRedo());
|
|
7994
8690
|
}, [historyMode, setHistoryAvailability]);
|
|
8691
|
+
useEffect12(() => {
|
|
8692
|
+
refs.historyRef.current.clear();
|
|
8693
|
+
syncHistoryAvailability();
|
|
8694
|
+
}, [enabled, syncHistoryAvailability]);
|
|
7995
8695
|
const sensors = useSensors(
|
|
7996
8696
|
useSensor(PointerSensor, { activationConstraint: { distance: 8 } })
|
|
7997
8697
|
);
|
|
@@ -8002,7 +8702,7 @@ function DesignModeOverlay() {
|
|
|
8002
8702
|
);
|
|
8003
8703
|
const [insideRect, setInsideRect] = useState6(null);
|
|
8004
8704
|
const [dropLine, setDropLine] = useState6(null);
|
|
8005
|
-
|
|
8705
|
+
useEffect12(() => {
|
|
8006
8706
|
if (!enabled) return;
|
|
8007
8707
|
const fn = (e) => {
|
|
8008
8708
|
refs.mouseRef.current = { x: e.clientX, y: e.clientY };
|
|
@@ -8010,7 +8710,7 @@ function DesignModeOverlay() {
|
|
|
8010
8710
|
window.addEventListener("pointermove", fn, { capture: true, passive: true });
|
|
8011
8711
|
return () => window.removeEventListener("pointermove", fn, { capture: true });
|
|
8012
8712
|
}, [enabled]);
|
|
8013
|
-
|
|
8713
|
+
useEffect12(() => {
|
|
8014
8714
|
if (!enabled) return;
|
|
8015
8715
|
if (historyMode !== "local") return;
|
|
8016
8716
|
const onKeyDown = (e) => {
|
|
@@ -8039,7 +8739,7 @@ function DesignModeOverlay() {
|
|
|
8039
8739
|
setInsideRect,
|
|
8040
8740
|
setDropLine
|
|
8041
8741
|
});
|
|
8042
|
-
|
|
8742
|
+
useEffect12(() => {
|
|
8043
8743
|
if (!enabled || activeDrag) return;
|
|
8044
8744
|
if (undoRequestId === refs.processedUndoIdRef.current) return;
|
|
8045
8745
|
refs.processedUndoIdRef.current = undoRequestId;
|
|
@@ -8056,7 +8756,7 @@ function DesignModeOverlay() {
|
|
|
8056
8756
|
});
|
|
8057
8757
|
syncHistoryAvailability();
|
|
8058
8758
|
}, [undoRequestId, enabled, activeDrag, applyCommand, resume, selectElement, syncHistoryAvailability, undoLastChange]);
|
|
8059
|
-
|
|
8759
|
+
useEffect12(() => {
|
|
8060
8760
|
if (!enabled || activeDrag || historyMode !== "local") return;
|
|
8061
8761
|
if (redoRequestId === refs.processedRedoIdRef.current) return;
|
|
8062
8762
|
refs.processedRedoIdRef.current = redoRequestId;
|
|
@@ -8068,49 +8768,29 @@ function DesignModeOverlay() {
|
|
|
8068
8768
|
}
|
|
8069
8769
|
syncHistoryAvailability();
|
|
8070
8770
|
}, [redoRequestId, enabled, activeDrag, applyCommand, historyMode, resume, selectElement, syncHistoryAvailability, redoLastChange]);
|
|
8071
|
-
|
|
8771
|
+
useEffect12(() => {
|
|
8072
8772
|
syncHistoryAvailability();
|
|
8073
8773
|
}, [syncHistoryAvailability]);
|
|
8074
|
-
|
|
8774
|
+
useEffect12(() => {
|
|
8075
8775
|
dispatchRuntimeEvent({
|
|
8076
8776
|
type: "design-mode-changed",
|
|
8077
8777
|
mode: enabled ? "design" : "off"
|
|
8078
8778
|
});
|
|
8079
8779
|
}, [enabled]);
|
|
8080
|
-
|
|
8780
|
+
useEffect12(() => {
|
|
8081
8781
|
const aiOutput = exportChangesForAI();
|
|
8082
8782
|
const updatedAt = orderedChanges.length > 0 ? orderedChanges[orderedChanges.length - 1]?.at ?? Date.now() : Date.now();
|
|
8083
8783
|
dispatchRuntimeEvent({
|
|
8084
8784
|
type: "arrange-session-changed",
|
|
8085
|
-
session: {
|
|
8086
|
-
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
id: `${event.kind}:${event.operationId}:${index}`,
|
|
8090
|
-
kind: event.kind,
|
|
8091
|
-
operationId: event.operationId,
|
|
8092
|
-
commandType: event.kind === "move" ? event.commandType : void 0,
|
|
8093
|
-
nodeKey: event.nodeKey,
|
|
8094
|
-
nodeId: event.nodeId,
|
|
8095
|
-
inspectorRef: event.inspectorRef,
|
|
8096
|
-
selectedNodeKey: event.kind === "move" ? event.selectedNodeKey : void 0,
|
|
8097
|
-
selectedNodeId: event.kind === "move" ? event.selectedNodeId : void 0,
|
|
8098
|
-
selectedInspectorRef: event.kind === "move" ? event.selectedInspectorRef : void 0,
|
|
8099
|
-
anchorNodeKey: event.kind === "move" ? event.anchorNodeKey : void 0,
|
|
8100
|
-
anchorNodeId: event.kind === "move" ? event.anchorNodeId : void 0,
|
|
8101
|
-
summary: summarizeEditorChangeEvent(event),
|
|
8102
|
-
at: event.at
|
|
8103
|
-
})),
|
|
8104
|
-
output: {
|
|
8105
|
-
json: aiOutput,
|
|
8106
|
-
prompt: aiOutput.aiPromptContext,
|
|
8107
|
-
summary: orderedChanges.length === 0 ? "No arrange changes recorded yet." : `${orderedChanges.length} arrange change${orderedChanges.length === 1 ? "" : "s"} recorded.`
|
|
8108
|
-
},
|
|
8785
|
+
session: buildArrangeSessionSnapshot({
|
|
8786
|
+
enabled,
|
|
8787
|
+
orderedChanges,
|
|
8788
|
+
aiOutput,
|
|
8109
8789
|
updatedAt
|
|
8110
|
-
}
|
|
8790
|
+
})
|
|
8111
8791
|
});
|
|
8112
8792
|
}, [enabled, exportChangesForAI, orderedChanges]);
|
|
8113
|
-
|
|
8793
|
+
useEffect12(() => {
|
|
8114
8794
|
if (!enabled) return;
|
|
8115
8795
|
document.body.classList.add("design-mode-active");
|
|
8116
8796
|
return () => document.body.classList.remove("design-mode-active");
|
|
@@ -8123,6 +8803,13 @@ function DesignModeOverlay() {
|
|
|
8123
8803
|
autoScrollStateRef: refs.autoScrollStateRef
|
|
8124
8804
|
});
|
|
8125
8805
|
useDragVisualLoop({ activeDrag, refs, getCommitLaneSnapshot, setInsideRect, setDropLine });
|
|
8806
|
+
useExternalDragLoop({
|
|
8807
|
+
externalDrag,
|
|
8808
|
+
refs,
|
|
8809
|
+
elements,
|
|
8810
|
+
resolveNodeElement,
|
|
8811
|
+
setDropLine
|
|
8812
|
+
});
|
|
8126
8813
|
const { handleResizeHandlePointerDown } = useResizeSession({
|
|
8127
8814
|
enabled,
|
|
8128
8815
|
refs,
|
|
@@ -8178,7 +8865,7 @@ function DesignModeOverlay() {
|
|
|
8178
8865
|
}
|
|
8179
8866
|
}
|
|
8180
8867
|
),
|
|
8181
|
-
dropLine && activeDrag && /* @__PURE__ */ jsx4(
|
|
8868
|
+
dropLine && (activeDrag ?? externalDrag) && /* @__PURE__ */ jsx4(
|
|
8182
8869
|
"div",
|
|
8183
8870
|
{
|
|
8184
8871
|
"data-design-mode-ui": true,
|
|
@@ -8237,35 +8924,40 @@ var combinedCollisionDetection = (args) => {
|
|
|
8237
8924
|
|
|
8238
8925
|
// src/client/dnd/DndProvider.tsx
|
|
8239
8926
|
import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
8927
|
+
var externalDragMouse = { x: 0, y: 0, commitRequested: false };
|
|
8240
8928
|
function DndProvider({ children }) {
|
|
8241
8929
|
const setActive = useDndStore((s) => s.setActive);
|
|
8242
8930
|
const dragEndHandlers = useDndStore((s) => s.dragEndHandlers);
|
|
8243
|
-
const toggle = useDesignModeStore((s) => s.toggle);
|
|
8244
8931
|
const designModeEnabled = useDesignModeStore((s) => s.enabled);
|
|
8245
|
-
const setDesignModeEnabled = useDesignModeStore((s) => s.setEnabled);
|
|
8246
8932
|
const setHistoryMode = useDesignModeStore((s) => s.setHistoryMode);
|
|
8247
8933
|
const setInspectorTheme = useDesignModeStore((s) => s.setInspectorTheme);
|
|
8248
8934
|
const requestUndo = useDesignModeStore((s) => s.requestUndo);
|
|
8249
|
-
|
|
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(() => {
|
|
8250
8942
|
const handleKeyDown = (e) => {
|
|
8251
8943
|
if (e.ctrlKey && e.shiftKey && e.key === "D") {
|
|
8252
8944
|
e.preventDefault();
|
|
8253
|
-
|
|
8945
|
+
transitionDesignMode(!useDesignModeStore.getState().enabled);
|
|
8254
8946
|
}
|
|
8255
8947
|
};
|
|
8256
8948
|
window.addEventListener("keydown", handleKeyDown);
|
|
8257
8949
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
8258
|
-
}, [
|
|
8259
|
-
|
|
8950
|
+
}, [transitionDesignMode]);
|
|
8951
|
+
useEffect13(() => {
|
|
8260
8952
|
const handleHostCommand = (event) => {
|
|
8261
8953
|
const detail = event.detail;
|
|
8262
8954
|
if (!detail || typeof detail !== "object") return;
|
|
8263
8955
|
switch (detail.type) {
|
|
8264
8956
|
case "sync-editor-state":
|
|
8265
8957
|
if (detail.mode === "design") {
|
|
8266
|
-
|
|
8958
|
+
transitionDesignMode(true);
|
|
8267
8959
|
} else if (detail.mode === "inspect" || detail.mode === "off") {
|
|
8268
|
-
|
|
8960
|
+
transitionDesignMode(false);
|
|
8269
8961
|
}
|
|
8270
8962
|
if (detail.historyMode) {
|
|
8271
8963
|
setHistoryMode(detail.historyMode);
|
|
@@ -8275,7 +8967,7 @@ function DndProvider({ children }) {
|
|
|
8275
8967
|
}
|
|
8276
8968
|
break;
|
|
8277
8969
|
case "set-design-mode":
|
|
8278
|
-
|
|
8970
|
+
transitionDesignMode(detail.enabled);
|
|
8279
8971
|
break;
|
|
8280
8972
|
case "set-history-mode":
|
|
8281
8973
|
setHistoryMode(detail.historyMode);
|
|
@@ -8286,6 +8978,21 @@ function DndProvider({ children }) {
|
|
|
8286
8978
|
case "request-undo":
|
|
8287
8979
|
requestUndo();
|
|
8288
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;
|
|
8289
8996
|
default:
|
|
8290
8997
|
break;
|
|
8291
8998
|
}
|
|
@@ -8295,7 +9002,7 @@ function DndProvider({ children }) {
|
|
|
8295
9002
|
window.removeEventListener(HOST_COMMAND_EVENT, handleHostCommand);
|
|
8296
9003
|
};
|
|
8297
9004
|
}, [
|
|
8298
|
-
|
|
9005
|
+
transitionDesignMode,
|
|
8299
9006
|
setHistoryMode,
|
|
8300
9007
|
setInspectorTheme,
|
|
8301
9008
|
requestUndo
|
|
@@ -8330,6 +9037,7 @@ function DndProvider({ children }) {
|
|
|
8330
9037
|
const handleDesignModeCrash = useCallback9(() => {
|
|
8331
9038
|
const designModeState = useDesignModeStore.getState();
|
|
8332
9039
|
designModeState.resetAll();
|
|
9040
|
+
designModeState.resetArrangeSession();
|
|
8333
9041
|
designModeState.setEnabled(false);
|
|
8334
9042
|
}, []);
|
|
8335
9043
|
if (designModeEnabled) {
|
|
@@ -8370,7 +9078,7 @@ function DndProvider({ children }) {
|
|
|
8370
9078
|
}
|
|
8371
9079
|
|
|
8372
9080
|
// src/client/useArchieDevToolsRuntime.ts
|
|
8373
|
-
import { useEffect as
|
|
9081
|
+
import { useEffect as useEffect14, useRef as useRef11 } from "react";
|
|
8374
9082
|
|
|
8375
9083
|
// src/client/inject-inspector/archieOrigins.ts
|
|
8376
9084
|
var ARCHIE_HOST_ORIGINS = [
|
|
@@ -8476,10 +9184,9 @@ function injectInspector(opts = {}) {
|
|
|
8476
9184
|
document.head.appendChild(script);
|
|
8477
9185
|
return null;
|
|
8478
9186
|
}
|
|
8479
|
-
const localInspectorModulePath = "./inspector.js";
|
|
8480
9187
|
return import(
|
|
8481
9188
|
/* @vite-ignore */
|
|
8482
|
-
|
|
9189
|
+
"./inspector.js"
|
|
8483
9190
|
).then((m) => {
|
|
8484
9191
|
try {
|
|
8485
9192
|
m.default();
|
|
@@ -8595,18 +9302,26 @@ installDomMetaSanitizer();
|
|
|
8595
9302
|
installDomMetaSanitizer();
|
|
8596
9303
|
function useArchieDevToolsRuntime({
|
|
8597
9304
|
router,
|
|
8598
|
-
inspector
|
|
9305
|
+
inspector,
|
|
9306
|
+
inspectorOptions
|
|
8599
9307
|
}) {
|
|
8600
|
-
|
|
9308
|
+
const stableOptionsKey = JSON.stringify(inspectorOptions ?? null);
|
|
9309
|
+
const inspectorOptionsRef = useRef11(inspectorOptions);
|
|
9310
|
+
inspectorOptionsRef.current = inspectorOptions;
|
|
9311
|
+
useEffect14(() => {
|
|
8601
9312
|
if (process.env.NODE_ENV !== "development") {
|
|
8602
9313
|
return;
|
|
8603
9314
|
}
|
|
8604
9315
|
const unsubscribe = setupArchieRouteListener(router);
|
|
8605
|
-
if (inspector) {
|
|
8606
|
-
|
|
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
|
+
});
|
|
8607
9322
|
}
|
|
8608
9323
|
return unsubscribe;
|
|
8609
|
-
}, [router, inspector]);
|
|
9324
|
+
}, [router, inspector, stableOptionsKey]);
|
|
8610
9325
|
}
|
|
8611
9326
|
|
|
8612
9327
|
// src/client/ArchieDevToolProvider.tsx
|
|
@@ -8614,9 +9329,10 @@ import { jsx as jsx6 } from "react/jsx-runtime";
|
|
|
8614
9329
|
function ArchieDevToolProvider({
|
|
8615
9330
|
children,
|
|
8616
9331
|
router,
|
|
8617
|
-
inspector
|
|
9332
|
+
inspector,
|
|
9333
|
+
inspectorOptions
|
|
8618
9334
|
}) {
|
|
8619
|
-
useArchieDevToolsRuntime({ router, inspector });
|
|
9335
|
+
useArchieDevToolsRuntime({ router, inspector, inspectorOptions });
|
|
8620
9336
|
return /* @__PURE__ */ jsx6(DndProvider, { children });
|
|
8621
9337
|
}
|
|
8622
9338
|
export {
|