refactor(web): extract allocation drag session
This commit is contained in:
@@ -0,0 +1,78 @@
|
|||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
import { beginAllocationDragSession } from "./timelineAllocationDragSession.js";
|
||||||
|
|
||||||
|
describe("timelineAllocationDragSession", () => {
|
||||||
|
it("starts the session, forwards movement, and finalizes on mouseup", () => {
|
||||||
|
const previousCleanup = vi.fn();
|
||||||
|
const setState = vi.fn();
|
||||||
|
const updatePosition = vi.fn();
|
||||||
|
const finalize = vi.fn();
|
||||||
|
const state = { allocationId: "alloc-1", isActive: true };
|
||||||
|
const cleanupRef = { current: previousCleanup as (() => void) | null };
|
||||||
|
const stateRef = { current: { allocationId: null, isActive: false } };
|
||||||
|
const handlers: {
|
||||||
|
move?: (event: MouseEvent) => void;
|
||||||
|
up?: (event: MouseEvent) => void;
|
||||||
|
} = {};
|
||||||
|
|
||||||
|
beginAllocationDragSession({
|
||||||
|
state,
|
||||||
|
cleanupRef,
|
||||||
|
stateRef,
|
||||||
|
setState,
|
||||||
|
documentTarget: {} as Document,
|
||||||
|
attachDrag: (_documentTarget, onMove, onUp) => {
|
||||||
|
handlers.move = onMove;
|
||||||
|
handlers.up = onUp;
|
||||||
|
return vi.fn();
|
||||||
|
},
|
||||||
|
updatePosition,
|
||||||
|
finalize,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(previousCleanup).toHaveBeenCalledOnce();
|
||||||
|
expect(stateRef.current).toBe(state);
|
||||||
|
expect(setState).toHaveBeenCalledWith(state);
|
||||||
|
|
||||||
|
handlers.move?.({ clientX: 44 } as MouseEvent);
|
||||||
|
expect(updatePosition).toHaveBeenCalledWith(44);
|
||||||
|
|
||||||
|
const preventDefault = vi.fn();
|
||||||
|
const attachedCleanup = cleanupRef.current;
|
||||||
|
handlers.up?.({ clientX: 65, preventDefault } as unknown as MouseEvent);
|
||||||
|
|
||||||
|
expect(attachedCleanup).toHaveBeenCalledOnce();
|
||||||
|
expect(cleanupRef.current).toBeNull();
|
||||||
|
expect(finalize).toHaveBeenCalledWith(65);
|
||||||
|
expect(preventDefault).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("works when the session starts without a prior cleanup", () => {
|
||||||
|
const setState = vi.fn();
|
||||||
|
const updatePosition = vi.fn();
|
||||||
|
const finalize = vi.fn().mockResolvedValue(undefined);
|
||||||
|
const cleanupRef = { current: null as (() => void) | null };
|
||||||
|
const stateRef = { current: { allocationId: null, isActive: false } };
|
||||||
|
let upHandler: ((event: MouseEvent) => void) | undefined;
|
||||||
|
|
||||||
|
beginAllocationDragSession({
|
||||||
|
state: { allocationId: "alloc-2", isActive: true },
|
||||||
|
cleanupRef,
|
||||||
|
stateRef,
|
||||||
|
setState,
|
||||||
|
documentTarget: {} as Document,
|
||||||
|
attachDrag: (_documentTarget, _onMove, onUp) => {
|
||||||
|
upHandler = onUp;
|
||||||
|
return vi.fn();
|
||||||
|
},
|
||||||
|
updatePosition,
|
||||||
|
finalize,
|
||||||
|
});
|
||||||
|
|
||||||
|
upHandler?.({ clientX: 18, preventDefault() {} } as MouseEvent);
|
||||||
|
|
||||||
|
expect(updatePosition).not.toHaveBeenCalled();
|
||||||
|
expect(finalize).toHaveBeenCalledWith(18);
|
||||||
|
expect(cleanupRef.current).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
type MutableCurrent<T> = {
|
||||||
|
current: T;
|
||||||
|
};
|
||||||
|
|
||||||
|
type AttachDocumentMouseDrag = (
|
||||||
|
documentTarget: Document,
|
||||||
|
onMove: (event: MouseEvent) => void,
|
||||||
|
onUp: (event: MouseEvent) => void,
|
||||||
|
) => () => void;
|
||||||
|
|
||||||
|
type BeginAllocationDragSessionParams<TState> = {
|
||||||
|
state: TState;
|
||||||
|
cleanupRef: MutableCurrent<(() => void) | null>;
|
||||||
|
stateRef: MutableCurrent<TState>;
|
||||||
|
setState: (state: TState) => void;
|
||||||
|
documentTarget: Document;
|
||||||
|
attachDrag: AttachDocumentMouseDrag;
|
||||||
|
updatePosition: (clientX: number) => void;
|
||||||
|
finalize: (clientX: number) => Promise<void> | void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function beginAllocationDragSession<TState>({
|
||||||
|
state,
|
||||||
|
cleanupRef,
|
||||||
|
stateRef,
|
||||||
|
setState,
|
||||||
|
documentTarget,
|
||||||
|
attachDrag,
|
||||||
|
updatePosition,
|
||||||
|
finalize,
|
||||||
|
}: BeginAllocationDragSessionParams<TState>) {
|
||||||
|
stateRef.current = state;
|
||||||
|
setState(state);
|
||||||
|
cleanupRef.current?.();
|
||||||
|
|
||||||
|
function handleMove(event: MouseEvent) {
|
||||||
|
updatePosition(event.clientX);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUp(event: MouseEvent) {
|
||||||
|
cleanupRef.current?.();
|
||||||
|
cleanupRef.current = null;
|
||||||
|
void finalize(event.clientX);
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanupRef.current = attachDrag(documentTarget, handleMove, handleUp);
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
import { beginAllocationMultiDragSession } from "./timelineAllocationMultiDragSession.js";
|
import { beginAllocationMultiDragSession } from "./timelineAllocationMultiDragSession.js";
|
||||||
import { resolveAllocationRelease } from "./timelineAllocationRelease.js";
|
import { resolveAllocationRelease } from "./timelineAllocationRelease.js";
|
||||||
import { createAllocationDragState } from "./timelineAllocationDragState.js";
|
import { createAllocationDragState } from "./timelineAllocationDragState.js";
|
||||||
|
import { beginAllocationDragSession } from "./timelineAllocationDragSession.js";
|
||||||
import { cleanupTimelineDragState } from "./timelineDragCleanup.js";
|
import { cleanupTimelineDragState } from "./timelineDragCleanup.js";
|
||||||
import { resolveAllocationDragPosition, resolveProjectDragPosition } from "./timelineDragPosition.js";
|
import { resolveAllocationDragPosition, resolveProjectDragPosition } from "./timelineDragPosition.js";
|
||||||
import { attachDocumentMouseDrag } from "./timelineDocumentDrag.js";
|
import { attachDocumentMouseDrag } from "./timelineDocumentDrag.js";
|
||||||
@@ -629,94 +630,90 @@ export function useTimelineDrag({
|
|||||||
endDate: opts.endDate,
|
endDate: opts.endDate,
|
||||||
startMouseX: e.clientX,
|
startMouseX: e.clientX,
|
||||||
});
|
});
|
||||||
allocDragRef.current = initial;
|
|
||||||
setAllocDragState(initial);
|
|
||||||
setAllocationPreviewTarget(e.currentTarget, opts.mode);
|
setAllocationPreviewTarget(e.currentTarget, opts.mode);
|
||||||
allocDragCleanupRef.current?.();
|
beginAllocationDragSession({
|
||||||
|
state: initial,
|
||||||
// ── document handlers ────────────────────────────────────────────────
|
cleanupRef: allocDragCleanupRef,
|
||||||
function handleMove(ev: MouseEvent) {
|
stateRef: allocDragRef,
|
||||||
updateAllocationDragPosition(ev.clientX);
|
setState: setAllocDragState,
|
||||||
}
|
documentTarget: document,
|
||||||
|
attachDrag: attachDocumentMouseDrag,
|
||||||
function handleUp(ev: MouseEvent) {
|
updatePosition: updateAllocationDragPosition,
|
||||||
allocDragCleanupRef.current?.();
|
finalize: (clientX) => {
|
||||||
allocDragCleanupRef.current = null;
|
updateAllocationDragPosition(clientX);
|
||||||
updateAllocationDragPosition(ev.clientX);
|
const alloc = allocDragRef.current;
|
||||||
const alloc = allocDragRef.current;
|
const release = resolveAllocationRelease(alloc, {
|
||||||
const release = resolveAllocationRelease(alloc, {
|
clickThresholdPx: DRAG_CLICK_THRESHOLD_PX,
|
||||||
clickThresholdPx: DRAG_CLICK_THRESHOLD_PX,
|
wasShift,
|
||||||
wasShift,
|
|
||||||
});
|
|
||||||
if (release.kind === "ignore") return;
|
|
||||||
|
|
||||||
if (release.preservePreview) {
|
|
||||||
preserveLivePreview(allocPreviewRef.current);
|
|
||||||
}
|
|
||||||
clearLivePreview(allocPreviewRef.current);
|
|
||||||
allocPreviewRef.current = null;
|
|
||||||
|
|
||||||
if (release.kind === "shift-click") {
|
|
||||||
onShiftClickAllocRef.current?.(release.allocationId);
|
|
||||||
} else if (release.kind === "click") {
|
|
||||||
onBlockClickRef.current?.(release.clickInfo);
|
|
||||||
} else if (release.kind === "mutation") {
|
|
||||||
const { mutationPlan } = release;
|
|
||||||
const {
|
|
||||||
activeAllocationId,
|
|
||||||
currentStartDate,
|
|
||||||
currentEndDate,
|
|
||||||
baseMutationAllocationId,
|
|
||||||
requiresExtraction,
|
|
||||||
pendingSnapshot,
|
|
||||||
} = mutationPlan;
|
|
||||||
|
|
||||||
pendingSnapshotRef.current = pendingSnapshot;
|
|
||||||
pendingOptimisticAllocationIdRef.current = activeAllocationId;
|
|
||||||
setOptimisticAllocations((prev) => {
|
|
||||||
const next = new Map(prev);
|
|
||||||
next.set(activeAllocationId, {
|
|
||||||
startDate: currentStartDate,
|
|
||||||
endDate: currentEndDate,
|
|
||||||
});
|
|
||||||
return next;
|
|
||||||
});
|
});
|
||||||
void (async () => {
|
if (release.kind === "ignore") return;
|
||||||
try {
|
|
||||||
let mutationAllocationId = baseMutationAllocationId;
|
|
||||||
|
|
||||||
if (requiresExtraction) {
|
if (release.preservePreview) {
|
||||||
const extracted = await extractAllocFragmentMutation.mutateAsync({
|
preserveLivePreview(allocPreviewRef.current);
|
||||||
allocationId: mutationAllocationId,
|
}
|
||||||
startDate: alloc.originalStartDate!,
|
clearLivePreview(allocPreviewRef.current);
|
||||||
endDate: alloc.originalEndDate!,
|
allocPreviewRef.current = null;
|
||||||
});
|
|
||||||
mutationAllocationId = extracted.extractedAllocationId;
|
|
||||||
}
|
|
||||||
|
|
||||||
pendingSnapshotRef.current = pendingSnapshotRef.current
|
if (release.kind === "shift-click") {
|
||||||
? {
|
onShiftClickAllocRef.current?.(release.allocationId);
|
||||||
...pendingSnapshotRef.current,
|
} else if (release.kind === "click") {
|
||||||
mutationAllocationId,
|
onBlockClickRef.current?.(release.clickInfo);
|
||||||
}
|
} else if (release.kind === "mutation") {
|
||||||
: null;
|
const { mutationPlan } = release;
|
||||||
|
const {
|
||||||
|
activeAllocationId,
|
||||||
|
currentStartDate,
|
||||||
|
currentEndDate,
|
||||||
|
baseMutationAllocationId,
|
||||||
|
requiresExtraction,
|
||||||
|
pendingSnapshot,
|
||||||
|
} = mutationPlan;
|
||||||
|
|
||||||
updateAllocMutation.mutate({
|
pendingSnapshotRef.current = pendingSnapshot;
|
||||||
allocationId: mutationAllocationId,
|
pendingOptimisticAllocationIdRef.current = activeAllocationId;
|
||||||
|
setOptimisticAllocations((prev) => {
|
||||||
|
const next = new Map(prev);
|
||||||
|
next.set(activeAllocationId, {
|
||||||
startDate: currentStartDate,
|
startDate: currentStartDate,
|
||||||
endDate: currentEndDate,
|
endDate: currentEndDate,
|
||||||
});
|
});
|
||||||
} catch {
|
return next;
|
||||||
clearPendingOptimisticAllocation(activeAllocationId);
|
});
|
||||||
}
|
void (async () => {
|
||||||
})();
|
try {
|
||||||
}
|
let mutationAllocationId = baseMutationAllocationId;
|
||||||
|
|
||||||
allocDragRef.current = INITIAL_ALLOC_DRAG;
|
if (requiresExtraction) {
|
||||||
setAllocDragState(INITIAL_ALLOC_DRAG);
|
const extracted = await extractAllocFragmentMutation.mutateAsync({
|
||||||
}
|
allocationId: mutationAllocationId,
|
||||||
|
startDate: alloc.originalStartDate!,
|
||||||
|
endDate: alloc.originalEndDate!,
|
||||||
|
});
|
||||||
|
mutationAllocationId = extracted.extractedAllocationId;
|
||||||
|
}
|
||||||
|
|
||||||
allocDragCleanupRef.current = attachDocumentMouseDrag(document, handleMove, handleUp);
|
pendingSnapshotRef.current = pendingSnapshotRef.current
|
||||||
|
? {
|
||||||
|
...pendingSnapshotRef.current,
|
||||||
|
mutationAllocationId,
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
|
||||||
|
updateAllocMutation.mutate({
|
||||||
|
allocationId: mutationAllocationId,
|
||||||
|
startDate: currentStartDate,
|
||||||
|
endDate: currentEndDate,
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
clearPendingOptimisticAllocation(activeAllocationId);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
allocDragRef.current = INITIAL_ALLOC_DRAG;
|
||||||
|
setAllocDragState(INITIAL_ALLOC_DRAG);
|
||||||
|
},
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
clearPendingOptimisticAllocation,
|
clearPendingOptimisticAllocation,
|
||||||
|
|||||||
@@ -340,6 +340,17 @@ export const rules = [
|
|||||||
],
|
],
|
||||||
forbidden: [],
|
forbidden: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
file: "apps/web/src/hooks/timelineAllocationDragSession.ts",
|
||||||
|
maxLines: 70,
|
||||||
|
required: [
|
||||||
|
{
|
||||||
|
pattern: /\bexport function beginAllocationDragSession\b/,
|
||||||
|
message: "timeline allocation drag session helpers must keep document drag lifecycle centralized",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
forbidden: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
file: "apps/web/src/hooks/timelineProjectDrag.ts",
|
file: "apps/web/src/hooks/timelineProjectDrag.ts",
|
||||||
maxLines: 80,
|
maxLines: 80,
|
||||||
@@ -429,6 +440,10 @@ export const rules = [
|
|||||||
pattern: /from "\.\/timelineAllocationDragState\.js"/,
|
pattern: /from "\.\/timelineAllocationDragState\.js"/,
|
||||||
message: "timeline drag must keep allocation drag bootstrap delegated to the extracted helper module",
|
message: "timeline drag must keep allocation drag bootstrap delegated to the extracted helper module",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
pattern: /from "\.\/timelineAllocationDragSession\.js"/,
|
||||||
|
message: "timeline drag must keep allocation drag document session wiring delegated to the extracted helper module",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
pattern: /from "\.\/timelineProjectDrag\.js"/,
|
pattern: /from "\.\/timelineProjectDrag\.js"/,
|
||||||
message: "timeline drag must keep project drag bootstrap and mutation gating delegated to the extracted helper module",
|
message: "timeline drag must keep project drag bootstrap and mutation gating delegated to the extracted helper module",
|
||||||
@@ -483,6 +498,10 @@ export const rules = [
|
|||||||
pattern: /\bfunction createAllocationDragState\b/,
|
pattern: /\bfunction createAllocationDragState\b/,
|
||||||
message: "timeline drag must not re-inline extracted allocation drag bootstrap helpers",
|
message: "timeline drag must not re-inline extracted allocation drag bootstrap helpers",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
pattern: /\bfunction handle(?:Move|Up)\b/,
|
||||||
|
message: "timeline drag must not re-inline extracted allocation drag session handlers",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
pattern: /\bfunction (?:createProjectDragState|buildProjectShiftMutationInput)\b/,
|
pattern: /\bfunction (?:createProjectDragState|buildProjectShiftMutationInput)\b/,
|
||||||
message: "timeline drag must not re-inline extracted project drag helper implementations",
|
message: "timeline drag must not re-inline extracted project drag helper implementations",
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ describe("architecture guardrails", () => {
|
|||||||
const positionRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineDragPosition.ts");
|
const positionRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineDragPosition.ts");
|
||||||
const documentDragRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineDocumentDrag.ts");
|
const documentDragRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineDocumentDrag.ts");
|
||||||
const allocationDragStateRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineAllocationDragState.ts");
|
const allocationDragStateRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineAllocationDragState.ts");
|
||||||
|
const allocationDragSessionRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineAllocationDragSession.ts");
|
||||||
const projectDragRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineProjectDrag.ts");
|
const projectDragRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineProjectDrag.ts");
|
||||||
const projectDragSessionRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineProjectDragSession.ts");
|
const projectDragSessionRule = rules.find((rule) => rule.file === "apps/web/src/hooks/timelineProjectDragSession.ts");
|
||||||
|
|
||||||
@@ -108,6 +109,7 @@ describe("architecture guardrails", () => {
|
|||||||
assert.ok(positionRule);
|
assert.ok(positionRule);
|
||||||
assert.ok(documentDragRule);
|
assert.ok(documentDragRule);
|
||||||
assert.ok(allocationDragStateRule);
|
assert.ok(allocationDragStateRule);
|
||||||
|
assert.ok(allocationDragSessionRule);
|
||||||
assert.ok(projectDragRule);
|
assert.ok(projectDragRule);
|
||||||
assert.ok(projectDragSessionRule);
|
assert.ok(projectDragSessionRule);
|
||||||
|
|
||||||
@@ -127,6 +129,7 @@ describe("architecture guardrails", () => {
|
|||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation multi-drag rules delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation multi-drag rules delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation multi-drag document session wiring delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation multi-drag document session wiring delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation drag bootstrap delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation drag bootstrap delegated to the extracted helper module",
|
||||||
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation drag document session wiring delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep project drag bootstrap and mutation gating delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep project drag bootstrap and mutation gating delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep project drag document session wiring delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep project drag document session wiring delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: forbidden pattern matched: timeline drag must not re-inline live preview helper implementations",
|
"apps/web/src/hooks/useTimelineDrag.ts: forbidden pattern matched: timeline drag must not re-inline live preview helper implementations",
|
||||||
@@ -204,6 +207,10 @@ describe("architecture guardrails", () => {
|
|||||||
"apps/web/src/hooks/timelineAllocationDragState.ts: missing guardrail anchor: timeline allocation drag state helpers must keep drag bootstrap centralized",
|
"apps/web/src/hooks/timelineAllocationDragState.ts: missing guardrail anchor: timeline allocation drag state helpers must keep drag bootstrap centralized",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
assert.deepEqual(evaluateRule(allocationDragSessionRule, ""), [
|
||||||
|
"apps/web/src/hooks/timelineAllocationDragSession.ts: missing guardrail anchor: timeline allocation drag session helpers must keep document drag lifecycle centralized",
|
||||||
|
]);
|
||||||
|
|
||||||
assert.deepEqual(evaluateRule(projectDragRule, "export function createProjectDragState() {}\n"), [
|
assert.deepEqual(evaluateRule(projectDragRule, "export function createProjectDragState() {}\n"), [
|
||||||
"apps/web/src/hooks/timelineProjectDrag.ts: missing guardrail anchor: timeline project drag helpers must keep no-op project-shift mutation gating centralized",
|
"apps/web/src/hooks/timelineProjectDrag.ts: missing guardrail anchor: timeline project drag helpers must keep no-op project-shift mutation gating centralized",
|
||||||
]);
|
]);
|
||||||
@@ -232,6 +239,7 @@ describe("architecture guardrails", () => {
|
|||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation multi-drag rules delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation multi-drag rules delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation multi-drag document session wiring delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation multi-drag document session wiring delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation drag bootstrap delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation drag bootstrap delegated to the extracted helper module",
|
||||||
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep allocation drag document session wiring delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep project drag bootstrap and mutation gating delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep project drag bootstrap and mutation gating delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep project drag document session wiring delegated to the extracted helper module",
|
"apps/web/src/hooks/useTimelineDrag.ts: missing guardrail anchor: timeline drag must keep project drag document session wiring delegated to the extracted helper module",
|
||||||
"apps/web/src/hooks/useTimelineDrag.ts: forbidden pattern matched: timeline drag must not re-inline synthetic touch pointer adapters",
|
"apps/web/src/hooks/useTimelineDrag.ts: forbidden pattern matched: timeline drag must not re-inline synthetic touch pointer adapters",
|
||||||
|
|||||||
Reference in New Issue
Block a user